Thread overview
Cannot use auto ref passing in expression template operator overloads
Jan 18, 2017
Nordlöw
Jan 18, 2017
Nordlöw
Jan 18, 2017
Eugene Wissner
January 18, 2017
I'm currently writing a high-level GMP-wrapper at

https://github.com/nordlow/gmp-d

In order to get efficient scheduling of calls to C API __gmpz-functions, I want expressions such as

    MpZ(3) + MpZ(4)

to return an instance of a lazy expression `MpzAddExpr` if and only if *both* it's arguments are passed by move to the `MpzAddExpr`-constructor. This is possible to statically detect in `auto-ref`-passing *free* functions such as

auto add()(auto ref const MpZ a, auto ref const MpZ b)
{
    static if (!isRef!a && !isRef!b) // both r-values
    {
        // PROBLEM!!!: this case cannot be implemented
        // in operator overloads because
        // __traits(isRef, this) is incorrectly always true

        // safe @nogc and no-RC lazy evaluation is possible
        return MpZAddExpr!(MpZ, MpZ)(move(a), move(b));
    }
    else static if (!isRef!a) // r-value a
    {
        // `a` is an r-value and can be added in-place as
        mpz_add(a._ptr, a._ptr, b._rhs)
        return move(a); of type MpZ
    }
    else static if (!isRef!b) // r-value b
    {
        // `b` is an r-value and can be added in-place as
        mpz_add(b._ptr, b._ptr, a._rhs) // commutative
        return move(b); // of type MpZ
    }
    else // both references
    {
        // direct evaluation is needed
        Z c; // separate allocation needed
        mpz_add(c._ptr, a._ptr, b._ptr);
        return c; // of type MpZ
    }
}

because parameters are passed by D's clever `auto-ref` semantics. But the same behaviour *cannot* be implemented using operator overloads for, in this case, `opBinary`-plus.

The problem boils down to the fact that

    __traits(isRef, this)

in members such as

struct S
{
    opUnary(string)() if (s == "-")
    {
        pragma(msg, __traits(isRef, this));
    }
}

evaluates to true in expressions such as

    -(S.init)

eventhough `S` here clearly is an r-value.

This blocks the library from applying nice optimizations during the evaluation of these lazily-evaluted expressions if operator oveloading is to be used.

I expected this to be fixed by qualifying the member as `auto ref`

    opUnary(string)() auto ref if (s == "-")

but that errors.

Is this by design or by accident?

Is there a way around this problem?

For details see

MpZ at https://github.com/nordlow/gmp-d/blob/master/src/gmp.d#L38
MpZAddExpr https://github.com/nordlow/gmp-d/blob/master/src/gmp.d#L1835
January 18, 2017
On Wednesday, 18 January 2017 at 10:49:43 UTC, Nordlöw wrote:
> https://github.com/nordlow/gmp-d/blob/master/src/gmp.d#L1835

Should be

https://github.com/nordlow/gmp-d/blob/master/src/gmp.d#L1853
January 18, 2017
On Wednesday, 18 January 2017 at 10:49:43 UTC, Nordlöw wrote:
> Is there a way around this problem?

I had the same problem with opSliceAssign, opIndexAssign lately.