View mode: basic / threaded / horizontal-split · Log in · Help
October 18, 2011
Re: Implicit cast to immutable
"bearophile" <bearophileHUGS@lycos.com> wrote in message 
news:j7jepi$prp$1@digitalmars.com...
> Daniel Murphy:
>
>> 2)
>> immutable(int[]) fun() { return new int[]; } // conversion happens here
>> immutable x  = fun();
>>
>> Bearophile's example is of the second, where it definately matters what 
>> the
>> purity of the function is.
>
> This is the enhancement request I have written days ago:
> http://d.puremagic.com/issues/show_bug.cgi?id=6783
>
> Bye,
> bearophile

Yes, and the problem in that report is that the function is const-pure, not 
strong-pure.
Without checking if the return type can contain a non-immutable reference 
from the arguments, it is not safe to implicitly convert the result to 
immutable.

eg.
immutable(int[]) foo(in int[] x) { return x; }
auto g = [1, 2, 3];
auto a = foo(g.idup); //safe
auto b = foo(g); // unsafe

Checking at the call site is possible, but not from inside the function.

int[] foo(in int[] x) { return new int[](3); }
auto g = [1, 2, 3];
immutable a = foo(g.idup); // safe
immutable b = foo(g); // unsafe, and easily rejected

In your example, it is safe as the argument is not returned.  Allowing this 
in the general case requires checking (recursively) that the return type 
does not contain any types that any of the arguments can implicitly convert 
to that are non-immutable.
October 19, 2011
Re: Implicit cast to immutable
On Tue, 18 Oct 2011 02:40:12 -0400, Daniel Murphy
<yebblies@nospamgmail.com> wrote:

> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
> news:op.v3h06olweav7ka@localhost.localdomain...
>>
>> That sounds like an incorrect restriction.  The implicit cast to  
>> immutable
>> should depend on whether the function being *called* qualifies, not if  
>> the
>> function you are calling *from* qualifies.
>>
>
> I think you've misunderstood what I'm saying.  The patch I made  
> implemented
> two ways to implicly convert to immutable: the result of a pure function
> returning immutable and a return statement inside a pure function.
>
> 1)
> int[] fun() { return new int[]; }
> immutable x = fun(); // conversion happens here
>
> 2)
> immutable(int[]) fun() { return new int[]; } // conversion happens here
> immutable x  = fun();

Neither of those is relevant, since they do not contain parameters (I'm  
assuming you meant them both to be pure).

>> Qualifying means the return type should be mutable, and cannot be  
>> derived
>> from the parameters without requiring casts.  The easiest way to do this
>> is to ensure the parameters are all const, immutable, or implicitly cast
>> to immutable.  You could do funky things like assume for instance an  
>> int[]
>> cannot possibly be implicit-casted to a char[], so therefore int[]
>> foo(char[] data) pure can be implicitly casted to immutable, but that
>> might be flirting with dangerous situations.
>>
>> The one exception should be allocating memory, which should always
>> qualify, even though it's not a pure function.
>>
>
> This is valid, but becomes very complicated with complex return types.  I
> doubt this will ever make it into the language.

No, it's not complicated, at least in my view.  The rules are:

1. if the pure function parameters are only immutable or
implicitly-convertible-to-immutable, the result is safe to cast to
immutable, regardless of what happens inside the function, or the type of
the result.
2. if the pure function parameters contain any mutable data that is
mutable and *not* implicitly convertible to mutable, and the result is
only safe to cast to immutable if it can already implicitly cast to
immutable.
3. if the pure function parameters are implicitly castible to immutable,
immutable, or are const, then:
      a) if the return type is implicitly castable to immutable, obviously
it can be cast.
      b) if the return type is not implicitly castable to immutable, but
contains only mutable references, then it's implicitly castable to
immutable.
      c) otherwise, it cannot be implicitly cast.

You do not need the function body to determine this, and the decision
should be made by the compiler at the call site.

