From 37d327d9972954fb4d1035a90fdc06c6fe1a46be Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Thu, 19 Aug 2021 21:56:51 -0400 Subject: [PATCH] Relax @safe restrictions on opAssign Overwriting a SumType's value with a pointer can be @safe so long as the old value is known, at compile time, to be either (a) a non-pointer, or (b) a pointer of the same type as the new value. This change allows such assignments in @safe code, while still forbidding other assignments that overwrite pointers. See also: - https://issues.dlang.org/show_bug.cgi?id=22225 - https://github.com/dlang/phobos/pull/8201 Fixes #67 on Github. --- src/sumtype.d | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/sumtype.d b/src/sumtype.d index f986c33..a1acde8 100644 --- a/src/sumtype.d +++ b/src/sumtype.d @@ -499,26 +499,27 @@ public: /** * Assigns a value to a `SumType`. * - * Assigning to a `SumType` is `@system` if any of the - * `SumType`'s members contain pointers or references, since - * those members may be reachable through external references, - * and overwriting them could therefore lead to memory - * corruption. + * Assigning to a `SumType` is `@system` if any of the `SumType`'s + * $(I other) members contain pointers or references, since those + * members may be reachable through external references, and + * overwriting them could therefore lead to memory corruption. * * An individual assignment can be `@trusted` if the caller can - * guarantee that there are no outstanding references to $(I any) - * of the `SumType`'s members when the assignment occurs. + * guarantee that, when the assignment occurs, there are no + * outstanding references to any such members. */ ref SumType opAssign(T rhs) { import core.lifetime: forward; import std.traits: hasIndirections, hasNested; - import std.meta: Or = templateOr; + import std.meta: AliasSeq, Or = templateOr; - enum mayContainPointers = - anySatisfy!(Or!(hasIndirections, hasNested), Types); + alias OtherTypes = + AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]); + enum unsafeToOverwrite = + anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes); - static if (mayContainPointers) { + static if (unsafeToOverwrite) { cast(void) () @system {}(); } @@ -1388,6 +1389,15 @@ version (D_BetterC) {} else assert(x.typeIndex == y.typeIndex); } +// @safe assignment to the only pointer in a SumType +@safe unittest { + SumType!(string, int) sm = 123; + + assert(__traits(compiles, () @safe { + sm = "this should be @safe"; + })); +} + /// True if `T` is an instance of the `SumType` template, otherwise false. private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);