Jump to page: 1 2
Thread overview
relax inout rules?
Dec 13, 2011
Jesse Phillips
Dec 13, 2011
Walter Bright
Dec 13, 2011
Timon Gehr
Dec 13, 2011
Walter Bright
Dec 13, 2011
Walter Bright
Dec 13, 2011
Timon Gehr
Dec 13, 2011
Walter Bright
Dec 13, 2011
kenji hara
Dec 13, 2011
Timon Gehr
Dec 13, 2011
kenji hara
Dec 27, 2011
Don
Dec 27, 2011
Kagamin
Dec 27, 2011
kenji hara
Jan 19, 2012
kenji hara
December 12, 2011
Currently, the rules of inout say you cannot put inout on a parameter without putting it on the return type as well.  This makes sense, it gives the compiler one place to worry about whether implicit casting rules will properly work.

But Timon Gehr brought up in another thread (started by Merdhad) that we can separate the resolution of inout from the applicability of the parameters.  Essentially, calling an inout function goes through two phases:

1. determine what inout resolves to
2. try and call the function.

inout rules don't have to change regarding resolving inout's actual value.  In other words, if all inout places are matched as int, immutable, or inout, then inout resolves to that value.  Otherwise inout resolves to const.

But what's different is (at least in my mind) the function call might not necessarily succeed even when the resolution is completed.

Take for example:

inout(int)* foo(inout(int)** a, inout(int)* b);

Now, if we call foo like this:

int a;
int b;
int* pa = &a;

foo(&pa, &b);

This means foo is called with (int **, int *).  This means inout resolves to mutable (no qualifier).  *NOW* we try calling foo as if it were written:

int *foo(int **a, int *b)

And it can be done.  Not only that, but there is nothing bad that could be happening in foo that should be disallowed by the compiler.

Now let's see what happens were we *could* do something bad:

immutable(int) c;
auto pc = &c;

foo(&pc, &b);

Now, foo is being called with (immutable(int)**, int *).  Inout resolves to const (due to the mix of mutable and immutable).  *NOW* we try calling foo as if it were written:

const(int)* foo(const(int)** a, const(int)* b);

And it *FAILS*.  This is because you cannot implicitly convert immutable(int)** to const(int)** (well, at least it *shouldn't* compile, I'm not sure if it does currently).

What this does is allow more possibilities for inout than we currently do.  Because inout is not now tied to returning something, we can create functions that have inout parameters but no inout return value.

The example we were discussing on the other thread was this:

void foo(ref inout(int)* a, inout(int)* b) { a = b;}

This would compile as long as you call with const(int)* as the first parameter, or if both parameters matched in constancy (i.e. both were immutable, both were mutable, or both were inout).

This gives us more cases where you don't have to repeat functions for the sake of handling different types of constancy, particularly when we have mutable references 2 levels deep.  I can't see anything technically wrong with this, can anyone else?