Your case 2 where the conversion happens at the return statement is
already covered.

> I've got the beginnings of a patch to enable a sort of 'pure expression',
> such as new, array.dup and array concatenation expressions.  The result  
> of a
> call to a const-pure function using immutable arguments can be converted  
> to
> immutable, while calling it with mutable or const arguments cannot,  
> without
> searching the return type for anything the arguments can implicitly  
> convert
> to (or create).
>
> Eg. I can't see a great way to detect situations like this:
>
> struct S { const void* p; }

This struct could only be cast to immutable under my rule 1 above.  It
does not implicitly cast to immutable.

> S[] fun(int[] arr)
> {
>     return [ S(arr.ptr) ];
> }
> immutable x = fun([1, 2, 3]);

Invalid.  arr is not implicitly castable to immutable and is mutable, so
the result must already be implicitly castable (which it is not).  This
falls under rule 2 above, and fails the test.

-Steve
October 20, 2011
Re: Implicit cast to immutable
"Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message 
news:op.v3lug2q2eav7ka@localhost.localdomain...
> On Tue, 18 Oct 2011 02:40:12 -0400, Daniel Murphy
> <yebblies@nospamgmail.com> wrote:
>
>> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
>> news:op.v3h06olweav7ka@localhost.localdomain...
>>>
>>> That sounds like an incorrect restriction.  The implicit cast to 
>>> immutable
>>> should depend on whether the function being *called* qualifies, not if 
>>> the
>>> function you are calling *from* qualifies.
>>>
>>
>> I think you've misunderstood what I'm saying.  The patch I made 
>> implemented
>> two ways to implicly convert to immutable: the result of a pure function
>> returning immutable and a return statement inside a pure function.
>>
>> 1)
>> int[] fun() { return new int[]; }
>> immutable x = fun(); // conversion happens here
>>
>> 2)
>> immutable(int[]) fun() { return new int[]; } // conversion happens here
>> immutable x  = fun();
>
> Neither of those is relevant, since they do not contain parameters (I'm 
> assuming you meant them both to be pure).
>

Yeah, they were supposed to be pure.  I was just demonstrating that 
conversion happens at the return statement, or the call site, but only 
currently for strongly pure functions.

>>> Qualifying means the return type should be mutable, and cannot be 
>>> derived
>>> from the parameters without requiring casts.  The easiest way to do this
>>> is to ensure the parameters are all const, immutable, or implicitly cast
>>> to immutable.  You could do funky things like assume for instance an 
>>> int[]
>>> cannot possibly be implicit-casted to a char[], so therefore int[]
>>> foo(char[] data) pure can be implicitly casted to immutable, but that
>>> might be flirting with dangerous situations.
>>>
>>> The one exception should be allocating memory, which should always
>>> qualify, even though it's not a pure function.
>>>
>>
>> This is valid, but becomes very complicated with complex return types.  I
>> doubt this will ever make it into the language.
>
> No, it's not complicated, at least in my view.  The rules are:
>
> 1. if the pure function parameters are only immutable or
> implicitly-convertible-to-immutable, the result is safe to cast to
> immutable, regardless of what happens inside the function, or the type of
> the result.
> 2. if the pure function parameters contain any mutable data that is
> mutable and *not* implicitly convertible to mutable, and the result is
> only safe to cast to immutable if it can already implicitly cast to
> immutable.
> 3. if the pure function parameters are implicitly castible to immutable,
> immutable, or are const, then:
>       a) if the return type is implicitly castable to immutable, obviously
> it can be cast.
>       b) if the return type is not implicitly castable to immutable, but
> contains only mutable references, then it's implicitly castable to
> immutable.
>       c) otherwise, it cannot be implicitly cast.
>
> You do not need the function body to determine this, and the decision
> should be made by the compiler at the call site.
>
> Your case 2 where the conversion happens at the return statement is
> already covered.
>
>> I've got the beginnings of a patch to enable a sort of 'pure expression',
>> such as new, array.dup and array concatenation expressions.  The result 
>> of a
>> call to a const-pure function using immutable arguments can be converted 
>> to
>> immutable, while calling it with mutable or const arguments cannot, 
>> without
>> searching the return type for anything the arguments can implicitly 
>> convert
>> to (or create).
>>
>> Eg. I can't see a great way to detect situations like this:
>>
>> struct S { const void* p; }
>
> This struct could only be cast to immutable under my rule 1 above.  It
> does not implicitly cast to immutable.
>
>> S[] fun(int[] arr)
>> {
>>     return [ S(arr.ptr) ];
>> }
>> immutable x = fun([1, 2, 3]);
>
> Invalid.  arr is not implicitly castable to immutable and is mutable, so
> the result must already be implicitly castable (which it is not).  This
> falls under rule 2 above, and fails the test.
>
> -Steve

