digitalmars.D - Function scope arguments
- bearophile (8/8) Jan 14 2013 Maybe this thread shows the need for some design work, see the
- Timon Gehr (13/21) Jan 14 2013 'in' is 'const scope'.
- Jacob Carlborg (5/17) Jan 14 2013 For delegates "scope" indicates the delegate is not a real closure and
- Artur Skawina (11/13) Jan 15 2013 A non-transitive scope would not be very useful. Eg. a 'scope' arg could
- deadalnix (3/13) Jan 15 2013 The compiler should assume they may escape unless scope is
- Artur Skawina (8/17) Jan 15 2013 This is about /avoiding/ "hidden" heap allocations as much as possible. ...
- bearophile (4/6) Jan 15 2013 A future "scope lazy" annotation?
- Timon Gehr (2/19) Jan 15 2013 Actually lazy args are implicitly 'scope' and never allocate.
- Artur Skawina (9/30) Jan 15 2013 I wish. :)
- Timon Gehr (25/55) Jan 15 2013 No, there is no way to get a heap allocated closure from a lazy paramete...
- Artur Skawina (16/90) Jan 15 2013 The output is actually "2" here.
- David Nadlinger (4/9) Jan 15 2013 This is a hole in SafeD – please file it as such if it isn't in
- deadalnix (35/43) Jan 15 2013 Well I think about it a lot, it is unclear what is the solution,
- bearophile (10/13) Jan 15 2013 While "scope" is probably not the most important feature to work
Maybe this thread shows the need for some design work, see the comments by Hara: http://d.puremagic.com/issues/show_bug.cgi?id=8695 What's the meaning of "in" function arguments? What are the purposes of "scope" for function arguments that contain indirections, and how to implement that? Bye, bearophile
Jan 14 2013
On 01/14/2013 06:52 PM, bearophile wrote:Maybe this thread shows the need for some design work, see the comments by Hara: http://d.puremagic.com/issues/show_bug.cgi?id=8695 What's the meaning of "in" function arguments? What are the purposes of "scope" for function arguments that contain indirections, and how to implement that? Bye, bearophile'in' is 'const scope'. 'scope' is supposed to restrict escaping references, but it is not entirely clear what that means, and how to make it powerful enough. Eg. what if part of a structure can be freely escaped, such as the contents of an array of Objects, but not the array itself? I'd argue that indirections in parameters should not be covered by 'scope', because it is not something that is transitive. (the opposite would be) Furthermore, there is the issue of how to treat 'ref' parameters. Also, we may want to use 'scope' to annotate struct fields in some way, so that structs can capture scope parameters that are ensured at the call site to live at least as long as the struct instance. An implementiation should use flow (and maybe lifetime) analysis.
Jan 14 2013
On 2013-01-15 00:07, Timon Gehr wrote:'in' is 'const scope'. 'scope' is supposed to restrict escaping references, but it is not entirely clear what that means, and how to make it powerful enough. Eg. what if part of a structure can be freely escaped, such as the contents of an array of Objects, but not the array itself? I'd argue that indirections in parameters should not be covered by 'scope', because it is not something that is transitive. (the opposite would be) Furthermore, there is the issue of how to treat 'ref' parameters. Also, we may want to use 'scope' to annotate struct fields in some way, so that structs can capture scope parameters that are ensured at the call site to live at least as long as the struct instance. An implementiation should use flow (and maybe lifetime) analysis.For delegates "scope" indicates the delegate is not a real closure and won't capture any variables on the heap. -- /Jacob Carlborg
Jan 14 2013
On 01/15/13 00:07, Timon Gehr wrote:'scope' is supposed to restrict escaping references, but it is not entirely clear what that means, and how to make it powerful enough. Eg. what if part of a structure can be freely escaped, such as the contents of an array of Objects, but not the array itself? I'd argue that indirections in parameters should not be covered by 'scope', because it is not something that is transitive. (the opposite would be)A non-transitive scope would not be very useful. Eg. a 'scope' arg could never refer to a stack object - which is the main advantage of having 'scope'. Introducing a second kind of top-level-scope would probably complicate the language too much.Furthermore, there is the issue of how to treat 'ref' parameters.Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime) artur
Jan 15 2013
On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.
Jan 15 2013
On 01/15/13 12:48, deadalnix wrote:On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal. 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages) arturDifferent problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.
Jan 15 2013
Artur Skawina:'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping,A future "scope lazy" annotation? Bye, bearophile
Jan 15 2013
On 01/15/2013 01:44 PM, Artur Skawina wrote:On 01/15/13 12:48, deadalnix wrote:Actually lazy args are implicitly 'scope' and never allocate.On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal. 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages) arturDifferent problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.
Jan 15 2013
On 01/15/13 15:09, Timon Gehr wrote:On 01/15/2013 01:44 PM, Artur Skawina wrote:I wish. :) Seriously though, I don't. They can be escaped and they do allocate. They have to. The problem is just that there currently is no way to tell the compiler i-know-what-i'm-doing and avoid the heap allocated closures. [if the behavior changed in newer (than my old gdc) compiler versions then such a change is bogus, as it would mean that stack objects could be escaped] arturOn 01/15/13 12:48, deadalnix wrote:Actually lazy args are implicitly 'scope' and never allocate.On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal. 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages)Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.
Jan 15 2013
On 01/15/2013 04:03 PM, Artur Skawina wrote:On 01/15/13 15:09, Timon Gehr wrote:Me neither, but that is what happens.On 01/15/2013 01:44 PM, Artur Skawina wrote:I wish. :) Seriously though, I don't.On 01/15/13 12:48, deadalnix wrote:Actually lazy args are implicitly 'scope' and never allocate.On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal. 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages)Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.They can be escaped and they do allocate. They have to. The problem is just that there currently is no way to tell the compiler i-know-what-i'm-doing and avoid the heap allocated closures.No, there is no way to get a heap allocated closure from a lazy parameter.[if the behavior changed in newer (than my old gdc) compiler versions then such a change is bogus, as it would mean that stack objects could be escaped] arturI think the behaviour has always been the same (at least with DMD). import std.stdio; int delegate() foo(lazy int x){ return ()=>x; } int delegate() escape(int x){ return foo(x); } void trash(){ int[2] x=1337; } void main(){ auto dg = escape(2); trash(); writeln(dg()); } $ dmd -run tt.d 1337 $ gdmd -run tt.d -1430461920 If the behaviour was as you suggest, the output would be: 2
Jan 15 2013
On 01/15/13 17:12, Timon Gehr wrote:On 01/15/2013 04:03 PM, Artur Skawina wrote:The output is actually "2" here. But after taking a closer look at the generated code I've now realized that the allocations I am seeing are originating from (the equivalent of) 'foo', not 'main'. Inlining confused me. Note to myself: never assume the D compiler behaves sanely. You are right - lazy args currently do *not* cause allocations. Thanks for the correction. Unfortunately this makes the situations much worse, as the problem isn't just a performance issue, but a potential source of nasty bugs. The fix would be straightforward - make 'lazy' create closures properly and avoid the unnecessary 'foo' allocations. Then the only thing needed would be a way to avoid those allocations, as in my real code I was actually semi-escaping them, at least as far as the compiler could tell. Oh well, I'll get used to the extra pair of braces. :) arturOn 01/15/13 15:09, Timon Gehr wrote:Me neither, but that is what happens.On 01/15/2013 01:44 PM, Artur Skawina wrote:I wish. :) Seriously though, I don't.On 01/15/13 12:48, deadalnix wrote:Actually lazy args are implicitly 'scope' and never allocate.On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal. 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages)Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)The compiler should assume they may escape unless scope is specified.They can be escaped and they do allocate. They have to. The problem is just that there currently is no way to tell the compiler i-know-what-i'm-doing and avoid the heap allocated closures.No, there is no way to get a heap allocated closure from a lazy parameter.[if the behavior changed in newer (than my old gdc) compiler versions then such a change is bogus, as it would mean that stack objects could be escaped] arturI think the behaviour has always been the same (at least with DMD). import std.stdio; int delegate() foo(lazy int x){ return ()=>x; } int delegate() escape(int x){ return foo(x); } void trash(){ int[2] x=1337; } void main(){ auto dg = escape(2); trash(); writeln(dg()); } $ dmd -run tt.d 1337 $ gdmd -run tt.d -1430461920 If the behaviour was as you suggest, the output would be: 2
Jan 15 2013
On Tuesday, 15 January 2013 at 16:12:41 UTC, Timon Gehr wrote:No, there is no way to get a heap allocated closure from a lazy parameter. […] I think the behaviour has always been the same (at least with DMD).This is a hole in SafeD – please file it as such if it isn't in Bugzilla already. David
Jan 15 2013
On Monday, 14 January 2013 at 17:52:32 UTC, bearophile wrote:Maybe this thread shows the need for some design work, see the comments by Hara: http://d.puremagic.com/issues/show_bug.cgi?id=8695 What's the meaning of "in" function arguments? What are the purposes of "scope" for function arguments that contain indirections, and how to implement that? Bye, bearophileWell I think about it a lot, it is unclear what is the solution, but here is how I see it : stack variable as marked as local. value marked local are considered scope in any inner scope. values marked local can be passed to function by value or via scope argument. values marked scope can be passed to function by value or via scope argument. void foo() { int a; // a is local. { // a is scope here } } Everything accessed throw a scope value is scope. scope are not referable throw any non scope. local are not referable to scope. each scope declaration introduce a new scope. different scope can never refers each others. void foo(scope A a, scope B b) { // a and b are in different scopes. a.memeber; // Is in the same scope as a, but not as b. C c; // c is local. D d; // ditto. { // c and d are in the same scope. a and b are in different scope. c.member = d; // OK c.member = a; // Error. } } I still have some trouble to figure out the proper definition of it. And what can't be defined clearly is probably not understood enough to create a feature.
Jan 15 2013
deadalnix:I still have some trouble to figure out the proper definition of it. And what can't be defined clearly is probably not understood enough to create a feature.While "scope" is probably not the most important feature to work now in D (const/immutable issues, and shared issues are possibly more important), it's one of the important unfinished parts of D. It needs a design, and later a partial implementation. Being a core language issue I think "scope" is more important than optimization issues, like this one: https://github.com/D-Programming-Language/dmd/commit/4b139c0a217ccbf3c71a0d993eb6e3556254de60 Bye, bearophile
Jan 15 2013