digitalmars.D - Casting const/invariant
- Jason House (12/12) Jan 02 2008 I don't know about others, but it looks to me like the community has com...
- Daniel919 (20/36) Jan 02 2008 When creating a new invariant struct/class, the compiler can always
- Jason House (8/14) Jan 02 2008 Maybe using that in my post was a mistake. I certainly don't intend to
- Janice Caron (8/11) Jan 02 2008 I don't believe that is true. Consider
- Daniel919 (26/40) Jan 03 2008 import std.stdio;
I don't know about others, but it looks to me like the community has come close to accepting D's const design. I know it's probably the last thing anyone wants to hear, but below are my thoughts on extending the design. Right now, the docs say "[When casting data to invariant] it is up to the programmer to ensure that no other mutable references to the same data exist. " Const is useful for interface guarantees, but does not allow the compiler to optimize code like it can with invariant data. Unlike const, the compiler will be unwilling to (silently) cast data to invariant. For invariant data to be useful, I think there needs to be a way for casting to be safe. I think the key is adding a concept of exclusive write access to data. For the sake of this post, I'll call data with exclusive write access "exclusive" and those that don't have it "shared". Just to confuse everyone, I'll use "readonly" instead of const in my discussion... Note that with this notation, invariant = exclusive readonly const = shared readonly shared data gives behavior exactly like everyone currently expects. You never know who has a mutable reference to the data, and the compiler gives no guarantees to that fact. exclusive data can be implicitly cast to scope invariant for function calls. This would allow a function with const parameter signature to use a version optimized for invariant access. I would suggest that member/global variables default to shared access and local variables (in functions) default to exclusive access. Using the variables as "in" parameters or "scope ref" parameters would require no modification. Using them inside delegates or passing to non-scope "ref" parameters would require the data be shared. Function return types and out parameters to functions are the stickiest part. By default, they'd have to be shared access, but for this scheme to really be worthwhile, some framework for specifying when these outputs can be used as exclusive access (AKA the function leaves no mutable references in any other locations) Thoughts?
Jan 02 2008
Right now, the docs say "[When casting data to invariant] it is up to the programmer to ensure that no other mutable references to the same data exist. "When creating a new invariant struct/class, the compiler can always infer a cast(invariant). invariant clFoo foo = /*cast(invariant)*/ new clFoo();Const is useful for interface guarantees, but does not allow the compiler to optimize code like it can with invariant data. Unlike const, the compiler will be unwilling to (silently) cast data to invariant. For invariant data to be useful, I think there needs to be a way for casting to be safe.Correct, const is only logical, not physical, as it is now and therefore has to be treated like mutable.I think the key is adding a concept of exclusive write access to data. For the sake of this post, I'll call data with exclusive write access "exclusive" and those that don't have it "shared". Just to confuse everyone, I'll use "readonly" instead of const in my discussion...I recommend the use of readonly instead of const. See my post "const=readonly invariant=const".Note that with this notation, invariant = exclusive readonly const = shared readonly shared data gives behavior exactly like everyone currently expects. You never know who has a mutable reference to the data, and the compiler gives no guarantees to that fact. exclusive data can be implicitly cast to scope invariant for function calls. This would allow a function with const parameter signature to use a version optimized for invariant access. I would suggest that member/global variables default to shared access and local variables (in functions) default to exclusive access. Using the variables as "in" parameters or "scope ref" parameters would require no modification. Using them inside delegates or passing to non-scope "ref" parameters would require the data be shared. Function return types and out parameters to functions are the stickiest part. By default, they'd have to be shared access, but for this scheme to really be worthwhile, some framework for specifying when these outputs can be used as exclusive access (AKA the function leaves no mutable references in any other locations) Thoughts?In my ideas, const is the keyword that is mostly used. Therefore the compiler would not have to silently try to convert "const int x = 1;" to what it actually means: "invariant int x = 1;" readonly would mean what you call "shared readonly" and const what you call "exclusive readonly" readonly data in general has to be treated like mutable data For optimization the compiler might generate two versions (of a function): one for the case that the data is mutable another for the case that the data is const void foo(readonly Foo foo) { return foo.x; } generates two versions, that are overloadable void foo(/*mutable*/ Foo foo) { return foo.x; } void foo(const Foo foo) { return foo.x; } //can be optimized
Jan 02 2008
Daniel919 wrote:I recommend the use of readonly instead of const. See my post "const=readonly invariant=const".Maybe using that in my post was a mistake. I certainly don't intend to start a syntax war. Since I was mucking with stuff, I figured using readonly would make stuff simpler. Turns out that I hardly used the term and probably could have removed it from the post.readonly data in general has to be treated like mutable data For optimization the compiler might generate two versions (of a function): one for the case that the data is mutable another for the case that the data is constOne step at a time ;) I'd love to see an endstate with const where the compiler knows how to handle/optimize const stuff with as little input from the programmer as possible.
Jan 02 2008
On 1/3/08, Daniel919 <Daniel919 web.de> wrote:When creating a new invariant struct/class, the compiler can always infer a cast(invariant). invariant clFoo foo = /*cast(invariant)*/ new clFoo();I don't believe that is true. Consider class C { int *p; this() { p = &some_global_variable; } } I don't see anything to stop *C.p from being modified by another means.
Jan 02 2008
Janice Caron wrote:On 1/3/08, Daniel919 <Daniel919 web.de> wrote:import std.stdio; int some_global_variable = 1; /*invariant*/ class C { int *p; this() { p = &some_global_variable; } } void main() { invariant C c = /*cast(invariant)*/ new C(); //Error: cannot implicitly convert expression (& some_global_variable) of type int* to invariant(int*) invariant C c = cast(invariant) new C(); //OK, you promise not to change some_global_variable anymore writefln( *c.p ); some_global_variable = 2; //undefined behavior, you lied writefln( *c.p ); } The inferred cast(invariant) would mean the same as declaring the struct/class "invariant" for this new object. So it would ensure that everything is deep invariant. An explicit cast(invariant) on an existing reference is different. There might be pointers/refs that point to mutable data. But we ensure that this data will not change anymore.When creating a new invariant struct/class, the compiler can always infer a cast(invariant). invariant clFoo foo = /*cast(invariant)*/ new clFoo();I don't believe that is true. Consider class C { int *p; this() { p = &some_global_variable; } } I don't see anything to stop *C.p from being modified by another means.
Jan 03 2008