From dc697c8dd8519cefc01b75ace597dce1e797b2ad Mon Sep 17 00:00:00 2001 From: wolframw Date: Tue, 28 Dec 2021 18:47:15 +0100 Subject: [PATCH] Fix Issue 22105 - std.container.array.Array.length setter creates values of init-less types --- std/algorithm/mutation.d | 18 +++++++++++++++- std/container/array.d | 45 +++++++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index e556f64f2..b0e770768 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -866,6 +866,9 @@ if (isInputRange!InputRange Initializes all elements of `range` with their `.init` value. Assumes that the elements of the range are uninitialized. +This function is unavailable if `T` is a `struct` and `T.this()` is annotated +with `@disable`. + Params: range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) @@ -877,7 +880,8 @@ See_Also: $(LREF uninitializedFill) */ void initializeAll(Range)(Range range) -if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) +if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range + && __traits(compiles, { static ElementType!Range _; })) { import core.stdc.string : memset, memcpy; import std.traits : hasElaborateAssign, isDynamicArray; @@ -1037,6 +1041,18 @@ if (is(Range == char[]) || is(Range == wchar[])) assert(xs[1].x == 3); } +// https://issues.dlang.org/show_bug.cgi?id=22105 +@system unittest +{ + struct NoDefaultCtor + { + @disable this(); + } + + NoDefaultCtor[1] array = void; + static assert(!__traits(compiles, array[].initializeAll)); +} + // move /** Moves `source` into `target`, via a destructive copy when necessary. diff --git a/std/container/array.d b/std/container/array.d index 63dc86403..08f9ead19 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -428,8 +428,6 @@ if (!is(immutable T == immutable bool)) @property void length(size_t newLength) { - import std.algorithm.mutation : initializeAll; - if (length >= newLength) { // shorten @@ -440,10 +438,22 @@ if (!is(immutable T == immutable bool)) _payload = _payload.ptr[0 .. newLength]; return; } - immutable startEmplace = length; - reserve(newLength); - initializeAll(_payload.ptr[startEmplace .. newLength]); - _payload = _payload.ptr[0 .. newLength]; + + static if (__traits(compiles, { static T _; })) + { + import std.algorithm.mutation : initializeAll; + + immutable startEmplace = length; + reserve(newLength); + initializeAll(_payload.ptr[startEmplace .. newLength]); + _payload = _payload.ptr[0 .. newLength]; + } + else + { + assert(0, "Cannot add elements to array because `" ~ + fullyQualifiedName!T ~ ".this()` is annotated with " ~ + "`@disable`."); + } } @property size_t capacity() const @@ -901,12 +911,16 @@ if (!is(immutable T == immutable bool)) /** * Sets the number of elements in the array to `newLength`. If `newLength` * is greater than `length`, the new elements are added to the end of the - * array and initialized with `T.init`. + * array and initialized with `T.init`. If `T` is a `struct` whose default + * constructor is annotated with `@disable`, `newLength` must be lower than + * or equal to `length`. * * Complexity: * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. * If `capacity < newLength` the worst case is $(BIGOH newLength). * + * Precondition: `__traits(compiles, { static T _; }) || newLength <= length` + * * Postcondition: `length == newLength` */ @property void length(size_t newLength) @@ -1708,6 +1722,23 @@ if (!is(immutable T == immutable bool)) assert(equal(a[], [1,2,3,4,5,6,7,8])); } +// https://issues.dlang.org/show_bug.cgi?id=22105 +@system unittest +{ + import core.exception : AssertError; + import std.exception : assertThrown, assertNotThrown; + + struct NoDefaultCtor + { + int i; + @disable this(); + this(int j) { i = j; } + } + + auto array = Array!NoDefaultCtor([NoDefaultCtor(1), NoDefaultCtor(2)]); + assertNotThrown!AssertError(array.length = 1); + assertThrown!AssertError(array.length = 5); +} //////////////////////////////////////////////////////////////////////////////// // Array!bool