Against enforce()

Steven Schveighoffer schveiguy at yahoo.com
Mon Mar 21 05:54:49 PDT 2011


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?  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!)?

So is the rule that if you pass a non-pure delegate into a pure function  
it's automatically weak-pure?  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?

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