inout and function/delegate parameters

Stewart Gordon smjg_1998 at
Sun Feb 19 06:27:42 PST 2012

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?


More information about the Digitalmars-d mailing list