Ok, I think I meant to make fun pure and fun's parameter 'in', so it would 
fall under rule 3 but be rejected by 3b as it contains a const member.

It's not always simple to determine if an aggregate contains non-mutable 
members.  An example would be a class when you only have a base class 
reference.
Another problem (which we're already seeing with the existing conversions) 
is that when you screw it up the conversion fails, but doesn't give you any 
hint as to why it failed.  Making the rules more complicated is just going 
to make this worse.
October 20, 2011
Re: Implicit cast to immutable
On Wed, 19 Oct 2011 21:39:47 -0400, Daniel Murphy  
<yebblies@nospamgmail.com> wrote:

>
> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
> news:op.v3lug2q2eav7ka@localhost.localdomain...
>> On Tue, 18 Oct 2011 02:40:12 -0400, Daniel Murphy
>> <yebblies@nospamgmail.com> wrote:
>>
>>> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
>>> news:op.v3h06olweav7ka@localhost.localdomain...
>>>>
>>>> That sounds like an incorrect restriction.  The implicit cast to
>>>> immutable
>>>> should depend on whether the function being *called* qualifies, not if
>>>> the
>>>> function you are calling *from* qualifies.
>>>>
>>>
>>> I think you've misunderstood what I'm saying.  The patch I made
>>> implemented
>>> two ways to implicly convert to immutable: the result of a pure  
>>> function
>>> returning immutable and a return statement inside a pure function.
>>>
>>> 1)
>>> int[] fun() { return new int[]; }
>>> immutable x = fun(); // conversion happens here
>>>
>>> 2)
>>> immutable(int[]) fun() { return new int[]; } // conversion happens here
>>> immutable x  = fun();
>>
>> Neither of those is relevant, since they do not contain parameters (I'm
>> assuming you meant them both to be pure).
>>
>
> Yeah, they were supposed to be pure.  I was just demonstrating that
> conversion happens at the return statement, or the call site, but only
> currently for strongly pure functions.
>
>>>> Qualifying means the return type should be mutable, and cannot be
>>>> derived
>>>> from the parameters without requiring casts.  The easiest way to do  
>>>> this
>>>> is to ensure the parameters are all const, immutable, or implicitly  
>>>> cast
>>>> to immutable.  You could do funky things like assume for instance an
>>>> int[]
>>>> cannot possibly be implicit-casted to a char[], so therefore int[]
>>>> foo(char[] data) pure can be implicitly casted to immutable, but that
>>>> might be flirting with dangerous situations.
>>>>
>>>> The one exception should be allocating memory, which should always
>>>> qualify, even though it's not a pure function.
>>>>
>>>
>>> This is valid, but becomes very complicated with complex return  
>>> types.  I
>>> doubt this will ever make it into the language.
>>
>> No, it's not complicated, at least in my view.  The rules are:
>>
>> 1. if the pure function parameters are only immutable or
>> implicitly-convertible-to-immutable, the result is safe to cast to
>> immutable, regardless of what happens inside the function, or the type  
>> of
>> the result.
>> 2. if the pure function parameters contain any mutable data that is
>> mutable and *not* implicitly convertible to mutable, and the result is
>> only safe to cast to immutable if it can already implicitly cast to
>> immutable.
>> 3. if the pure function parameters are implicitly castible to immutable,
>> immutable, or are const, then:
>>       a) if the return type is implicitly castable to immutable,  
>> obviously
>> it can be cast.
>>       b) if the return type is not implicitly castable to immutable, but
>> contains only mutable references, then it's implicitly castable to
>> immutable.
>>       c) otherwise, it cannot be implicitly cast.
>>
>> You do not need the function body to determine this, and the decision
>> should be made by the compiler at the call site.
>>
>> Your case 2 where the conversion happens at the return statement is
>> already covered.
>>
>>> I've got the beginnings of a patch to enable a sort of 'pure  
>>> expression',
>>> such as new, array.dup and array concatenation expressions.  The result
>>> of a
>>> call to a const-pure function using immutable arguments can be  
>>> converted
>>> to
>>> immutable, while calling it with mutable or const arguments cannot,
>>> without
>>> searching the return type for anything the arguments can implicitly
>>> convert
>>> to (or create).
>>>
>>> Eg. I can't see a great way to detect situations like this:
>>>
>>> struct S { const void* p; }
>>
>> This struct could only be cast to immutable under my rule 1 above.  It
>> does not implicitly cast to immutable.
>>
>>> S[] fun(int[] arr)
>>> {
>>>     return [ S(arr.ptr) ];
>>> }
>>> immutable x = fun([1, 2, 3]);
>>
>> Invalid.  arr is not implicitly castable to immutable and is mutable, so
>> the result must already be implicitly castable (which it is not).  This
>> falls under rule 2 above, and fails the test.
>>
>> -Steve
>
> Ok, I think I meant to make fun pure and fun's parameter 'in', so it  
> would
> fall under rule 3 but be rejected by 3b as it contains a const member.
>
> It's not always simple to determine if an aggregate contains non-mutable
> members.  An example would be a class when you only have a base class
> reference.