The one thing that I think still should be required is if you have inout on the return value, there *must* be an inout on a parameter.  Otherwise, the compiler has no idea what it should be (since we don't overload on return type in D).

If this ends up being viable, this is actually easier to explain than the current rules for inout.  We just have to make sure the rules are sound before doing something like this.

-Steve
December 13, 2011
On Mon, 12 Dec 2011 13:56:43 -0500, Steven Schveighoffer wrote:

> The example we were discussing on the other thread was this:
> 
> void foo(ref inout(int)* a, inout(int)* b) { a = b;}

Is this just so it is easier to explain inout and won't have to explain that const will do the same thing? Or does this actually do something using const there doesn't?

void f(ref const(int)* a, const(int)* b) {a = b; }
void main() {
   const(int)* a;
   immutable(int)* b;
   auto c = (new int[](5)).ptr;
   f(a, c);
   f(b, c); //test.d(7): Error: cast(const(int)*)b is not an lvalue
}
December 13, 2011
On 12/12/2011 10:56 AM, Steven Schveighoffer wrote:
> If this ends up being viable, this is actually easier to explain than the
> current rules for inout. We just have to make sure the rules are sound before
> doing something like this.

I don't understand the point of doing this. Just make it const.
December 13, 2011
2011/12/13 Steven Schveighoffer <schveiguy@yahoo.com>:
> Currently, the rules of inout say you cannot put inout on a parameter without putting it on the return type as well.  This makes sense, it gives the compiler one place to worry about whether implicit casting rules will properly work.
>
> But Timon Gehr brought up in another thread (started by Merdhad) that we can separate the resolution of inout from the applicability of the parameters.  Essentially, calling an inout function goes through two phases:
>
> 1. determine what inout resolves to
> 2. try and call the function.
>
> inout rules don't have to change regarding resolving inout's actual value.  In other words, if all inout places are matched as int, immutable, or inout, then inout resolves to that value.  Otherwise inout resolves to const.
>
> But what's different is (at least in my mind) the function call might not necessarily succeed even when the resolution is completed.
>
> Take for example:
>
> inout(int)* foo(inout(int)** a, inout(int)* b);
>
> Now, if we call foo like this:
>
> int a;
> int b;
> int* pa = &a;
>
> foo(&pa, &b);
>
> This means foo is called with (int **, int *).  This means inout resolves to
> mutable (no qualifier).  *NOW* we try calling foo as if it were written:
>
> int *foo(int **a, int *b)
>
> And it can be done.  Not only that, but there is nothing bad that could be happening in foo that should be disallowed by the compiler.
>
> Now let's see what happens were we *could* do something bad:
>
> immutable(int) c;
> auto pc = &c;
>
> foo(&pc, &b);
>
> Now, foo is being called with (immutable(int)**, int *).  Inout resolves to
> const (due to the mix of mutable and immutable).  *NOW* we try calling foo
> as if it were written:
>
> const(int)* foo(const(int)** a, const(int)* b);
>
> And it *FAILS*.  This is because you cannot implicitly convert
> immutable(int)** to const(int)** (well, at least it *shouldn't* compile, I'm
> not sure if it does currently).

Separating phases is good.
Current implementation does not separate the two phases, and I agree
that has a bug you appears.

And, we need fixing issue 4251 and 5493 to fill the hole of const system. Today, I've posted a pull for it. https://github.com/D-Programming-Language/dmd/pull/558

> What this does is allow more possibilities for inout than we currently do.  Because inout is not now tied to returning something, we can create functions that have inout parameters but no inout return value.
>
> The example we were discussing on the other thread was this:
>
> void foo(ref inout(int)* a, inout(int)* b) { a = b;}
>
> This would compile as long as you call with const(int)* as the first
> parameter, or if both parameters matched in constancy (i.e. both were
> immutable, both were mutable, or both were inout).
>
> This gives us more cases where you don't have to repeat functions for the sake of handling different types of constancy, particularly when we have mutable references 2 levels deep.  I can't see anything technically wrong with this, can anyone else?
>
> The one thing that I think still should be required is if you have inout on the return value, there *must* be an inout on a parameter.  Otherwise, the compiler has no idea what it should be (since we don't overload on return type in D).
>
> If this ends up being viable, this is actually easier to explain than the current rules for inout.  We just have to make sure the rules are sound before doing something like this.
>
> -Steve

Against an inout function that does not return inout type:
- the number of inout parameters should be 2 more?
- at least one parameter should have 'out' or 'ref' storage class as a
*return parameter*?

But I'm not sure these restrictions are necessarily required.

Kenji Hara
December 13, 2011
On 12/13/2011 07:26 AM, Walter Bright wrote:
> On 12/12/2011 10:56 AM, Steven Schveighoffer wrote:
>> If this ends up being viable, this is actually easier to explain than the
>> current rules for inout. We just have to make sure the rules are sound
>> before
>> doing something like this.
>
> I don't understand the point of doing this. Just make it const.

It does not work with const. (the fact that it currently does with DMD is a severe bug).

Consider:

void bad(const(int)** x, const(int)* y){ *x=y; }

void main(){
    immutable(int) i = 1;
    int* x;
    bad(&x, &i); // should error int** does not convert to const(int)**
    // currently:
    assert(x is &i); // mutable reference to immutable data
    *x = 2; // BOOM!
}

On the other hand:

void main(){
    immutable(int) i = 1;
    immutable(int)* x;
    bad(&x, &i); // fine, but the type checker cannot know that
}

Therefore it is sensible to allow

void good(inout(int)** x, inout(int)* y){ *x=y; }

void main(){
    immutable(int) i = 1;
    int* x;
    good(&x, &i); // error, no value for inout makes this typecheck
}

On the other hand:

void good(inout(int)** x, inout(int)* y){ *x=y; }

void main(){
    immutable(int) i = 1;
    immutable(int)* x;
    good(&x, &i); // this can now typecheck: use inout as immutable
}








December 13, 2011
On 12/13/2011 09:41 AM, kenji hara wrote:
> 2011/12/13 Steven Schveighoffer<schveiguy@yahoo.com>:
>> Currently, the rules of inout say you cannot put inout on a parameter
>> without putting it on the return type as well.  This makes sense, it gives
>> the compiler one place to worry about whether implicit casting rules will
>> properly work.
>>
>> But Timon Gehr brought up in another thread (started by Merdhad) that we can
>> separate the resolution of inout from the applicability of the parameters.
>>   Essentially, calling an inout function goes through two phases:
>>
>> 1. determine what inout resolves to
>> 2. try and call the function.
>>
>> inout rules don't have to change regarding resolving inout's actual value.
>>   In other words, if all inout places are matched as int, immutable, or
>> inout, then inout resolves to that value.  Otherwise inout resolves to
>> const.
>>
>> But what's different is (at least in my mind) the function call might not
>> necessarily succeed even when the resolution is completed.
>>
>> Take for example:
>>
>> inout(int)* foo(inout(int)** a, inout(int)* b);
>>
>> Now, if we call foo like this:
>>
>> int a;
>> int b;
>> int* pa =&a;
>>
>> foo(&pa,&b);
>>
>> This means foo is called with (int **, int *).  This means inout resolves to
>> mutable (no qualifier).  *NOW* we try calling foo as if it were written:
>>
>> int *foo(int **a, int *b)
>>
>> And it can be done.  Not only that, but there is nothing bad that could be
>> happening in foo that should be disallowed by the compiler.
>>
>> Now let's see what happens were we *could* do something bad:
>>
>> immutable(int) c;
>> auto pc =&c;
>>
>> foo(&pc,&b);
>>
>> Now, foo is being called with (immutable(int)**, int *).  Inout resolves to
>> const (due to the mix of mutable and immutable).  *NOW* we try calling foo
>> as if it were written:
>>
>> const(int)* foo(const(int)** a, const(int)* b);
>>
>> And it *FAILS*.  This is because you cannot implicitly convert
>> immutable(int)** to const(int)** (well, at least it *shouldn't* compile, I'm
>> not sure if it does currently).
>
> Separating phases is good.
> Current implementation does not separate the two phases, and I agree
> that has a bug you appears.
>
> And, we need fixing issue 4251 and 5493 to fill the hole of const system.
> Today, I've posted a pull for it.
> https://github.com/D-Programming-Language/dmd/pull/558
>
>> What this does is allow more possibilities for inout than we currently do.
>>   Because inout is not now tied to returning something, we can create
>> functions that have inout parameters but no inout return value.
>>
>> The example we were discussing on the other thread was this:
>>
>> void foo(ref inout(int)* a, inout(int)* b) { a = b;}
>>
>> This would compile as long as you call with const(int)* as the first
>> parameter, or if both parameters matched in constancy (i.e. both were
>> immutable, both were mutable, or both were inout).
>>
>> This gives us more cases where you don't have to repeat functions for the
>> sake of handling different types of constancy, particularly when we have
>> mutable references 2 levels deep.  I can't see anything technically wrong
>> with this, can anyone else?
>>
>> The one thing that I think still should be required is if you have inout on
>> the return value, there *must* be an inout on a parameter.  Otherwise, the
>> compiler has no idea what it should be (since we don't overload on return
>> type in D).
>>
>> If this ends up being viable, this is actually easier to explain than the
>> current rules for inout.  We just have to make sure the rules are sound
>> before doing something like this.
>>
>> -Steve
>
> Against an inout function that does not return inout type:
> - the number of inout parameters should be 2 more?

If we required that, then IFTI would also have to replace inout with const in case there is only one of them. That potentially introduces counter-intuitive behaviour.

> - at least one parameter should have 'out' or 'ref' storage class as a
> *return parameter*?

Not necessarily.

>
> But I'm not sure these restrictions are necessarily required.
>
> Kenji Hara

I think we should indeed drop the restrictions, because they complicate the language for little gain.


December 13, 2011
2011/12/13 Timon Gehr <timon.gehr@gmx.ch>:
> On 12/13/2011 09:41 AM, kenji hara wrote:
[snip]
>>
>> Against an inout function that does not return inout type: - the number of inout parameters should be 2 more?
>
> If we required that, then IFTI would also have to replace inout with const in case there is only one of them. That potentially introduces counter-intuitive behaviour.

Maybe it is an enhancement filed as 6809.

>> - at least one parameter should have 'out' or 'ref' storage class as a *return parameter*?
>
> Not necessarily.
>
>> But I'm not sure these restrictions are necessarily required.
>
> I think we should indeed drop the restrictions, because they complicate the language for little gain.

Indeed.

Kenji Hara
December 13, 2011
I never thought of that. It's a sensible argument.
December 13, 2011
Is this bug and enhancement suggestion on bugzilla?
December 13, 2011
On 12/13/2011 08:06 PM, Walter Bright wrote:
> Is this bug and enhancement suggestion on bugzilla?

I think the bug has just been fixed by Kenji Hara:
https://github.com/D-Programming-Language/dmd/pull/558

I have filed an enhancement:
http://d.puremagic.com/issues/show_bug.cgi?id=7105
« First   ‹ Prev
1 2