Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c90cb26
fix #22386 - Indexed initialization of multi dimensional const array …
rainers Jan 12, 2026
1fe509b
fix #22406 - [REG 2.112] Error: function '_d_aaLen' is not callable u…
rainers Jan 17, 2026
c75c2d2
Fix #22422 - Missing _d_cast lowering for CommaExp (#22424)
kinke Jan 19, 2026
737a9bf
etc.linux.memoryerror: Adapt to dynamic SIGSTKSZ since glibc v2.34 (#…
kinke Jan 30, 2026
e6902e3
Bump OSX runner image to 14 (#21985)
rikkimax Oct 21, 2025
cfa1806
Bump Windows 2019 to 2022 for Azure pipelines (#22000)
rikkimax Oct 25, 2025
477cb99
redirect output of gc/printf test to file to avoid pollution of CI lo…
rainers Oct 24, 2025
215c967
allow dmd test run arguments to be grouped with quotes so they don't …
rainers Nov 4, 2025
f6e7604
AA: don't require TypeInfo if compiling without it (#22137)
rainers Nov 24, 2025
903a7af
fix #22480 - Regression in DMD v2.112, betterC can not work with CTFE…
rainers Feb 1, 2026
e9edba9
fix #22510 - dup of inout aa broke with 2.112
rainers Feb 5, 2026
e5ca2f1
fix #21976: [REG 2.112.0] Array comparison performance regression
rainers Feb 6, 2026
0226084
Revert "fix #21976: [REG 2.112.0] Array comparison performance regres…
rainers Feb 7, 2026
b01f9a6
Fix #22544 - Cannot index AA in `with` block (#22547)
kinke Feb 9, 2026
883c2f6
buildAAIndexRValueX(): Avoid IdentifierExp, prefer direct VarExp (#22…
kinke Feb 11, 2026
76c59d8
[stable] Fix #22543 - Cannot index AA literal in global-variable init…
kinke Feb 13, 2026
3666dcf
Fix #22501 - GC allocation for CTFE-available static array (#22571)
kinke Feb 14, 2026
f7bf401
avoid spurious Windows CI test failure in druntime/test/shared (#22052)
rainers Nov 1, 2025
69a04d3
Serialize compilation of dllgc test (#22162)
limepoutine Dec 2, 2025
f2c0a8c
Fix issue #22535 - [REG 2.112] Unicode symbols inside q (#22539)
rikkimax Feb 20, 2026
26895c7
Fix #22594 - TypeInfo_Class.m_flags wrong wrt. noPointers flag when t…
kinke Feb 20, 2026
75e04f4
Fix LONG instead of LRESULT return in winuser.d (#22583)
dkorpel Feb 20, 2026
df6465e
Fix #22625 - undefined identifier 'addr' in core.sys.posix.netinet.in…
dkorpel Feb 24, 2026
546af4a
Fix Issue #22567 - alias this and nested AAs cause compiler SIGILL (#…
divyansharma001 Feb 25, 2026
40555fc
fix issue #22556 - shared AAs with RefCounted values don't work even …
rainers Feb 26, 2026
6da2d9e
Fix Issue 22614 - [Windows] tmpnam() in runPreprocessor generates roo…
divyansharma001 Mar 2, 2026
6bb9fd7
bump VERSION to v2.112.1-rc.1
ibuclaw Mar 3, 2026
623d9c4
Fix the fstatat declaration on macOS/x64. (#22689)
s-ludwig Mar 3, 2026
7af5ba3
bump VERSION to v2.112.1
ibuclaw Mar 20, 2026
4c5e50c
Merge remote-tracking branch 'upstream/stable' into merge_stable
ibuclaw Mar 20, 2026
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
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.112.0
v2.112.1
7 changes: 7 additions & 0 deletions compiler/src/dmd/common/charactertables.d
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ bool c_isalnum(const int c)
( c >= 'A' && c <= 'Z'));
}

///
bool isAlphaASCII(const dchar c)
{
return (( c >= 'a' && c <= 'z') ||
( c >= 'A' && c <= 'Z'));
}

extern(D) private:

// originally from dmd.root.utf
Expand Down
58 changes: 42 additions & 16 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -6992,8 +6992,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor

if (!global.params.useGC && sc.needsCodegen())
{
error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-%s`", exp.toErrMsg(), SwitchVariadic.ptr);
return setError();
if (sc.func)
{
sc.func.skipCodegen = true; // same net result as calling checkGC
goto LskipNewArrayLowering; // not checked in sc.needsCodegen() !?
}
}

if (!sc.needsCodegen())
Expand Down Expand Up @@ -8516,7 +8519,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
exp.type = tf.next;
auto casted_exp = exp.castTo(sc, t);
if (auto cex = casted_exp.isCastExp())
if (auto cex = lastComma(casted_exp).isCastExp())
{
lowerCastExp(cex, sc);
}
Expand Down Expand Up @@ -10933,7 +10936,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}

if (auto cex = ex.isCastExp())
if (auto cex = lastComma(ex).isCastExp())
{
lowerCastExp(cex, sc);
}
Expand Down Expand Up @@ -15079,12 +15082,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}

// When array comparison is not lowered to `__equals`, `memcmp` is used, but
// GC checks occur before the expression is lowered to `memcmp` in e2ir.d.
// Thus, we will consider the literal arrays as on-stack arrays to avoid issues
// during GC checks.
// Remaining array comparisons are trivially memcmp-able.
// Allocate array-literal operands on the stack, since they don't
// escape during the comparison; enabling @nogc for these.
if (isArrayComparison)
{
exp.e1 = exp.e1.optimize(WANTvalue);
exp.e2 = exp.e2.optimize(WANTvalue);

if (auto ale1 = exp.e1.isArrayLiteralExp())
{
ale1.onstack = true;
Expand Down Expand Up @@ -19745,12 +19750,12 @@ private Expression buildAAIndexRValueX(Type t, Expression eaa, Expression ekey,
auto call = new CallExp(loc, func, arguments);
e0 = Expression.combine(e0, call);

if (arrayBoundsCheck(sc.func))
if (sc.func && arrayBoundsCheck(sc.func))
{
// __aaget = _d_aaGetRvalueX(aa, key), __aaget ? __aaget : onRangeError(__FILE__, __LINE__)
auto ei = new ExpInitializer(loc, e0);
auto vartmp = Identifier.generateId("__aaget");
auto vardecl = new VarDeclaration(loc, null, vartmp, ei, STC.exptemp);
auto id = Identifier.generateId("__aaget");
auto vardecl = new VarDeclaration(loc, null, id, ei, STC.exptemp);
auto declexp = new DeclarationExp(loc, vardecl);

//Expression idrange = new IdentifierExp(loc, Identifier.idPool("_d_arraybounds"));
Expand All @@ -19760,9 +19765,9 @@ private Expression buildAAIndexRValueX(Type t, Expression eaa, Expression ekey,
auto locargs = new Expressions(new FileInitExp(loc, EXP.file), new LineInitExp(loc));
auto ex = new CallExp(loc, idrange, locargs);

auto idvar1 = new IdentifierExp(loc, vartmp);
auto idvar2 = new IdentifierExp(loc, vartmp);
auto cond = new CondExp(loc, idvar1, idvar2, ex);
auto ve1 = new VarExp(loc, vardecl);
auto ve2 = new VarExp(loc, vardecl);
auto cond = new CondExp(loc, ve1, ve2, ex);
auto comma = new CommaExp(loc, declexp, cond);
return comma;
}
Expand Down Expand Up @@ -19795,6 +19800,26 @@ Expression revertIndexAssignToRvalues(IndexExp ie, Scope* sc)
return lowerAAIndexRead(ie, sc);
}

// Ditto, but traverses DotVarExp from `alias this` rewrites.
private Expression revertModifiableAAIndexReads(Expression e, Scope* sc)
{
// Recurse through dot-accesses (alias this produces DotVarExp on an inner IndexExp)
if (auto dve = e.isDotVarExp())
{
dve.e1 = revertModifiableAAIndexReads(dve.e1, sc);
return e;
}
if (auto ie = e.isIndexExp())
{
// Recurse first to handle deeper nesting
ie.e1 = revertModifiableAAIndexReads(ie.e1, sc);
// Lower a modifiable AA IndexExp to an rvalue read
if (ie.modifiable && ie.e1.type.isTypeAArray())
return lowerAAIndexRead(ie, sc);
}
return e;
}

// helper for rewriteAAIndexAssign
private Expression implicitConvertToStruct(Expression ev, StructDeclaration sd, Scope* sc)
{
Expand Down Expand Up @@ -19845,6 +19870,7 @@ private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] alias
// find the AA of multi dimensional access
for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp())
eaa = ieaa.e1;
eaa = revertModifiableAAIndexReads(eaa, sc);
eaa = extractSideEffect(sc, "__aatmp", e0, eaa);
// collect all keys of multi dimensional access
Expressions ekeys;
Expand Down Expand Up @@ -19883,7 +19909,7 @@ private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] alias
auto tiargs = new Objects(taa.index, taa.next);
func = new DotTemplateInstanceExp(loc, func, hook, tiargs);

auto arguments = new Expressions(eaa, ekeys[i-1], new IdentifierExp(loc, idfound));
auto arguments = new Expressions(eaa, ekeys[i-1], new VarExp(loc, varfound));
eaa = new CallExp(loc, func, arguments);
if (i > 1)
{
Expand Down Expand Up @@ -19940,7 +19966,7 @@ private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] alias
ex = new CastExp(ex.loc, ex, Type.tvoid);
ey = new CastExp(ey.loc, ey, Type.tvoid);
}
Expression condfound = new IdentifierExp(loc, idfound);
Expression condfound = new VarExp(loc, varfound);
ex = new CondExp(loc, condfound, ex, ey);
ex = Expression.combine(e0, ex);
ex.isCommaExp().originalExp = exp;
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -7979,6 +7979,8 @@ extern bool c_isxdigit(const int32_t c);

extern bool c_isalnum(const int32_t c);

extern bool isAlphaASCII(const char32_t c);

extern void error(Loc loc, const char* format, ...);

extern void error(const char* filename, uint32_t linnum, uint32_t charnum, const char* format, ...);
Expand Down
14 changes: 5 additions & 9 deletions compiler/src/dmd/glue/toobj.d
Original file line number Diff line number Diff line change
Expand Up @@ -1348,17 +1348,13 @@ private void ClassInfoToDt(ref DtBuilder dtb, ClassDeclaration cd, Symbol* sinit
Louter:
for (ClassDeclaration pc = cd; pc; pc = pc.baseClass)
{
if (pc.members)
foreach (vd; pc.fields)
{
for (size_t i = 0; i < pc.members.length; i++)
//printf("vd = %s %s\n", vd.kind(), vd.toChars());
if (vd.hasPointers())
{
Dsymbol sm = (*pc.members)[i];
//printf("sm = %s %s\n", sm.kind(), sm.toChars());
if (sm.hasPointers())
{
flags &= ~ClassFlags.noPointers; // not no-how, not no-way
break Louter;
}
flags &= ~ClassFlags.noPointers; // not no-how, not no-way
break Louter;
}
}
}
Expand Down
18 changes: 17 additions & 1 deletion compiler/src/dmd/initsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,22 @@ Expression initializerToExpression(Initializer init, Type itype = null, const bo
}
}

// enforce the element type only if the dimensions match, otherwise the value
// might be used as an initializer for the whole array at another dimension
Type isTypeArray(Type tn)
{
auto ty = tn ? tn.ty : Tnone;
return ty == Tarray || ty == Tsarray || ty == Taarray || ty == Tvector ? tn : null;
}
Type tnext = isTypeArray(itype);
auto initn = init;
while (tnext && initn)
{
tnext = isTypeArray(tnext.nextOf());
initn = initn.value.length ? initn.value[0].isArrayInitializer() : null;
}
Type telem = itype && !tnext && !initn ? itype.nextOf() : null;

auto elements = new Expressions(edim);
elements.zero();
size_t j = 0;
Expand All @@ -1478,7 +1494,7 @@ Expression initializerToExpression(Initializer init, Type itype = null, const bo
assert(j < edim);
if (Initializer iz = init.value[i])
{
if (Expression ex = iz.initializerToExpression(null, isCfile))
if (Expression ex = iz.initializerToExpression(telem, isCfile))
{
(*elements)[j] = ex;
++j;
Expand Down
24 changes: 14 additions & 10 deletions compiler/src/dmd/lexer.d
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ struct CompileEnv
*/
class Lexer
{
private __gshared OutBuffer stringbuffer;
private __gshared
{
OutBuffer stringbuffer;
OutBuffer stringbuffersecondary; // functions that use stringbuffer can call scan that needs this.
}

BaseLoc* baseLoc; // Used to generate `scanloc`, which is just an index into this data structure
Loc scanloc; // for error messages
Expand Down Expand Up @@ -1431,7 +1435,7 @@ class Lexer
p++;
break;
default:
if (isalpha(*p) || (p != idstart && isdigit(*p)))
if (isAlphaASCII(*p) || (p != idstart && isdigit(*p)))
continue;
error(loc, "unterminated named entity &%.*s;", cast(int)(p - idstart + 1), idstart);
c = '?';
Expand Down Expand Up @@ -1766,7 +1770,7 @@ class Lexer
uint blankrol = 0;
uint startline = 0;
p++;
stringbuffer.setsize(0);
stringbuffersecondary.setsize(0);
while (1)
{
const s = p;
Expand All @@ -1785,7 +1789,7 @@ class Lexer
}
if (hereid)
{
stringbuffer.writeUTF8(c);
stringbuffersecondary.writeUTF8(c);
continue;
}
break;
Expand Down Expand Up @@ -1825,7 +1829,7 @@ class Lexer
delimright = ']';
else if (c == '<')
delimright = '>';
else if (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c)))
else if (isAlphaASCII(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c)))
{
// Start of identifier; must be a heredoc
Token tok;
Expand Down Expand Up @@ -1875,7 +1879,7 @@ class Lexer
goto Ldone;

// we're looking for a new identifier token
if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) && hereid)
if (startline && (isAlphaASCII(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) && hereid)
{
Token tok;
auto psave = p;
Expand All @@ -1890,7 +1894,7 @@ class Lexer
}
p = psave;
}
stringbuffer.writeUTF8(c);
stringbuffersecondary.writeUTF8(c);
startline = 0;
}
}
Expand All @@ -1903,7 +1907,7 @@ class Lexer
error("delimited string must end in `\"`");
else
error(token.loc, "delimited string must end in `%c\"`", delimright);
result.setString(stringbuffer);
result.setString(stringbuffersecondary);
stringPostfix(result);
}

Expand Down Expand Up @@ -2404,7 +2408,7 @@ class Lexer
case '.':
if (p[1] == '.')
goto Ldone; // if ".."
if (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80)
if (isAlphaASCII(p[1]) || p[1] == '_' || p[1] & 0x80)
{
if (Ccompile && (p[1] == 'f' || p[1] == 'F' || p[1] == 'l' || p[1] == 'L'))
goto Lreal; // if `0.f` or `0.L`
Expand Down Expand Up @@ -2477,7 +2481,7 @@ class Lexer
case '.':
if (p[1] == '.')
goto Ldone; // if ".."
if (base <= 10 && n > 0 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
if (base <= 10 && n > 0 && (isAlphaASCII(p[1]) || p[1] == '_' || p[1] & 0x80))
{
if (Ccompile && base == 10 &&
(p[1] == 'e' || p[1] == 'E' || p[1] == 'f' || p[1] == 'F' || p[1] == 'l' || p[1] == 'L'))
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/dmd/link.d
Original file line number Diff line number Diff line change
Expand Up @@ -962,9 +962,13 @@ public int runPreprocessor(Loc loc, const(char)[] cpp, const(char)[] filename, c
version (Windows)
{
// generate unique temporary file name for preprocessed output
const(char)* tmpname = tmpnam(null);
assert(tmpname);
const(char)[] ifilename = tmpname[0 .. strlen(tmpname) + 1];
char[MAX_PATH] tempDir = void;
char[MAX_PATH] tempFile = void;
if (GetTempPathA(MAX_PATH, tempDir.ptr) == 0)
return STATUS_FAILED;
if (GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr) == 0)
return STATUS_FAILED;
const(char)[] ifilename = tempFile[0 .. strlen(tempFile.ptr) + 1];
ifilename = xarraydup(ifilename);
const(char)[] output = ifilename;

Expand Down
13 changes: 13 additions & 0 deletions compiler/test/compilable/imports/test22480b.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module immports.test22480b;

auto parseAA()
{
bool[string] aa;
aa["key"] = true;
assert("key" in aa);
assert(aa["key"]);
assert(aa.length == 1);
assert(aa == aa);
aa.rehash();
return true;
}
9 changes: 9 additions & 0 deletions compiler/test/compilable/test22480.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import imports.test22480b;

@nogc nothrow:
enum a = parseAA();

extern(C) int main()
{
return 0;
}
12 changes: 12 additions & 0 deletions compiler/test/compilable/test22501.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// https://github.com/dlang/dmd/issues/22501

struct A {
ubyte[16] bytes;

enum something = A(cast(ubyte[16])[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);

@nogc nothrow pure
bool isB() const {
return bytes[0..12] == something.bytes[0..12];
}
}
11 changes: 11 additions & 0 deletions compiler/test/compilable/test22543.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// https://github.com/dlang/dmd/issues/22543

enum int[string] aa = [
"a": 42,
];

int i = aa["a"];

void foo() {
static int j = aa["a"];
}
11 changes: 11 additions & 0 deletions compiler/test/compilable/test22544.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// https://github.com/dlang/dmd/issues/22544

struct S {
int bar() { return 1; }
}

void foo(string key) {
int[string] aa;
with (S())
aa[key] = bar();
}
11 changes: 11 additions & 0 deletions compiler/test/compilable/test24295.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// REQUIRED_ARGS: -betterC

int f()
{
int[] overlaps = new int[1];
overlaps[0] = 3;
return overlaps[0];
}

enum res_f = f();
static assert(res_f == 3);
Loading
Loading