I had not thought about classes (or interfaces), I was only thinking of  
concrete types.  I think in the case of classes, all parameters with  
classes must be marked as immutable to have an implicit cast of the result  
to immutable (i.e. fall under rule 1).  In reality, the rules I specify  
are enough, but they simply aren't explicit about how classes make it  
impossible to determine if any const members are aboard.

The same goes for void *, which could point to a type which has const  
members.

All cases where classes or void * pointers are involved must fall under  
rule 1, since you cannot determine whether such types are transitively  
mutable.

> Another problem (which we're already seeing with the existing  
> conversions)
> is that when you screw it up the conversion fails, but doesn't give you  
> any
> hint as to why it failed.  Making the rules more complicated is just  
> going
> to make this worse.

By letting the caller choose the return type, you are eliminating code  
duplication.

Consider the function:

int[] repeat(in int[] x, uint ntimes) pure
{
   int[] result;
   result.reserve(x.length * ntimes);
   foreach(uint i; 0..ntimes)
      result ~= x;
   return result;
}

Under my rules, you need only this one function to handle all 9 cases of  
converting between constancies of int

int[]        => int[]
int[]        => const(int)[]
int[]        => immutable(int)[]
const(int)[] => int[]
...

I think this is worth having.  In contrast, what we have now is quite  
limited, and inconsistent.

A long-standing request I've had is this:

http://d.puremagic.com/issues/show_bug.cgi?id=1654

I think this new feature of implicit immutable casting via pure is the key.

The more we make immutable data safer and easier to create and convert,  
the better off D (and pure functions) will be.

-Steve
October 21, 2011
Re: Implicit cast to immutable
"Daniel Murphy" , dans le message (digitalmars.D.learn:30139), a écrit :
> "bearophile" <bearophileHUGS@lycos.com> wrote in message 
> news:j7jepi$prp$1@digitalmars.com...
>> Daniel Murphy:
>>
>>> 2)
>>> immutable(int[]) fun() { return new int[]; } // conversion happens here
>>> immutable x  = fun();
>>>
>>> Bearophile's example is of the second, where it definately matters what 
>>> the
>>> purity of the function is.
>>
>> This is the enhancement request I have written days ago:
>> http://d.puremagic.com/issues/show_bug.cgi?id=6783
>>
>> Bye,
>> bearophile
> 
> Yes, and the problem in that report is that the function is const-pure, not 
> strong-pure.
> Without checking if the return type can contain a non-immutable reference 
> from the arguments, it is not safe to implicitly convert the result to 
> immutable.
> 
> eg.
> immutable(int[]) foo(in int[] x) { return x; }
> auto g = [1, 2, 3];
> auto a = foo(g.idup); //safe
> auto b = foo(g); // unsafe
> 
> Checking at the call site is possible, but not from inside the function.
> 
> int[] foo(in int[] x) { return new int[](3); }
> auto g = [1, 2, 3];
> immutable a = foo(g.idup); // safe
> immutable b = foo(g); // unsafe, and easily rejected
> 
> In your example, it is safe as the argument is not returned.  Allowing this 
> in the general case requires checking (recursively) that the return type 
> does not contain any types that any of the arguments can implicitly convert 
> to that are non-immutable. 

What is the rule ?
The result of a pure function can be cast to immutable if the arguments 
are immutable. That requires specific checking by the compiler. The real 
type of the argument prior to the conversion to the argument type has to 
be checked.

in, scope, or inout arguments are supposed to solve the problem with 
function site checking: without call-site checking, which are IMO a 
rather bad idea.

The result of
pure int[] foo(in int[] x);
is castable to immutable, since elements of x are not supposed to escape 
the function.

The result of
pure int[] foo(const int[] x);
is not, because the value return by foo may be elements of x.
October 24, 2011
Re: Implicit cast to immutable
On Fri, 21 Oct 2011 11:20:01 -0400, Christophe  
<travert@phare.normalesup.org> wrote:

> "Daniel Murphy" , dans le message (digitalmars.D.learn:30139), a écrit :
>> "bearophile" <bearophileHUGS@lycos.com> wrote in message
>> news:j7jepi$prp$1@digitalmars.com...
>>> Daniel Murphy:
>>>
>>>> 2)
>>>> immutable(int[]) fun() { return new int[]; } // conversion happens  
>>>> here
>>>> immutable x  = fun();
>>>>
>>>> Bearophile's example is of the second, where it definately matters  
>>>> what
>>>> the
>>>> purity of the function is.
>>>
>>> This is the enhancement request I have written days ago:
>>> http://d.puremagic.com/issues/show_bug.cgi?id=6783
>>>
>>> Bye,
>>> bearophile
>>
>> Yes, and the problem in that report is that the function is const-pure,  
>> not
>> strong-pure.
>> Without checking if the return type can contain a non-immutable  
>> reference
>> from the arguments, it is not safe to implicitly convert the result to
>> immutable.
>>
>> eg.
>> immutable(int[]) foo(in int[] x) { return x; }
>> auto g = [1, 2, 3];
>> auto a = foo(g.idup); //safe
>> auto b = foo(g); // unsafe
>>
>> Checking at the call site is possible, but not from inside the function.
>>
>> int[] foo(in int[] x) { return new int[](3); }
>> auto g = [1, 2, 3];
>> immutable a = foo(g.idup); // safe
>> immutable b = foo(g); // unsafe, and easily rejected
>>
>> In your example, it is safe as the argument is not returned.  Allowing  
>> this
>> in the general case requires checking (recursively) that the return type
>> does not contain any types that any of the arguments can implicitly  
>> convert
>> to that are non-immutable.
>
> What is the rule ?

The theoretical rule should be, if it can be proven (except for the case  
where a cast is used) the result is not a subset of the input, then the  
result can be implicitly cast to immutable.

The actual rule may be more conservative, since it may be difficult to  
prove it.

> The result of
> pure int[] foo(in int[] x);
> is castable to immutable, since elements of x are not supposed to escape
> the function.
>
> The result of
> pure int[] foo(const int[] x);
> is not, because the value return by foo may be elements of x.

Only via cast.  How does foo legally change const int[] data to int[] data  
without a cast?

Note that the two functions you wrote are equivalent, since in translates  
to "const scope" and scope does nothing to array parameters.

-Steve
October 27, 2011
Re: Implicit cast to immutable
"Steven Schveighoffer" , dans le message (digitalmars.D.learn:30255), a
écrit :
> On Fri, 21 Oct 2011 11:20:01 -0400, Christophe  
> <travert@phare.normalesup.org> wrote:
> 
>> "Daniel Murphy" , dans le message (digitalmars.D.learn:30139), a écrit :
>>> "bearophile" <bearophileHUGS@lycos.com> wrote in message
>>> news:j7jepi$prp$1@digitalmars.com...
>>>> Daniel Murphy:
>>>>
>>>>> 2)
>>>>> immutable(int[]) fun() { return new int[]; } // conversion happens  
>>>>> here
>>>>> immutable x  = fun();
>>>>>
>>>>> Bearophile's example is of the second, where it definately matters  
>>>>> what
>>>>> the
>>>>> purity of the function is.
>>>>
>>>> This is the enhancement request I have written days ago:
>>>> http://d.puremagic.com/issues/show_bug.cgi?id=6783
>>>>
>>>> Bye,
>>>> bearophile
>>>
>>> Yes, and the problem in that report is that the function is const-pure,  
>>> not
>>> strong-pure.
>>> Without checking if the return type can contain a non-immutable  
>>> reference
>>> from the arguments, it is not safe to implicitly convert the result to
>>> immutable.
>>>
>>> eg.
>>> immutable(int[]) foo(in int[] x) { return x; }
>>> auto g = [1, 2, 3];
>>> auto a = foo(g.idup); //safe
>>> auto b = foo(g); // unsafe
>>>
>>> Checking at the call site is possible, but not from inside the function.
>>>
>>> int[] foo(in int[] x) { return new int[](3); }
>>> auto g = [1, 2, 3];
>>> immutable a = foo(g.idup); // safe
>>> immutable b = foo(g); // unsafe, and easily rejected
>>>
>>> In your example, it is safe as the argument is not returned.  Allowing  
>>> this
>>> in the general case requires checking (recursively) that the return type
>>> does not contain any types that any of the arguments can implicitly  
>>> convert
>>> to that are non-immutable.
>>
>> What is the rule ?
> 
> The theoretical rule should be, if it can be proven (except for the case  
> where a cast is used) the result is not a subset of the input, then the  
> result can be implicitly cast to immutable.
> 
> The actual rule may be more conservative, since it may be difficult to  
> prove it.

OK, it must be proven from the signature of the function alone, and not 
from the function body.

>> The result of
>> pure int[] foo(in int[] x);
>> is castable to immutable, since elements of x are not supposed to escape
>> the function.
>>
>> The result of
>> pure int[] foo(const int[] x);
>> is not, because the value return by foo may be elements of x.
> 
> Only via cast.  How does foo legally change const int[] data to int[] data  
> without a cast?
> 
> Note that the two functions you wrote are equivalent, since in translates  
> to "const scope" and scope does nothing to array parameters.
> 

I meant:
pure const(int)[] foo(const/in int[] x);

But I thought scope was transitive like const, so it had effect on 
arrays. I find it pretty useless like that. A transitive scope could be 
very useful for purity checking and memory management optimisations. It 
would be useful for implicit casting to immutable too, even in no pure 
function. I may write about that later in another thread.
Next ›   Last »
1 2
Top | Discussion index | About this forum | D home