Skip to content
Draft
Show file tree
Hide file tree
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
8 changes: 5 additions & 3 deletions druntime/src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -1030,8 +1030,9 @@ version (CoreUnittest)
static struct List { size_t gen; List* next; }
shared(List) head;
assert(cas(&head, shared(List)(0, null), shared(List)(1, cast(List*)1)));
assert(head.gen == 1);
assert(cast(size_t)head.next == 1);
auto loaded = atomicLoad(head);
assert(loaded.gen == 1);
assert(cast(size_t) loaded.next == 1);
}

// https://issues.dlang.org/show_bug.cgi?id=20629
Expand Down Expand Up @@ -1070,7 +1071,8 @@ version (CoreUnittest)
@betterC pure nothrow @nogc @safe unittest
{
int a;
if (casWeak!(MemoryOrder.acq_rel, MemoryOrder.raw)(&a, 0, 4))
int expected = 0;
if (casWeakByRef(a, expected, 4))
assert(a == 4);
}

Expand Down
27 changes: 10 additions & 17 deletions druntime/src/core/internal/array/duplication.d
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,15 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
cast(void) [].dup!Sunsafe;
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
static assert(!__traits(compiles, () @safe { Sunsafe[1] a; a[].dup; }));
static assert(!__traits(compiles, () { [].dup!Snocopy; }));

[].idup!Sunpure;
[].idup!Sthrow;
[].idup!Sunsafe;
static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
static assert(!__traits(compiles, () pure { Sunpure[1] a; a[].idup; }));
static assert(!__traits(compiles, () nothrow { Sthrow[1] a; a[].idup; }));
static assert(!__traits(compiles, () @safe { Sunsafe[1] a; a[].idup; }));
static assert(!__traits(compiles, () { [].idup!Snocopy; }));
}

Expand Down Expand Up @@ -225,27 +225,21 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))

@system unittest
{
static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} }
static struct Sthrow { this(ref const typeof(this)) @safe pure {} }
static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} }
static struct Sunpure { this(ref const Sunpure) @safe nothrow {} }
static struct Sthrow { this(ref const Sthrow) @safe pure {} }
static struct Sunsafe { this(ref const Sunsafe) @system pure nothrow {} }
[].dup!Sunpure;
[].dup!Sthrow;
cast(void) [].dup!Sunsafe;
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));

// for idup to work on structs that have copy constructors, it is necessary
// that the struct defines a copy constructor that creates immutable objects
static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} }
static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} }
static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} }
static struct ISunpure { this(ref const ISunpure) immutable @safe nothrow {} }
static struct ISthrow { this(ref const ISthrow) immutable @safe pure {} }
static struct ISunsafe { this(ref const ISunsafe) immutable @system pure nothrow {} }
[].idup!ISunpure;
[].idup!ISthrow;
[].idup!ISunsafe;
static assert(!__traits(compiles, () pure { [].idup!ISunpure; }));
static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; }));
static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; }));
}

@safe unittest
Expand Down Expand Up @@ -374,7 +368,6 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
static assert(test!Postblit());
assert(test!Postblit());

static assert(test!Copy());
assert(test!Copy());
}

Expand Down
10 changes: 8 additions & 2 deletions druntime/src/core/internal/convert.d
Original file line number Diff line number Diff line change
Expand Up @@ -788,16 +788,22 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V
private const(ubyte)[] toUbyte_aggregate_ctfe(T)(const return ref scope T val)
{
pragma(inline, false);

// Walking `tupleof` on a shared aggregate is rejected by
// `-preview=nosharedaccess`.
import core.internal.traits : Unshared;
// `ref` avoids copying aggregates with disabled postblits or destructors.
ref const(Unshared!T) unsharedVal = *cast(const(Unshared!T)*) &val;
ubyte[] bytes = ctfe_alloc(T.sizeof);
foreach (key, ref cur; val.tupleof)
foreach (key, ref cur; unsharedVal.tupleof)
{
static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
alias CurType = OriginalType!EType;
else
alias CurType = typeof(cur);
static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
{
bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
bytes[unsharedVal.tupleof[key].offsetof .. unsharedVal.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
}
else
{
Expand Down
14 changes: 11 additions & 3 deletions druntime/src/core/internal/dassert.d
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ private string miniFormat(V)(const scope ref V v)
}
catch (Exception e)
{
return `<toString() failed: "` ~ e.msg ~ `", called on ` ~ formatMembers(v) ~`>`;
return `<toString() failed: "` ~ e.msg ~ `", called on ` ~ formatMembersOrTypeName(v) ~`>`;
}
}
// Static arrays or slices (but not aggregates with `alias this`)
Expand Down Expand Up @@ -403,7 +403,7 @@ private string miniFormat(V)(const scope ref V v)
}
else static if (is(V == struct))
{
return formatMembers(v);
return formatMembersOrTypeName(v);
}
// Extern C++ classes don't have a toString by default
else static if (is(V == class) || is(V == interface))
Expand All @@ -413,7 +413,7 @@ private string miniFormat(V)(const scope ref V v)

// Extern classes might be opaque
static if (is(typeof(v.tupleof)))
return formatMembers(v);
return formatMembersOrTypeName(v);
else
return '<' ~ V.stringof ~ '>';
}
Expand All @@ -423,6 +423,14 @@ private string miniFormat(V)(const scope ref V v)
}
}

