Bug 17645 – `pure` is transitively applied to all delegates inside a pure function

Status
NEW
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-07-13T12:03:21Z
Last change time
2024-12-13T18:53:20Z
Keywords
industry
Assigned to
No Owner
Creator
Tomer Filiba (weka)
Moved to GitHub: dmd#19281 →

Comments

Comment #0 by tomer — 2017-07-13T12:03:21Z
DMD64 D Compiler v2.074.0. Consider the following: static int x; void f() pure { auto dg = (){x++;}; } src/dtest.d(45,19): Error: pure delegate 'dtest.f.__lambda1' cannot access mutable static data 'x' Had I invoked dg() inside f(), this error would be correct. But I'm only creating a delegate, which I will pass to some external, non-pure entity, which is allowed to call it. The use-case here is that sometimes we need the compiler to "just trust us" and assume something is pure. It's used in exception cases which would soon after reach an abort() so we don't consider this impure. And this restriction is too broad for us
Comment #1 by schveiguy — 2017-07-13T12:24:56Z
Do you have a better use case? This works: void f() pure { void function() dg = {x++;}; } i.e. you don't need access to the pure function's stack frame, so you can have a function instead of a delegate. I'm kind of surprised the compiler inferred dg as a delegate and not a function in the first place.
Comment #2 by dlang-bugzilla — 2017-07-15T04:41:22Z
FWIW, the test case works in DMD 2.013 through 2.027 :)
Comment #3 by tomer — 2017-07-15T20:39:29Z
(In reply to Steven Schveighoffer from comment #1) > Do you have a better use case? the use case may sound odd, but it's surprisingly common. suppose you have struct Foo { private int x; @property int someTrivialProperty() const pure @nogc { assert(x > 8, "oh no x=%s".format(x)); return x; } } `format` does GC and isn't pure. the property itself is perfectly pure and @nogc and whatnot, but by adding an assert i have to remove these attributes from it. so we have ASSERT which hides away the impurity and GC-ness, since it it blows up we really don't care about the GC or purity. ASSERT!"oh no x=%s"(x > 8, x); we practically use it everywhere. > i.e. you don't need access to the pure function's stack frame, so you can > have a function instead of a delegate. maybe, but it means i can't write it in the same statement, i.e. this won't work assumePure({x++}); and i'll have to use void function() fn = {x++}; assumePure(x); which defeats the purpose of easy-to-spell-out lambdas
Comment #4 by schveiguy — 2017-07-17T15:27:07Z
(In reply to Tomer Filiba (weka) from comment #3) > `format` does GC and isn't pure. `format` actually is pure. void main() pure { auto str = format("oh no x=%s", 5); } But you are right that it's not @nogc. However, this isn't the focus of the bug report (or is it?). In your new example, you are actually calling the GC-allocating function from inside a @nogc context, not creating a delegate. Even if you did create a delegate and needed to save the context, that creates a closure, and that definitely needs the GC. > the property itself is perfectly pure and > @nogc and whatnot, but by adding an assert i have to remove these attributes > from it. I understand. This is more like an enhancement request to have assert ignore the requirements of pure and @nogc for the lazy evaluated arg. > maybe, but it means i can't write it in the same statement, i.e. this won't > work > > assumePure({x++}); As I said earlier, I think the fact that the literal isn't inferred to be a function instead of a delegate is a bug. but you can force a function literal out of this: int x; auto foo() pure { return function() { x++; }; // should be able to omit 'function' } void main() { foo()(); } So you have a few choices here: 1. Update the bug to say that these should be inferred as functions (even if you don't do this, another bug should be filed). 2. Argue that a delegate with a context of a pure function should be able to be unpure, and demonstrate why that's needed with an updated use case. 3. Alter the bug report to mean something different.
Comment #5 by tomer — 2017-07-17T16:39:46Z
(In reply to Steven Schveighoffer from comment #4) > `format` actually is pure. nitpicking. sure, format may be pure, but the variables i want to log in the assert may be __gshared and then i can't access them. or i may want to invoke some impure functions to add extra info before the process dies. e.g. assert (cond, "%s x=%s".format(getTimeOfDay(), x)); > In your new example, you are actually calling the > GC-allocating function from inside a @nogc context, not creating a delegate. > Even if you did create a delegate and needed to save the context, that > creates a closure, and that definitely needs the GC. the ASSERT function takes a `scope delegate` so no closure of GC is needed. unless it blows up, and then i'm ok with that. > So you have a few choices here: > > 1. Update the bug to say that these should be inferred as functions (even if > you don't do this, another bug should be filed). > 2. Argue that a delegate with a context of a pure function should be able to > be unpure, and demonstrate why that's needed with an updated use case. > 3. Alter the bug report to mean something different. opting for 2. i will file another bug for the inference issue, but i don't see why this is not a bug. the fact i *create* a delegate in a pure scope shouldn't force the *delegate* to be pure. i shouldn't be able to *invoke* that impure delegate, just like i shouldn't be able to invoke any other impure function. but that's not the issue. once i have a delegate, i just cast it to a pure one -- and this will happen only when the assert blows up. and the function(){} hack won't help me, because i require access to arguments/variables on the function's stack.
Comment #6 by tomer — 2017-07-17T16:48:10Z
Comment #7 by schveiguy — 2017-07-17T17:51:34Z
(In reply to Tomer Filiba (weka) from comment #5) > the fact i *create* a delegate in a pure scope > shouldn't force the *delegate* to be pure. I'm not 100% sure about that, but it seems a plausible possibility. As long as you aren't calling the delegate from within the function (your ASSERT function seems to be, and assert definitely would be), then just accessing the stack frame data should be OK. As far as @nogc goes, that is simply an issue with the functions you need to call. If you need GC functions within @nogc, then you have to obscure and cast. I think it's possible to argue that assert should allow GC functionality, but not 100% sure of that. It appears that any inner functions inside a pure function are also marked pure. Since there's no "unpure" attribute, it's hard to undo this.
Comment #8 by robert.schadek — 2024-12-13T18:53:20Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19281 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB