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

Commit 29ce054

Browse files
authored
Merge pull request #2363 from radcapricorn/fix19370
Fix Issue 19370 - AA require() and update() can't be used in @safe code merged-on-behalf-of: Jacob Carlborg <jacob-carlborg@users.noreply.github.com>
2 parents 8d10c7b + 0914668 commit 29ce054

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
@@ -3164,7 +3164,18 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
31643164
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
31653165
{
31663166
bool found;
3167-
auto p = cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found);
3167+
// if key is @safe-ly copyable, `require` can infer @safe
3168+
static if (isSafeCopyable!K)
3169+
{
3170+
auto p = () @trusted
3171+
{
3172+
return cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3173+
} ();
3174+
}
3175+
else
3176+
{
3177+
auto p = cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
3178+
}
31683179
return found ? *p : (*p = value);
31693180
}
31703181

@@ -3193,6 +3204,9 @@ private
31933204
}
31943205
}
31953206

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

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

0 commit comments

Comments
 (0)