Skip to content

Passing rvalues with a reference is unintuitive with preview=rvaluerefparam #22078

@limepoutine

Description

@limepoutine

D's ternary operator (? :) can have reference semantics despite being rvalues. For example, (a() ? a() : b()).mutate() mutates a() if a() returns by reference and happens to be truthy, even if b() returns by value. However, -preview=rvaluerefparam requires implementations to always copy rvalue parameters, allowing the result of function calls to be subtly different from hand-inlined code.

struct S {
   int i;
}

__gshared S gs = S(1);
ref S getRef() => gs;
S getVal() => gs;
int inc(ref S s) => ++s.i;

void main()
{
    import core.stdc.stdio;

    // modifies a copy of gs
    inc(getRef().i ? getRef() : getVal());
    printf("%d\n", gs.i);

    // modifies gs
    ++((getRef().i ? getRef() : getVal()).i);
    printf("%d\n", gs.i);
}

In the code above, the inc() call copies its argument and has no side-effect on gs. However, after several versions, the maintainer decides that getVal() should be ref to improve performance. Such a tiny change suddenly changes getRef().i ? getRef() : getVal() into an lvalue, and the code now prints 2 3 instead of 1 2.

The wording in dlang/dlang.org#3924 is not clear about the situation. That all reference semantics is lost when passing an rvalue should be emphasized.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions