Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Struct constructor doesn't require initialization of class reference #21

Open
ntrel opened this issue Jan 2, 2023 · 5 comments
Open

Comments

@ntrel
Copy link

ntrel commented Jan 2, 2023

module objembed;

class C
{
    int i;
}

struct S
{
    C c;
    this() {}
}

void main()
{
    //S s; // error
    S s = S();
}

Declaring S s; gives:

      ~ Cannot declare s without initializer: S is not zero-initializable

But declaring S.this without initialization of c is not caught. s.c is then null.

@FeepingCreature
Copy link
Contributor

FeepingCreature commented Jan 2, 2023

Yes, this is ... kind of hard to fix. The problem is that we'd need control flow analysis (or runtime errors) to catch cases where there exist flows through the constructor that don't set a field. At the limit, this is no-shit halting-problem tier undecidable.

I could hack something like "every field in the struct must have at least one assignment somewhere in the constructor", but I fear this would only give the false impression of safety.

@ntrel
Copy link
Author

ntrel commented Jan 2, 2023

Yes, some kind of basic flow analysis would be needed. If implemented like cppfront, the following code would error:

this(){
    if (random!bool()) // line 12
        c = new C; 
}
objembed:12: Error: `if` statement initializes `c` in only one branch

Requiring if to initialize a member in both branches if it is initialized in either seems a pragmatic way to solve the halting problem.

@FeepingCreature
Copy link
Contributor

FeepingCreature commented Jan 2, 2023

Okay, I'm gonna say that yes, I could do that, but I'm not gonna in the short and medium term. It seems a lot of effort for a mid improvement, and a lot of compiler machinery that wouldn't be used anywhere else. (Neat is relatively less reliant on compiler analysis than some other languages.)

Patches, as usual, welcome!

@ntrel
Copy link
Author

ntrel commented Jan 21, 2023

It seems a lot of effort for a mid improvement

For now perhaps an out contract for structs with non-nullable members could be added in constructors that asserts that those members are not null. Later the if branch checking could be implemented and the out contracts removed (though the former can have false positives unlike the latter).

a lot of compiler machinery that wouldn't be used anywhere else

This would also be useful to ensure a struct with both a defined constructor and destructor is actually constructed and not left as T.init. This would guarantee that such structs do not have to handle T.init in their destructor (which has a runtime cost as well as a design cost to detect the init value). Then if there is no initialization of the struct, the compiler can avoid calling the destructor or allowing it to be copied. (I also raised this here).

Patches, as usual, welcome!

Thanks, I haven't looked at the source yet. (I'm a bit put off by the compile time as I'm spoilt by dmd ;-)).

@FeepingCreature
Copy link
Contributor

FeepingCreature commented Jan 21, 2023

Initial compile time is high, but follow-up should be fine as the cache is warm.

./build.sh builds the compiler with optimization. This is usually the right move, but can be turned off with FAST= ./build.sh.

That's a good idea with the out-condition assert! I'll put it on the TODO list.

Note that I'm distracted ATM making a toy cpu in VHDL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants