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