Against enforce()
Don
nospam at nospam.com
Mon Mar 21 07:08:56 PDT 2011
Steven Schveighoffer wrote:
> On Fri, 18 Mar 2011 20:06:16 -0400, Don <nospam at nospam.com> wrote:
>
>> Steven Schveighoffer wrote:
>>> On Fri, 18 Mar 2011 14:35:27 -0400, Don <nospam at nospam.com> wrote:
>>>
>>>> Steven Schveighoffer wrote:
>>>>> On Fri, 18 Mar 2011 04:34:54 -0400, Don <nospam at nospam.com> wrote:
>>>>>
>>>>>>> Steven Schveighoffer Wrote:
>>>>>>>
>>>>>>>> As long as the delegate does not access shared/global data, it
>>>>>>>> should be able to be pure. Even delegates which modify TLS
>>>>>>>> data should be able to be pure (weak-pure, but still pure).
>>>>>>
>>>>>> TLS variables are global and must not be accessed from any
>>>>>> function marked as pure. With regard to purity, there isn't any
>>>>>> difference between shared and TLS variables.
>>>>> However, it's still not shared.
>>>>> This, for example, is a weak pure function:
>>>>> void foo(int *n) pure { *n = 5;}
>>>>> Because TLS variables are not shared, you should be able to do this:
>>>>> int x;
>>>>> void bar()
>>>>> {
>>>>> foo(&x);
>>>>> }
>>>>
>>>> Yes, that compiles fine. But bar() is not pure.
>>>>
>>>>> But you are right, there is a huge difference between a local
>>>>> reference to TLS data and directly accessing TLS data -- the latter
>>>>> can be obscured from the compiler, resulting in the compiler
>>>>> thinking the function can be strong pure.
>>>>> So I don't know exactly how to mitigate this, but in my mind, it
>>>>> feels like this should work:
>>>>> int foo(bool cond, lazy int n) pure { if(cond) return n; return 0;}
>>>>> int x;
>>>>> void bar()
>>>>> {
>>>>> foo(x == 4, x = 5);
>>>>> }
>>>>> It seems not too different from the above example where you pass
>>>>> the address of x. But obviously the x = 5 delegate cannot be pure
>>>>> (it modifies TLS data).
>>>>> We may have no recourse to get this to work. It may be a lost
>>>>> cause, and you just can't have lazy variables for pure functions.
>>>>
>>>> It's not a lost cause, it's a two-liner!
>>>> mtype.c line 5045:
>>>>
>>>> if (fparam->storageClass & STClazy)
>>>> {
>>>> - error(0, "cannot have lazy parameters to a pure
>>>> function");
>>>> + tf->purity = PUREweak;
>>>> + break;
>>>> }
>>>>
>>>> This is a bit conservative: it would be possible to allow lazy
>>>> parameters to be marked as pure, which would allow them to be
>>>> strongly pure. But that would probably not be worth the extra
>>>> complexity.
>>> I'm not sure this works. Aren't you allowed to pass in a delegate
>>> to a lazy parameter?
>>
>> Yes.
>>
>>> For example:
>>> shared int x;
>>> int foo()
>>> {
>>> return x;
>>> }
>>> int bar(lazy int n) pure
>>> {
>>> return n;
>>> }
>>> void baz()
>>> {
>>> bar(&foo);
>>> }
>>> or alternatively:
>>> void baz()
>>> {
>>> bar(x);
>>> }
>>
>> This compiles just fine. (Well, you need to use bar(foo) not bar(&foo)).
>> But if you try to mark baz() as pure, here's what you get:
>>
>> test0.d(135): Error: pure function 'baz' cannot call impure function
>> 'foo'
>
> But does this make sense? A pure function (bar) is reading a shared
> integer via foo, I thought that was a big no-no?
>
>> or for the second case:
>>
>> test0.d(136): Error: pure function 'baz' cannot access mutable static
>> data 'x'
>>
>> bar is just weakly pure.
>
> But I wasn't saying baz is pure, I was saying bar is pure (probably
> should be more diverse in the names). But I'm concerned about a pure
> function being able to indirectly read/write shared data. Does this
> make sense? I guess a weak-pure function acts like a normal function
> when called from a normal-function. Is that why it's ok?
Yes.
> Will there
> not be an expectation that a pure function will not read/write shared
> data that will be broken (i.e. why did the compiler allow this, I
> thought I was safe from this!)?
If you pass the address of global or shared data into a weakly pure
function, it won't be pure. If you have a function with a strongly pure
signature, global data can never be passed to it.
> So is the rule that if you pass a non-pure delegate into a pure function
> it's automatically weak-pure?
Yes.
> If so, does this not mean we need two
> versions of pure functions that take delegates, one that takes a pure
> delegate, and one that takes a non-pure one? Otherwise, how do you know
> what's strong and what's weak? For example, a weak pure function is
> strong when called from a strong-pure function, so you could say if the
> calling function is pure, the call is strong-pure. But wouldn't you
> need separate generated code for the two cases?
Generally not. Basically, all weak-pure means, is that it's OK for a
strongly pure function to call it. And that's pretty much all it means.
If it's called from outside a strongly pure function, it's just a normal
impure function.
Now, there is some potential for a call to a weakly pure function to be
considered as strongly pure, even when called from a normal function, if
all the parameters are pure. But that's a secondary effect (and the
compiler currently never does it): the real value of weakly pure is that
it allows a dramatic increase in the number of strongly pure functions.
> I guess you can see from the number of question marks, I'm not sure
> about this at all, either way :) If you think it will work, then I
> trust your judgment.
>
> -Steve
More information about the Digitalmars-d
mailing list