digitalmars.D - Voldemort structs no longer work?
- H. S. Teoh (19/19) Dec 15 2012 With latest git dmd:
- Iain Buclaw (5/25) Dec 15 2012 Pretty certain it's deliberate. No closure is created for nested
- Jonathan M Davis (4/39) Dec 15 2012 static nested structs don't have access to their outer scopes. Non-stati...
- H. S. Teoh (18/51) Dec 15 2012 [...]
- H. S. Teoh (12/28) Dec 15 2012 [...]
- Rob T (4/4) Dec 15 2012 Good finds.
- Iain Buclaw (5/53) Dec 15 2012 If it is one, it's a bug in FuncDeclaration::getLevel.
- Iain Buclaw (6/63) Dec 15 2012 And if it isn't FuncDeclaration::getLevel(), then it is a bug because th...
- Jonathan M Davis (7/10) Dec 15 2012 No, the real irony is that it's Andrei who promoted them in the first pl...
- H. S. Teoh (32/43) Dec 15 2012 [...]
- js.mdnq (25/83) Dec 16 2012 auto func(int x) {
- js.mdnq (13/13) Dec 16 2012 As an alternative possible solution,
- kenji hara (6/86) Dec 16 2012 I have recently added a note about "init property is sometimes unsafe".
- Jonathan M Davis (8/11) Dec 15 2012 That would incur unnecessary overhead and probably break all kinds of co...
- deadalnix (5/23) Dec 15 2012 I always found them inconsistent with the behavior they have in
- H. S. Teoh (29/41) Dec 15 2012 [...]
- Timon Gehr (3/6) Dec 15 2012 Lazy initialization of a member of such a type would require unsafe
- Jonathan M Davis (21/29) Dec 15 2012 That's pretty much how it is now. The problem is all of the stuff that r...
- Walter Bright (3/16) Dec 15 2012 This compiles if the @property is elided.
With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v = makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples. T -- You have to expect the unexpected. -- RL
Dec 15 2012
On Saturday, 15 December 2012 at 18:38:29 UTC, H. S. Teoh wrote:With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v = makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples. TPretty certain it's deliberate. No closure is created for nested structs to access it's parent, complying with it's POD behaviour. Regards, Iain.
Dec 15 2012
On Saturday, December 15, 2012 19:50:34 Iain Buclaw wrote:On Saturday, 15 December 2012 at 18:38:29 UTC, H. S. Teoh wrote:static nested structs don't have access to their outer scopes. Non-static structs do. This reeks of a bug. - Jonathan M DavisWith latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v = makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples. TPretty certain it's deliberate. No closure is created for nested structs to access it's parent, complying with it's POD behaviour.
Dec 15 2012
On Sat, Dec 15, 2012 at 11:31:22AM -0800, Jonathan M Davis wrote:On Saturday, December 15, 2012 19:50:34 Iain Buclaw wrote:[...]On Saturday, 15 December 2012 at 18:38:29 UTC, H. S. Teoh wrote:With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v = makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples.[...] Found the reference in TDPL, §7.1.9 (p.263): Nested structs embed the magic "frame pointer" that allows them to access outer values such as a and b in the example above. [...] If you want to define a nested struct without that baggage, just prefix struct with static in the definition of Local, which makes Local a regular struct and consequently prevents it from accessing a and b. Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :) Anyway, filed a bug: http://d.puremagic.com/issues/show_bug.cgi?id=9162 T -- Nobody is perfect. I am Nobody. -- pepoluan, GKC forumPretty certain it's deliberate. No closure is created for nested structs to access it's parent, complying with it's POD behaviour.static nested structs don't have access to their outer scopes. Non-static structs do. This reeks of a bug.
Dec 15 2012
On Sat, Dec 15, 2012 at 11:45:10AM -0800, H. S. Teoh wrote: [...]Found the reference in TDPL, §7.1.9 (p.263): Nested structs embed the magic "frame pointer" that allows them to access outer values such as a and b in the example above. [...] If you want to define a nested struct without that baggage, just prefix struct with static in the definition of Local, which makes Local a regular struct and consequently prevents it from accessing a and b. Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :) Anyway, filed a bug: http://d.puremagic.com/issues/show_bug.cgi?id=9162[...] Also, according to http://dlang.org/struct.html: A nested struct is a struct that is declared inside the scope of a function or a templated struct that has aliases to local functions as a template argument. Nested structs have member functions. It has access to the context of its enclosing scope (via an added hidden field). T -- If you compete with slaves, you become a slave. -- Norbert Wiener
Dec 15 2012
Good finds. The definition of a "nested struct" is not consistently or well defined, so there's no wonder it's not working as anyone expects. --rt
Dec 15 2012
On 15 December 2012 19:45, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:On Sat, Dec 15, 2012 at 11:31:22AM -0800, Jonathan M Davis wrote:If it is one, it's a bug in FuncDeclaration::getLevel. --=20 Iain Buclaw *(p < e ? p++ : p) =3D (c & 0x0f) + '0';On Saturday, December 15, 2012 19:50:34 Iain Buclaw wrote:[...]On Saturday, 15 December 2012 at 18:38:29 UTC, H. S. Teoh wrote:With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v =3D makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples.[...] Found the reference in TDPL, =A77.1.9 (p.263): Nested structs embed the magic "frame pointer" that allows them to access outer values such as a and b in the example above. [...] If you want to define a nested struct without that baggage, just prefix struct with static in the definition of Local, which makes Local a regular struct and consequently prevents it from accessing a and b. Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :) Anyway, filed a bug: http://d.puremagic.com/issues/show_bug.cgi?id=3D9162Pretty certain it's deliberate. No closure is created for nested structs to access it's parent, complying with it's POD behaviour.static nested structs don't have access to their outer scopes. Non-static structs do. This reeks of a bug.
Dec 15 2012
On 15 December 2012 19:58, Iain Buclaw <ibuclaw ubuntu.com> wrote:On 15 December 2012 19:45, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:And if it isn't FuncDeclaration::getLevel(), then it is a bug because the struct Voldemort does not represent itself as a nested type. --=20 Iain Buclaw *(p < e ? p++ : p) =3D (c & 0x0f) + '0';On Sat, Dec 15, 2012 at 11:31:22AM -0800, Jonathan M Davis wrote:If it is one, it's a bug in FuncDeclaration::getLevel.On Saturday, December 15, 2012 19:50:34 Iain Buclaw wrote:[...]On Saturday, 15 December 2012 at 18:38:29 UTC, H. S. Teoh wrote:With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v =3D makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort Changing 'struct' to 'class' works. Is this deliberate, or is it a bug? It is certainly inconsistent with Walter's article on Voldemort types, which uses structs as examples.[...] Found the reference in TDPL, =A77.1.9 (p.263): Nested structs embed the magic "frame pointer" that allows them to access outer values such as a and b in the example above. [...] If you want to define a nested struct without that baggage, just prefix struct with static in the definition of Local, which makes Local a regular struct and consequently prevents it from accessing a and b. Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :) Anyway, filed a bug: http://d.puremagic.com/issues/show_bug.cgi?id=3D9162Pretty certain it's deliberate. No closure is created for nested structs to access it's parent, complying with it's POD behaviour.static nested structs don't have access to their outer scopes. Non-static structs do. This reeks of a bug.
Dec 15 2012
On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :)No, the real irony is that it's Andrei who promoted them in the first place. :) We _are_ finding some serious issues with them though (e.g. they don't work with init and apparently can't work with init), and there has been some discussion of ditching them due to such issues, but no consensus has been reached on that. - Jonathan M Davis
Dec 15 2012
On Sat, Dec 15, 2012 at 12:02:16PM -0800, Jonathan M Davis wrote:On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:Heh. :)Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :)No, the real irony is that it's Andrei who promoted them in the first place. :)We _are_ finding some serious issues with them though (e.g. they don't work with init and apparently can't work with init), and there has been some discussion of ditching them due to such issues, but no consensus has been reached on that.[...] Hmm, that's true. They can't possibly work with init: if you do something like: auto func(int x) { struct V { property int value() { return x; } } return V(); } auto v = func(123); auto u = v.init; // <-- what happens here? writeln(u.value); // <-- and here? It seems that we'd have to make .init illegal on these Voldemort structs. But that breaks consistency with the rest of the language that every type must have an .init value. Alternatively, .init can implicitly create a context where the value of x is set to int.init. But this is unnecessary (it's supposed to be a *Voldemort* type!) and very ugly (why are we creating a function's local context when it isn't even being called?), not to mention useless (u.value will return a nonsensical value). Or, perhaps a less intrusive workaround, is to have .init set the hidden context pointer to null, and you'll get a null deference when accessing u.value. Which is not pretty either, since no pointers or references are apparent in V.value; it's implicit. It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem. T -- If creativity is stifled by rigid discipline, then it is not true creativity.
Dec 15 2012
On Saturday, 15 December 2012 at 20:20:10 UTC, H. S. Teoh wrote:On Sat, Dec 15, 2012 at 12:02:16PM -0800, Jonathan M Davis wrote:auto func(int x) { struct V { int value() { return x; } property V init() { return this; } } return V(); } auto v = func(123); auto u = v.init; // <-- what happens here? auto x = u.value; writeln(x); If you add an init property then it is not null. I'm not sure if this is the correct behavior as I just jumped into this discussion and I'm not sure if I understand exactly what the problem with these types of structs are. (as far as I can tell they simply let you pass "sets" of data from a function rather easily while also keeping the details hidden(impossible to directly instantiate the struct since it is hidden inside the func) In any case, when stepping through the code above, I noticed that init being called but resulting in u.value throwing a null exception. so I added an init property to V and then got the expected behavior. Probably too easy to be the correct solution but who knows ;)On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:Heh. :)Ironically enough, Andrei in the subsequent paragraph discourages the use of such nested structs, whereas Walter's article promotes the use of such Voldemort types as a "happy discovery". :)No, the real irony is that it's Andrei who promoted them in the first place. :)We _are_ finding some serious issues with them though (e.g. they don't work with init and apparently can't work with init), and there has been some discussion of ditching them due to such issues, but no consensus has been reached on that.[...] Hmm, that's true. They can't possibly work with init: if you do something like: auto func(int x) { struct V { property int value() { return x; } } return V(); } auto v = func(123); auto u = v.init; // <-- what happens here? writeln(u.value); // <-- and here? It seems that we'd have to make .init illegal on these Voldemort structs. But that breaks consistency with the rest of the language that every type must have an .init value. Alternatively, .init can implicitly create a context where the value of x is set to int.init. But this is unnecessary (it's supposed to be a *Voldemort* type!) and very ugly (why are we creating a function's local context when it isn't even being called?), not to mention useless (u.value will return a nonsensical value). Or, perhaps a less intrusive workaround, is to have .init set the hidden context pointer to null, and you'll get a null deference when accessing u.value. Which is not pretty either, since no pointers or references are apparent in V.value; it's implicit. It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem. T
Dec 16 2012
As an alternative possible solution, The following allows efficient nested structs in classes: http://dpaste.dzfl.pl/64025e0a It is very similar to the idea of voldemort structs except that instead of outer being a function it is a class. Therefore, it makes sense that one could simply use functors in replace of the functions to get the same behavior(I'm not sure if D supports functors yet as I haven't got that far but I imagine it does). To be useful, of course, one would have to use static functors so instantiation would not be necessary and direct replacement could be observed only if functor notation is identical to function notation.
Dec 16 2012
I have recently added a note about "init property is sometimes unsafe". https://github.com/D-Programming-Language/d-programming-language.org/pull/201 To reduce the risk, I have proposed an enhancement for .init property usage. http://d.puremagic.com/issues/show_bug.cgi?id=8752 Kenji Hara 2012/12/16 js.mdnq <js_adddot+mdng gmail.com>On Saturday, 15 December 2012 at 20:20:10 UTC, H. S. Teoh wrote:On Sat, Dec 15, 2012 at 12:02:16PM -0800, Jonathan M Davis wrote:auto func(int x) { struct V { int value() { return x; } property V init() { return this; } } return V(); } auto v = func(123); auto u = v.init; // <-- what happens here? auto x = u.value; writeln(x); If you add an init property then it is not null. I'm not sure if this is the correct behavior as I just jumped into this discussion and I'm not sure if I understand exactly what the problem with these types of structs are. (as far as I can tell they simply let you pass "sets" of data from a function rather easily while also keeping the details hidden(impossible to directly instantiate the struct since it is hidden inside the func) In any case, when stepping through the code above, I noticed that init being called but resulting in u.value throwing a null exception. so I added an init property to V and then got the expected behavior. Probably too easy to be the correct solution but who knows ;)On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:Heh. :) We _are_ finding some serious issues with them though (e.g. they don'tIronically enough, Andrei in the subsequent paragraph > discourages the use of such nested structs, whereas Walter's article > promotes the use of such Voldemort types as a "happy discovery". :)No, the real irony is that it's Andrei who promoted them in the first place. :)work with init and apparently can't work with init), and there has been some discussion of ditching them due to such issues, but no consensus has been reached on that.[...] Hmm, that's true. They can't possibly work with init: if you do something like: auto func(int x) { struct V { property int value() { return x; } } return V(); } auto v = func(123); auto u = v.init; // <-- what happens here? writeln(u.value); // <-- and here? It seems that we'd have to make .init illegal on these Voldemort structs. But that breaks consistency with the rest of the language that every type must have an .init value. Alternatively, .init can implicitly create a context where the value of x is set to int.init. But this is unnecessary (it's supposed to be a *Voldemort* type!) and very ugly (why are we creating a function's local context when it isn't even being called?), not to mention useless (u.value will return a nonsensical value). Or, perhaps a less intrusive workaround, is to have .init set the hidden context pointer to null, and you'll get a null deference when accessing u.value. Which is not pretty either, since no pointers or references are apparent in V.value; it's implicit. It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem. T
Dec 16 2012
On Saturday, December 15, 2012 12:18:21 H. S. Teoh wrote:It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem.That would incur unnecessary overhead and probably break all kinds of code, because they're then reference types instead of value types, and a _lot_ of code doesn't use save propperly. If structs can't do what we need as Voldemort types, it's just better to make it so that they're not Voldemort types. Voldemort types are a cute idea, and in principle are great, but I don't think that it's worth fighting to keep them if they have problems. - Jonathan M Davis
Dec 15 2012
On Saturday, 15 December 2012 at 21:10:19 UTC, Jonathan M Davis wrote:On Saturday, December 15, 2012 12:18:21 H. S. Teoh wrote:I always found them inconsistent with the behavior they have in classes (where no outer pointer is created). This is a lot of work to do in the standard lib however.It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem.That would incur unnecessary overhead and probably break all kinds of code, because they're then reference types instead of value types, and a _lot_ of code doesn't use save propperly. If structs can't do what we need as Voldemort types, it's just better to make it so that they're not Voldemort types. Voldemort types are a cute idea, and in principle are great, but I don't think that it's worth fighting to keep them if they have problems. - Jonathan M Davis
Dec 15 2012
On Sat, Dec 15, 2012 at 01:09:33PM -0800, Jonathan M Davis wrote:On Saturday, December 15, 2012 12:18:21 H. S. Teoh wrote:[...] Well, the current way many Phobos ranges work, they're kind of pseudo-Voldemort types (except they don't actually need/use the context pointer): auto cycle(R)(R range) { struct CycleImpl { R r; this(R _r) { r = _r; } ... // range methods } return CycleImpl(range); } auto r = cycle(myRange); While it's true that you can write: typeof(cycle(myRange)) s; and thereby break encapsulation, if someone is desperate enough to do such things they probably have a good reason for it, and they should be able to deal with the consequences too. But anyway, thinking a bit more about the .init problem, couldn't we just say that .init is not accessible outside the scope of the function that defines the type, and therefore you cannot declare a variable of that type (using typeof or whatever other workaround) without also assigning it to an already-initialized instance of the type? This way, the type still has an .init, except that it's only accessible inside the function itself. Or are there unintended consequences here? T -- It is impossible to make anything foolproof because fools are so ingenious. -- SammyIt seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem.That would incur unnecessary overhead and probably break all kinds of code, because they're then reference types instead of value types, and a _lot_ of code doesn't use save propperly. If structs can't do what we need as Voldemort types, it's just better to make it so that they're not Voldemort types. Voldemort types are a cute idea, and in principle are great, but I don't think that it's worth fighting to keep them if they have problems.
Dec 15 2012
On 12/15/2012 10:44 PM, H. S. Teoh wrote:... This way, the type still has an .init, except that it's only accessible inside the function itself. Or are there unintended consequences here?Lazy initialization of a member of such a type would require unsafe language features and not work in CTFE.
Dec 15 2012
On Saturday, December 15, 2012 13:44:13 H. S. Teoh wrote:But anyway, thinking a bit more about the .init problem, couldn't we just say that .init is not accessible outside the scope of the function that defines the type, and therefore you cannot declare a variable of that type (using typeof or whatever other workaround) without also assigning it to an already-initialized instance of the type? This way, the type still has an .init, except that it's only accessible inside the function itself. Or are there unintended consequences here?That's pretty much how it is now. The problem is all of the stuff that requires .init. For instance, lots of template constraints and static if use .init to check stuff about a type. Not having an .init gets in the way of a lot of template metaprogramming. And sometimes, you have to have .init or some things are impossible. For instance, takeNone will attempt to return an empty range of the given type (something which some algorithms need, or they won't work), and if the range doesn't have init or slicing, then it's forced to return the result of takeExactly, which is then a different range type. For some stuff, that's fine, for other stuff, that renders it unusable. I think that the only place that that affects Phobos at the moment is that you lose an optimization path in one of find's overloads, but I've been in other situations where I've had to make a range empty without changing its type and popping all of the elements off was unacceptable, and Voldemort types make that impossible. We may be able to work around enough of the problems caused by a lack of a usable init property to be able to continue to use Voldemort types, but some issues can't be fixed with them (like that of takeNone), and until we do find a solution for even the problems that we can fix, the lack of an init property tends to be crippling for metaprogramming. - Jonathan M Davis
Dec 15 2012
On 12/15/2012 10:36 AM, H. S. Teoh wrote:With latest git dmd: auto makeVoldemort(int x) { struct Voldemort { property int value() { return x; } } return Voldemort(); } void main() { auto v = makeVoldemort(); writeln(v.value); } Compile error: test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemortThis compiles if the property is elided. Definitely a bug.
Dec 15 2012