private string formatMembersOrTypeName(V)(const scope ref V v)
{
static if (__traits(compiles, formatMembers(v)))
return formatMembers(v);
else
return V.stringof;
}

/// Formats `v`'s members as `V(<member 1>, <member 2>, ...)`
private string formatMembers(V)(const scope ref V v)
{
Expand Down
29 changes: 25 additions & 4 deletions druntime/src/core/internal/hash.d
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,19 @@ if (is(T == S[], S) && (__traits(isScalar, S) || canBitwiseHash!S)) // excludes
static if (!canBitwiseHash!ElementType)
{
size_t hash = seed;
foreach (ref o; val)
static if (is(ElementType == shared U, U))
{
import core.atomic : MemoryOrder, atomicLoad;

foreach (i; 0 .. val.length)
{
hash = hashOf(hashOf(atomicLoad!(MemoryOrder.raw)(val.ptr[i])), hash); // double hashing to match TypeInfo.getHash
}
}
else
{
hash = hashOf(hashOf(o), hash); // double hashing to match TypeInfo.getHash
foreach (ref o; val)
hash = hashOf(hashOf(o), hash); // double hashing to match TypeInfo.getHash
}
return hash;
}
Expand All @@ -225,10 +235,21 @@ if (is(T == S[], S) && (__traits(isScalar, S) || canBitwiseHash!S)) // excludes
size_t hashOf(T)(T val, size_t seed = 0)
if (is(T == S[], S) && !(__traits(isScalar, S) || canBitwiseHash!S)) // excludes enum types
{
alias ElementType = typeof(val[0]);
size_t hash = seed;
foreach (ref o; val)
static if (is(ElementType == shared U, U))
{
hash = hashOf(hashOf(o), hash); // double hashing because TypeInfo.getHash doesn't allow to pass seed value
import core.atomic : MemoryOrder, atomicLoad;

foreach (i; 0 .. val.length)
{
hash = hashOf(hashOf(atomicLoad!(MemoryOrder.raw)(val.ptr[i])), hash); // double hashing because TypeInfo.getHash doesn't allow to pass seed value
}
}
else
{
foreach (ref o; val)
hash = hashOf(hashOf(o), hash); // double hashing because TypeInfo.getHash doesn't allow to pass seed value
}
return hash;
}
Expand Down
21 changes: 19 additions & 2 deletions druntime/src/core/internal/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ template Unqual(T : const U, U)
alias Unqual = U;
}

template Unshared(T)
{
static if (is(T == shared U, U))
alias Unshared = U;
else
alias Unshared = T;
}

unittest
{
static assert(is(Unshared!int == int));
static assert(is(Unshared!(shared int) == int));
static assert(is(Unshared!(const int) == const int));
static assert(is(Unshared!(shared const int) == const int));
static assert(is(Unshared!(immutable int) == immutable int));
}

