Skip to content
5 changes: 3 additions & 2 deletions compiler/include/dmd/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ class ArrayInitializer final : public Initializer
public:
Expressions index; // indices
Initializers value; // of Initializer *'s
unsigned dim; // length of array being initialized
Type *type; // type that array will be used to initialize
d_bool isCarray; // C array semantics
unsigned dim; // length of array being initialized
d_bool isCarray; // C array semantics
d_bool defaultInitialze; // ends with `,...]` meaning "default initialize the rest"

bool isAssociativeArray() const;

Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -6526,8 +6526,10 @@ struct ASTBase
{
Expressions index;
Initializers value;
uint dim;
Type type;
uint dim;
bool isCarray;
bool defaultInitialize;

extern (D) this(Loc loc)
{
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -4129,9 +4129,10 @@ class ArrayInitializer final : public Initializer
public:
Array<Expression* > index;
Array<Initializer* > value;
uint32_t dim;
Type* type;
uint32_t dim;
bool isCarray;
bool defaultInitialize;
bool isAssociativeArray() const;
void accept(Visitor* v) override;
};
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/hdrgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -4265,6 +4265,8 @@ private void initializerToBuffer(Initializer inx, ref OutBuffer buf, ref HdrGenS
if (auto iz = ai.value[i])
initializerToBuffer(iz, buf, hgs);
}
if (ai.defaultInitialize)
buf.put(", ...");
buf.put(']');
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/init.d
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,10 @@ extern (C++) final class ArrayInitializer : Initializer
{
Expressions index; // indices
Initializers value; // of Initializer *'s
uint dim; // length of array being initialized
Type type; // type that array will be used to initialize
uint dim; // length of array being initialized
bool isCarray; // C array semantics
bool defaultInitialize; // ends with `,...]` meaning "default initialize the rest"

extern (D) this(Loc loc)
{
Expand Down
19 changes: 19 additions & 0 deletions compiler/src/dmd/initsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,13 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
}
i.type = t;
length = 0;
bool hasIndices = false;
for (size_t j = 0; j < i.index.length; j++) // don't replace with foreach; j is modified
{
Expression idx = i.index[j];
if (idx)
{
hasIndices = true;
sc = sc.startCTFE();
idx = idx.expressionSemantic(sc);
sc = sc.endCTFE();
Expand Down Expand Up @@ -320,13 +322,30 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
else
{
ulong edim = tsa.dim.toInteger();
if (i.defaultInitialize && hasIndices)
{
error(i.loc, "cannot use both indices and `...` in static array initializer");
return err();
}
if (i.dim < edim && !i.defaultInitialize && !i.isCarray && !hasIndices &&
sc.hasEdition(Edition.v2024))
{
deprecation(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
deprecationSupplemental(i.loc, "use `, ...]` if intentional");
//return err();
}
if (i.dim > edim)
{
error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
return err();
}
}
}
else if (i.defaultInitialize)
{
error(i.loc, "can only use `...` in static array initializer");
return err();
}
if (errors)
return err();

Expand Down
11 changes: 11 additions & 0 deletions compiler/src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -7012,6 +7012,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
*/
private AST.Initializer parseInitializer()
{
//printf("parseInitializer()\n");
const loc = token.loc;

switch (token.value)
Expand Down Expand Up @@ -7086,6 +7087,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
commaExpected = true;
continue;

case TOK.dotDotDot:
if (commaExpected)
{
error("comma expected before `...]`");
}
ia.defaultInitialize = true;
nextToken();
check(TOK.rightBracket);
break;

case TOK.leftCurly:
case TOK.leftBracket:
if (commaExpected)
Expand Down
3 changes: 2 additions & 1 deletion compiler/test/compilable/extra-files/vcg-ast.d.cg
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ template imported()
alias myImport = vcg_ast_import;
enum bool compiles = true;
enum bool isexp = true;
int[3] arr = [1, ...];
R!int
{
struct _R
Expand Down Expand Up @@ -257,4 +258,4 @@ RTInfo!(_R)
{
enum immutable(void)* RTInfo = null;

}
}
2 changes: 2 additions & 0 deletions compiler/test/compilable/vcg-ast.d
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,5 @@ enum isexp = is(typeof({
int[] arr;
arr ~= 1;
}));

int[3] arr = [1, ...];
30 changes: 30 additions & 0 deletions compiler/test/fail_compilation/array1.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
REQUIRED_ARGS: -de
TEST_OUTPUT:
---
fail_compilation/array1.d(22): Deprecation: array initializer has 3 elements, but array length is 4
fail_compilation/array1.d(22): use `, ...]` if intentional
fail_compilation/array1.d(25): Error: can only use `...` in static array initializer
fail_compilation/array1.d(26): Error: cannot use both indices and `...` in static array initializer
fail_compilation/array1.d(29): Deprecation: array initializer has 1 elements, but array length is 4
fail_compilation/array1.d(29): use `, ...]` if intentional
---
*/

module m 2024;

extern (C) immutable int[4] a = [1,2,3,...];
static assert(a[3] == 0);

immutable int[4] b = [1,2,3,...];
static assert(b[3] == 0);

int[4] c = [1,2,3];
int[4] d = [1:1]; // OK

int[] e = [1,...];
int[4] f = [2:1,...];

// nested initializer
int[4][] s1 = [[1]];
int[4][] s2 = [[1,...]]; // OK
3 changes: 2 additions & 1 deletion compiler/test/runnable/b20890.d
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ void format8(string spec, S8[1] s)
assert (spec == "lengthy");
assert (s[0].m == 42);
}
struct S42 { ubyte[42] m = [42]; }
struct S42 { ubyte[42] m = [42, ...]; }
void format42(string spec, S42[1] s)
{
assert (spec == "lengthy");
assert (s[0].m[0] == 42);
assert (s[0].m[1] == 0);
}

void main()
Expand Down
2 changes: 1 addition & 1 deletion druntime/src/core/sys/windows/winsock2.d
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ union in6_addr
}


enum in6_addr IN6ADDR_ANY = { s6_addr8: [0] };
enum in6_addr IN6ADDR_ANY = { s6_addr8: 0 };
enum in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] };
//alias IN6ADDR_ANY_INIT = IN6ADDR_ANY;
//alias IN6ADDR_LOOPBACK_INIT = IN6ADDR_LOOPBACK;
Expand Down
2 changes: 1 addition & 1 deletion druntime/src/core/sys/windows/winver.d
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ VERSIONHELPERAPI IsWindows10OrGreater()

VERSIONHELPERAPI IsWindowsServer()
{
OSVERSIONINFOEXW osvi = { OSVERSIONINFOEXW.sizeof, 0, 0, 0, 0, [0], 0, 0, 0, VER_NT_WORKSTATION };
OSVERSIONINFOEXW osvi = { OSVERSIONINFOEXW.sizeof, 0, 0, 0, 0, 0, 0, 0, 0, VER_NT_WORKSTATION };
const DWORDLONG dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL );

return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
Expand Down
31 changes: 28 additions & 3 deletions spec/arrays.dd
Original file line number Diff line number Diff line change
Expand Up @@ -1140,11 +1140,11 @@ $(H3 $(LNAME2 array-initializers, Array Initializers))

$(GRAMMAR
$(GNAME ArrayInitializer):
$(D [) $(I ArrayElementInitializers)$(OPT) $(D ])
`[` *ArrayElementInitializers* `,`$(OPT) `]`
`[` *ArrayElementInitializers* `, ... ]`

$(GNAME ArrayElementInitializers):
$(I ArrayElementInitializer)
$(I ArrayElementInitializer) $(D ,)
$(I ArrayElementInitializer) $(D ,) $(GSELF ArrayElementInitializers)

$(GNAME ArrayElementInitializer):
Expand All @@ -1153,7 +1153,12 @@ $(GNAME ArrayElementInitializer):
)

$(P An *ArrayInitializer* is a list of element initializers enclosed in `[ ]`.
Each element initializer can be optionally preceded by an index expression and a `:`.
The $(GRAMMAR_INLINE *ArrayElementInitializers* `, ...`) form
$(RELATIVE_LINK2 static-init-static, can only be used) when the declaration
is a static array.)

$(P Each element initializer can be optionally preceded by an index
expression and a `:`.
If an index is not supplied, it is set to the previous index
plus 1, or 0 if it is the first value.
Any missing elements will be initialized to the default value
Expand Down Expand Up @@ -1207,6 +1212,26 @@ assert(value == [5, 6, 2]);

$(H3 $(LNAME2 static-init-static, Static Initialization of Statically Allocated Arrays))

$(P When a static array type is expected, an *ArrayInitializer* must either:)

- Match the expected number of elements in the type
- Have at least one element index provided
- Use the trailing `...` syntax

$(P The `...` syntax is used to specify 0 or more missing elements. Any
missing elements will be initialized to the default value of the element type:)

$(SPEC_RUNNABLE_EXAMPLE_RUN
---------
int[4] a = [1, 2, 3, 4]; // OK

//int[4] b = [1, 2]; // Error, missing elements
int[4] b = [1, 2, ...]; // OK

void main() => assert(b == [1, 2, 0, 0]);
---------
)

$(P All elements of a static array can be initialized to a specific value
which implicitly converts to the array element type:)

Expand Down
Loading