On 02/19/2012 03:27 PM, Stewart Gordon wrote:
At the moment, if a function has an inout parameter, it must have an
inout return type.

But this prevents doing stuff like

void test(ref inout(int)[] x, inout(int)[] y) {
x = y;
}

or passing the constancy through to a delegate instead of a return value.

A typical use case of the latter is to define an opApply that works
regardless of the constancy of this and allows the delegate to modify
the iterated-through objects _if_ this is mutable.

int opApply(int delegate(ref inout(T)) dg) inout;

But then I realised a potential ambiguity:
(a) the constancy is passed through to the delegate
(b) the delegate has an inout parameter in its own right

If we go by interpretation (b), then each signature contains only one
inout, so even if we relaxed the rules to allow this it would just be
equivalent to

int opApply(int delegate(ref const(T)) dg) const;

however, this won't always be true in the general case.

The essence of functions with inout parameters is that they have a
hidden constancy parameter. This is essentially a template parameter,
except that only one instance of the function is generated, rather like
Java generics. If we made this parameter explicit in the code, we could
distinguish the two meanings:

(a) int opApply(constancy K)(int delegate(ref K(T)) dg) K;
(b) int opApply(constancy K)(int delegate(constancy L)(ref L(T)) dg) K;

Moreover, in case (a), opApply would accept for dg:
- an int delegate(ref T) only if this is mutable
- an int delegate(ref immutable(T)) only if this is immutable
- an int delegate(ref const(T)), or a delegate that is itself
constancy-templated, regardless of the constancy of this

Perhaps the simplest example where meaning (b) is actually useful is

inout(char)[] process(inout(char)[] delegate(inout(char)[]) dg,
inout(char[]) text) {
return text;
}

but still, somebody might want meaning (a). Anyway, under DMD 2.058
(Win32) this gives

inout_delegate.d(1): Error: inout must be all or none on top level for
inout(char)[](inout(char)[] function(inout(char)[]) fn, inout(char[]) text)

but why? At least it seems that DMD acknowledges the ambiguity, even if
the error message doesn't make sense.


The question really is: When inout is applied both to a parameter in a
function's signature and to something in the signature of a
function/delegate parameter therewithin, how should it be interpreted?
The spec doesn't seem to address the issue at all. Indeed, we can ask
two things:
- what actually does the compiler make of it at the moment?
- what would be the ideal way for it to work?

Possibilities I can see:

- always (a)
- always (b)
- (a) if at either level inout only occurs once, otherwise (b) (probably
undesirable because of fragility)
- just reject such signatures as ambiguous

And moreover, should we support some syntax (similar to what I've used
here or otherwise) to state explicitly whether we want to pass the
constancy through to the delegate signature or not?

Stewart.

I think some kind of disambiguation is essential.

I see multiple solutions:

1.

(b) would be the default. We could use the some parameter storage class to opt-in a):

(a) int opApply(@somequalifier int delegate(ref inout(T)) dg) inout;
(b) int opApply(int delegate(ref inout(T)) dg) inout;

This would work well. Maybe this is a little bit hackish, but I propose to use @somequalifier:=inout

a) int opApply(inout int delegate(ref inout(T)) dg) inout;
b) int opApply(constancy K)(int delegate(constancy L)(ref L(T)) dg) K;

Actually affecting the constancy of the delegate itself is not an useful operation anyway, but it could still be achieved:

a) int opApply(inout inout(int delegate(ref inout(T)))) dg) inout; // every inout means the same thing b) int opApply(inout(int delegate(ref inout(T)))) dg) inout; // the inout on the delegate parameter is distinct from the other two

2.

We introduce an infinite number of wildcard storage classes:
inout, inout', inout'', inout''', inout'''', ...

a) int opApply(int delegate(ref inout(T)) dg) inout;
b) int opApply(int delegate(ref inout'(T)) dg) inout;

This would have the additional boon that it would allow to type check a larger set of const-correct functions [1], like the following:

void swapPairs(ref inout(int)[] x1, ref inout'(int)[] y1,
               ref inout(int)[] x2, ref inout'(int)[] y2){
    swap(x1,x2);
    swap(y1,y2);
}


[1] furthermore, we'd finally get identifier' identifiers ;)





Reply via email to