www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Voldemort structs no longer work?

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
next sibling parent reply "Iain Buclaw" <ibuclaw ubuntu.com> writes:
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.


 T
Pretty 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
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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.
 
 
 T
Pretty 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. - Jonathan M Davis
Dec 15 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
[...]
 Pretty 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.
[...] 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 forum
Dec 15 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
parent "Rob T" <rob ucora.com> writes:
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
prev sibling next sibling parent Iain Buclaw <ibuclaw ubuntu.com> writes:
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:
 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.
[...]
 Pretty 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.
[...] 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=3D9162
If it is one, it's a bug in FuncDeclaration::getLevel. --=20 Iain Buclaw *(p < e ? p++ : p) =3D (c & 0x0f) + '0';
Dec 15 2012
prev sibling parent Iain Buclaw <ibuclaw ubuntu.com> writes:
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:

 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 =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.
[...]
 Pretty 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.
[...] 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=3D9162
If it is one, it's a bug in FuncDeclaration::getLevel.
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';
Dec 15 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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. :)
Heh. :)
 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
parent reply "js.mdnq" <js_adddot+mdng gmail.com> writes:
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:
 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. :)
Heh. :)
 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
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 ;)
Dec 16 2012
next sibling parent "js.mdnq" <js_adddot+mdng gmail.com> writes:
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
prev sibling parent kenji hara <k.hara.pg gmail.com> writes:
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:

 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. :)
Heh. :) 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
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 ;)
Dec 16 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
parent "deadalnix" <deadalnix gmail.com> writes:
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:
 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
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.
Dec 15 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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.
[...] 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. -- Sammy
Dec 15 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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.makeVoldemort
This compiles if the property is elided. Definitely a bug.
Dec 15 2012