Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions std/sumtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -635,9 +635,19 @@ public:

this.match!destroyIfOwner;

mixin("Storage newStorage = { ",
Storage.memberName!T, ": forward!rhs",
" };");
static if (isCopyable!T)
{
// Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
mixin("Storage newStorage = { ",
Storage.memberName!T, ": __ctfe ? rhs : forward!rhs",
" };");
}
else
{
mixin("Storage newStorage = { ",
Storage.memberName!T, ": forward!rhs",
" };");
}

storage = newStorage;
tag = tid;
Expand Down Expand Up @@ -678,7 +688,17 @@ public:
{
import core.lifetime : move;

rhs.match!((ref value) { this = move(value); });
rhs.match!((ref value) {
static if (isCopyable!(typeof(value)))
{
// Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
this = __ctfe ? value : move(value);
Copy link
Contributor

@nordlow nordlow Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about also adding a CTFE-version of move being executable at compile-time by replacing the memcpy with an void[]-copy for the __ctfe-case? Ping, @RazvanN7.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing a proper move in CTFE is impossible, because CTFE does not support reinterpreting casts (which includes casting to void[]). See PR #936 for related discussion. The best that can be done is to fall back to a copy, as is done here.

Copy link
Contributor

@nordlow nordlow Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict, the core of this limitation is rather that the compiler is lacking the ability to automatically (internally) perform a move of value into this at

this = value;

as this statment is the last statement that references value before this is returned. The are more such cornercases where the compiler should move (but currently doesn't) thereby avoiding these static if-branchings on isCopyable!T. Afaict, if the compiler had that ability that would unlock being able to use non-copyable types at CTFE at least in situations like these. Ping, @andralex @RazvanN7 @WalterBright.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if the compiler could automatically turn these last-use assignments into moves, the explicit calls to move and forward would no longer be necessary in the first place, so there would be no need to add workarounds to avoid calling them in CTFE.

Copy link
Contributor

@nordlow nordlow Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. That would be awesome. Moreover, there are many Phobos' ranges that would be able to operate on non-copyable source range types if that restriction was lifted.

}
else
{
this = move(value);
}
});
return this;
}
}
Expand Down Expand Up @@ -1569,6 +1589,28 @@ version (D_BetterC) {} else
}
}

// Assignment of struct with overloaded opAssign in CTFE
// https://issues.dlang.org/show_bug.cgi?id=23182
@safe unittest
{
static struct HasOpAssign
{
void opAssign(HasOpAssign rhs) {}
}

static SumType!HasOpAssign test()
{
SumType!HasOpAssign s;
// Test both overloads
s = HasOpAssign();
s = SumType!HasOpAssign();
return s;
}

// Force CTFE
enum result = test();
}

/// True if `T` is an instance of the `SumType` template, otherwise false.
private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);

Expand Down