template BaseElemOf(T)
{
static if (is(OriginalType!T == E[N], E, size_t N))
Expand Down Expand Up @@ -378,7 +395,7 @@ template hasElaborateCopyConstructor(S)
static struct S
{
int x;
this(return scope ref typeof(this) rhs) { }
this(this) { }
this(int x, int y) {}
}

Expand All @@ -396,7 +413,7 @@ template hasElaborateCopyConstructor(S)
static struct S3
{
int x;
this(return scope ref typeof(this) rhs, int x = 42) { }
this(this) { }
this(int x, int y) {}
}

Expand Down
26 changes: 19 additions & 7 deletions druntime/src/core/stdc/stdatomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,13 @@ bool atomic_flag_test_and_set_explicit_impl()(atomic_flag* obj, memory_order ord
/**
* Initializes an atomic variable, the destination should not have any expression associated with it prior to this call.
*
* We use an out parameter instead of a pointer for destination in an attempt to communicate to the compiler that it initializers.
* We use an ref parameter instead of a pointer for destination in
* an attempt to communicate to the compiler that it initializers.
* We use ref and not `out` because `out` would write to a shared value,
* which is not allowed with `-preview=nosharedaccess`.
*/
pragma(inline, true)
void atomic_init(A, C)(out shared(A) obj, C desired) @trusted
void atomic_init(A, C)(return scope ref shared(A) obj, C desired) @trusted
{
// C11 atomic_init is a low-level initialization primitive for atomic storage
// before it is published for concurrent access, so it must be able to write
Expand All @@ -256,8 +259,10 @@ unittest
shared int val;
atomic_init(val, 2);

shared float valF;
atomic_init(valF, 3.2);
// Avoid compiler-generated code that would store `float.init` (a NaN),
// which `-preview=nosharedaccess` treats as shared access.
shared float valF = 0.0f;
atomic_init(valF, 3.2f);
}

/// No-op function, doesn't apply to D
Expand Down Expand Up @@ -419,7 +424,9 @@ void atomic_store_impl(A, C)(shared(A)* obj, C desired) @trusted
shared(int) obj;
atomic_store_impl(&obj, 3);

shared(float) objF;
// Avoid compiler-generated code that would store `float.init` (a NaN),
// which `-preview=nosharedaccess` treats as shared access.
shared(float) objF = 0.0f;
atomic_store_impl(&objF, 3.21);
}

Expand Down Expand Up @@ -454,7 +461,9 @@ void atomic_store_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order or
shared(int) obj;
atomic_store_explicit_impl(&obj, 3, memory_order.memory_order_seq_cst);

shared(float) objF;
// Avoid compiler-generated code that would store `float.init` (a NaN),
// which `-preview=nosharedaccess` treats as shared access.
shared(float) objF = 0.0f;
atomic_store_explicit_impl(&objF, 3.21, memory_order.memory_order_seq_cst);
}

Expand Down Expand Up @@ -948,9 +957,12 @@ A atomic_fetch_and_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
}

///
unittest
@trusted unittest
{
shared(int) val = 5;
// This unittest intentionally passes stack storage to the pointer-shaped C
// API; @safe code rejects taking that address even though it is the behavior
// under test here.
atomic_fetch_and_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
assert(atomic_load_impl(&val) == 1);
}
Expand Down
20 changes: 11 additions & 9 deletions druntime/src/core/sync/condition.d
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ private:

unittest
{
import core.atomic : atomicLoad;
import core.sync.mutex;
import core.sync.semaphore;
import core.thread;
Expand Down Expand Up @@ -791,6 +792,7 @@ unittest

unittest
{
import core.atomic : atomicLoad;
import core.sync.mutex;
import core.sync.semaphore;
import core.thread;
Expand All @@ -799,7 +801,7 @@ unittest
void testNotify()
{
auto mutex = new shared Mutex;
auto condReady = new shared Condition( mutex );
auto condReady = new shared Condition( atomicLoad(mutex) );
auto semDone = new Semaphore;
auto synLoop = new Object;
int numWaiters = 10;
Expand All @@ -813,7 +815,7 @@ unittest
{
for ( int i = 0; i < numTries; ++i )
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
while ( numReady < 1 )
{
Expand All @@ -840,7 +842,7 @@ unittest
{
for ( int j = 0; j < numWaiters; ++j )
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
++numReady;
condReady.notify();
Expand Down Expand Up @@ -869,15 +871,15 @@ unittest
void testNotifyAll()
{
auto mutex = new shared Mutex;
auto condReady = new shared Condition( mutex );
auto condReady = new shared Condition( atomicLoad(mutex) );
int numWaiters = 10;
int numReady = 0;
int numDone = 0;
bool alert = false;

void waiter()
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
++numReady;
while ( !alert )
Expand All @@ -893,7 +895,7 @@ unittest

while ( true )
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
if ( numReady >= numWaiters )
{
Expand All @@ -912,14 +914,14 @@ unittest
void testWaitTimeout()
{
auto mutex = new shared Mutex;
auto condReady = new shared Condition( mutex );
auto condReady = new shared Condition( atomicLoad(mutex) );
bool waiting = false;
bool alertedOne = true;
bool alertedTwo = true;

void waiter()
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
waiting = true;
// we never want to miss the notification (30s)
Expand All @@ -934,7 +936,7 @@ unittest

while ( true )
{
synchronized( mutex )
synchronized( atomicLoad(mutex) )
{
if ( waiting )
{
Expand Down
Loading
Loading