www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP Proposal: manualScoped to prevent automatic field destruction

reply FeepingCreature <feepingcreature gmail.com> writes:
A new UDA is introduced:  manualScoped. It is valid for fields in 
structs and classes, as well as variables and parameters. Fields 
marked with  manualScoped are not automatically destructed on 
scope end.

For instance, a function taking a struct as a  manualScoped value 
will lead to a copy constructor call, but no destructor call. It 
is assumed the passed value will be moved into another field via 
move() or moveEmplace().

In  safe, only  manualScoped fields may be initialized with 
.init. This is to indicate that init represents a hole in the 
typesystem, and using it forces you to engage in manual lifecycle 
management.

The goal of this DIP is to make the union hack unnecessary and 
resolve the value/variable problem with .init initialized struct 
destruction, where { S s = S.init; } led to a destructor call but 
no corresponding constructor call.

Opinions?
Jul 27 2018
next sibling parent reply aliak <something something.com> writes:
On Friday, 27 July 2018 at 09:30:00 UTC, FeepingCreature wrote:
 A new UDA is introduced:  manualScoped. It is valid for fields 
 in structs and classes, as well as variables and parameters. 
 Fields marked with  manualScoped are not automatically 
 destructed on scope end.

 For instance, a function taking a struct as a  manualScoped 
 value will lead to a copy constructor call, but no destructor 
 call. It is assumed the passed value will be moved into another 
 field via move() or moveEmplace().

 In  safe, only  manualScoped fields may be initialized with 
 .init. This is to indicate that init represents a hole in the 
 typesystem, and using it forces you to engage in manual 
 lifecycle management.

 The goal of this DIP is to make the union hack unnecessary and 
 resolve the value/variable problem with .init initialized 
 struct destruction, where { S s = S.init; } led to a destructor 
 call but no corresponding constructor call.

 Opinions?
A) I'd suggest " nodestruct" instead, since it sounds like that what it's supposed to do? B) is this basically for the case of invariants being run before destructors where T.init are not valid runtime instances of T? C) If it is, then this seems to me that this is something that should just work without a programmer needing to know about how T.init and invariants are implemented, so an implementation that doesn't call invariants before a destructor only if an instance was never constructed at runtime is maybe the way to go? Though I have no idea how possible that is. Cheers, - Ali maybe your PR where invariants is not called before a destructor if an instance is a T.init is maybe the way to go? [0]
Jul 27 2018
parent FeepingCreature <feepingcreature gmail.com> writes:
On Friday, 27 July 2018 at 11:44:10 UTC, aliak wrote:
 A) I'd suggest " nodestruct" instead, since it sounds like that 
 what it's supposed to do?
Yes-ish, but it's also supposed to fill the hole in the typesystem created by T.init, and "you can only assign T.init to types marked nodestruct" sounds kind of magic.
 B) is this basically for the case of invariants being run 
 before destructors where T.init are not valid runtime instances 
 of T?
Yep, that's the reason I'm looking at it.
 C) If it is, then this seems to me that this is something that 
 should just work without a programmer needing to know about how 
 T.init and invariants are implemented, so an implementation 
 that doesn't call invariants before a destructor only if an 
 instance was never constructed at runtime is maybe the way to 
 go? Though I have no idea how possible that is.
Basically impossible without giving every type a hidden "bool initialized" field. So, basically impossible. The advantage of doing it with manualScoped is twofold. First, it also covers the case of opAssign methods taking parameters that don't need to be destroyed at scope end. Second, even a constructor may return a T.init (and, for instance, increment a static variable), so if being T.init skipped the destructor we'd again get a constructor/destructor mismatch. manualScoped makes it clear this is a variable that contains a value with a manually managed lifetime, so users take responsibility to call moveEmplace/destroy as required to make constructor/destructor calls match up, which is the goal. Basically, think of a manualScoped variable as a "weak value" in analogy to weak references.
 Cheers,
 - Ali

 maybe your PR where invariants is not called before a 
 destructor if an instance is a T.init is maybe the way to go? 
 [0]
The PR itself was just a way to hack around this. The problem isn't the invariant check on destruction, the problem is the destruction without the matching construction.
Jul 27 2018
prev sibling parent FeepingCreature <feepingcreature gmail.com> writes:
Update:

https://github.com/dlang/dmd/pull/5830

Turns out unions with fields with destructors is **define 
behavior**. I did not know this. Probably the spec needs updating.

So that pretty much alleviates the need for this DIP. Though I 
kind of half-believe that this DIP is actually *better* than 
unrestricted unions, so I'd still leave it open.
Jul 27 2018