Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 0914668

Browse files
committed
Fix Issue 19370 - AA require() and update() can't be used in @safe code
1 parent 0450507 commit 0914668

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

src/object.d

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,7 +3163,18 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
31633163
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
31643164
{
31653165
bool found;
3166-
auto p = cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found);
3166+
// if key is @safe-ly copyable, `require` can infer @safe
3167+
static if (isSafeCopyable!K)
3168+
{
3169+
auto p = () @trusted
3170+
{
3171+
return cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3172+
} ();
3173+
}
3174+
else
3175+
{
3176+
auto p = cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3177+
}
31673178
return found ? *p : (*p = value);
31683179
}
31693180

@@ -3192,6 +3203,9 @@ private
31923203
}
31933204
}
31943205

3206+
// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
3207+
private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
3208+
31953209
/***********************************
31963210
* Looks up key; if it exists applies the update delegate else evaluates the
31973211
* create delegate and adds it to the associative array
@@ -3205,13 +3219,54 @@ void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
32053219
if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
32063220
{
32073221
bool found;
3208-
auto p = cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found);
3222+
// if key is @safe-ly copyable, `update` may infer @safe
3223+
static if (isSafeCopyable!K)
3224+
{
3225+
auto p = () @trusted
3226+
{
3227+
return cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3228+
} ();
3229+
}
3230+
else
3231+
{
3232+
auto p = cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3233+
}
32093234
if (!found)
32103235
*p = create();
32113236
else
32123237
*p = update(*p);
32133238
}
32143239

3240+
unittest
3241+
{
3242+
static struct S
3243+
{
3244+
int x;
3245+
@nogc nothrow pure:
3246+
this(this) @system {}
3247+
3248+
@safe const:
3249+
// stubs
3250+
bool opEquals(S rhs) { assert(0); }
3251+
size_t toHash() { assert(0); }
3252+
}
3253+
3254+
int[string] aai;
3255+
static assert(is(typeof(() @safe { aai.require("a", 1234); })));
3256+
static assert(is(typeof(() @safe { aai.update("a", { return 1234; }, (ref int x) { x++; return x; }); })));
3257+
3258+
S[string] aas;
3259+
static assert(is(typeof(() { aas.require("a", S(1234)); })));
3260+
static assert(is(typeof(() { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
3261+
static assert(!is(typeof(() @safe { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
3262+
3263+
int[S] aais;
3264+
static assert(is(typeof(() { aais.require(S(1234), 1234); })));
3265+
static assert(is(typeof(() { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
3266+
static assert(!is(typeof(() @safe { aais.require(S(1234), 1234); })));
3267+
static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
3268+
}
3269+
32153270
private void _destructRecurse(E, size_t n)(ref E[n] arr)
32163271
{
32173272
import core.internal.traits : hasElaborateDestructor;

0 commit comments

Comments
 (0)