From d3ddb0822bd516e2e2c38eb26f17cad815109df4 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 3 Jun 2026 11:01:06 +0200 Subject: [PATCH 1/7] Run test suite with druntime, not Phobos The dmd test suite is supposed to be independent of Phobos. Consolidate ^^ related tests, and only link Phobos if a test has RUNNABLE_PHOBOS_TEST or COMPILABLE_MATH_TEST set. Co-Authored-By: Claude Opus 4.8 --- compiler/test/compilable/b19294.d | 1 + compiler/test/compilable/compile1.d | 5 +- compiler/test/compilable/ddoc24871.d | 4 +- .../test/compilable/extra-files/json2.json | 5 +- compiler/test/compilable/paranoia_ctfe.d | 1 + compiler/test/fail_compilation/b3841.d | 9 - compiler/test/fail_compilation/diag10327.d | 5 +- compiler/test/fail_compilation/fail18938.d | 1 + compiler/test/fail_compilation/fail21045.d | 5 +- compiler/test/fail_compilation/fail21091a.d | 5 +- compiler/test/fail_compilation/fail21091b.d | 5 +- compiler/test/fail_compilation/ice7782.d | 5 +- compiler/test/fail_compilation/issue11070.d | 3 +- compiler/test/fail_compilation/test22361.d | 5 +- compiler/test/run.d | 65 +++-- compiler/test/runnable/argufilem.d | 3 +- compiler/test/runnable/arrayop.d | 1 - compiler/test/runnable/ctortests.d | 119 +++++++++ compiler/test/runnable/dhry.d | 1 - .../test/runnable/extra-files/importc_main.d | 4 +- .../test/runnable/extra-files/importc_main2.d | 1 - compiler/test/runnable/imports/argufile.d | 14 +- compiler/test/runnable/link11069b.d | 1 + compiler/test/runnable/paranoia.d | 1 + .../test/runnable/{ctorpowtests.d => pow.d} | 247 ++++++++++-------- compiler/test/runnable/test13117.d | 1 + compiler/test/runnable/test13117b.d | 1 + compiler/test/runnable/test18076.sh | 2 +- compiler/test/runnable/test19.d | 4 +- compiler/test/runnable/test5943.d | 77 ------ compiler/test/runnable/test8544.d | 8 +- compiler/test/runnable/test9287.sh | 2 +- compiler/test/runnable/testabi.d | 2 +- compiler/test/runnable/xtest46.d | 28 -- compiler/test/tools/d_do_test.d | 23 +- 35 files changed, 368 insertions(+), 296 deletions(-) create mode 100644 compiler/test/runnable/ctortests.d rename compiler/test/runnable/{ctorpowtests.d => pow.d} (58%) delete mode 100644 compiler/test/runnable/test5943.d diff --git a/compiler/test/compilable/b19294.d b/compiler/test/compilable/b19294.d index ed1a71747e40..69a425a13d3c 100644 --- a/compiler/test/compilable/b19294.d +++ b/compiler/test/compilable/b19294.d @@ -1,3 +1,4 @@ +// COMPILABLE_MATH_TEST alias MT = MyStruct!int; struct MyStruct(T) diff --git a/compiler/test/compilable/compile1.d b/compiler/test/compilable/compile1.d index f0b6dffe3622..85caa8088582 100644 --- a/compiler/test/compilable/compile1.d +++ b/compiler/test/compilable/compile1.d @@ -1,10 +1,9 @@ -// COMPILABLE_MATH_TEST // PERMUTE_ARGS: // REQUIRED_ARGS: -verrors=simple // EXTRA_FILES: imports/a12506.d /* TEST_OUTPUT: --- -compilable/compile1.d(231): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/compile1.d(230): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead --- */ @@ -872,8 +871,6 @@ alias TT14166(T...) = T; static assert(is(typeof((s14166.x += 1) = 2) == int)); // ok <- error static assert(is(typeof(s14166.a.length += 2) == size_t)); // ok <- error static assert(is(typeof(s14166++) == S14166)); // ok <- error -static assert(is(typeof(s14166.x ^^ 2) == int)); // ok <- error -static assert(is(typeof(s14166.y ^^= 2.5) == double)); // ok <- error static assert(is(typeof(makeAA14166()[0] = 1) == X14166)); // ok <- error static assert(is(typeof(tup14166.field = makeTup14166()) == TT14166!(int, int))); // ok <- error diff --git a/compiler/test/compilable/ddoc24871.d b/compiler/test/compilable/ddoc24871.d index 98bd36dce6eb..97591346e945 100644 --- a/compiler/test/compilable/ddoc24871.d +++ b/compiler/test/compilable/ddoc24871.d @@ -2,7 +2,7 @@ // REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- // POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh // EXTRA_SOURCES: extra-files/ddoc_minimal.ddoc -import std.stdio; +import core.stdc.stdio; /// Example /// --- @@ -14,6 +14,6 @@ import std.stdio; /// --- void main() { - writeln("Hello, World!"); + printf("Hello, World!\n"); } diff --git a/compiler/test/compilable/extra-files/json2.json b/compiler/test/compilable/extra-files/json2.json index e4ee50e71427..53fd87bf7ae9 100644 --- a/compiler/test/compilable/extra-files/json2.json +++ b/compiler/test/compilable/extra-files/json2.json @@ -8,8 +8,7 @@ "defFile": null, "importPaths": [ "compilable", - "../../druntime/import", - "../../../phobos" + "../../druntime/import" ], "libName": "VALUE_REMOVED_FOR_TEST", "libraryFiles": [ @@ -89,4 +88,4 @@ } ] } -} +} \ No newline at end of file diff --git a/compiler/test/compilable/paranoia_ctfe.d b/compiler/test/compilable/paranoia_ctfe.d index e48a6d1f4fd6..ed72263ec7de 100644 --- a/compiler/test/compilable/paranoia_ctfe.d +++ b/compiler/test/compilable/paranoia_ctfe.d @@ -1,3 +1,4 @@ +// COMPILABLE_MATH_TEST /* EXTRA_SOURCES: ../runnable/extra-files/paranoia.d REQUIRED_ARGS: -o- -version=CTFE diff --git a/compiler/test/fail_compilation/b3841.d b/compiler/test/fail_compilation/b3841.d index bbbb6c96d3c9..f3939d8cab0f 100644 --- a/compiler/test/fail_compilation/b3841.d +++ b/compiler/test/fail_compilation/b3841.d @@ -70,13 +70,4 @@ void main() f!(op, int, float)(); f!(op, long, double)(); } - - // OK - f!("^^=", int, int)(); - f!("^^=", long, int)(); - f!("^^=", long, short)(); - f!("^^=", float, long)(); - f!("^^=", double, float)(); - // Should that really be OK ? - f!("^^=", float, double)(); } diff --git a/compiler/test/fail_compilation/diag10327.d b/compiler/test/fail_compilation/diag10327.d index 1366882edeb8..18de6dbb1fde 100644 --- a/compiler/test/fail_compilation/diag10327.d +++ b/compiler/test/fail_compilation/diag10327.d @@ -1,11 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10327.d(12): Error: unable to read module `test10327` -fail_compilation/diag10327.d(12): Expected 'imports/test10327.d' or 'imports/test10327/package.d' in one of the following import paths: +fail_compilation/diag10327.d(11): Error: unable to read module `test10327` +fail_compilation/diag10327.d(11): Expected 'imports/test10327.d' or 'imports/test10327/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ --- */ diff --git a/compiler/test/fail_compilation/fail18938.d b/compiler/test/fail_compilation/fail18938.d index f7ece6daf809..59dd2b514305 100644 --- a/compiler/test/fail_compilation/fail18938.d +++ b/compiler/test/fail_compilation/fail18938.d @@ -1,3 +1,4 @@ +// RUNNABLE_PHOBOS_TEST // REQUIRED_ARGS: -c -Ifail_compilation/imports/ // EXTRA_SOURCES: imports/test18938a/cache.d imports/test18938a/file.d // EXTRA_FILES: imports/test18938b/file.d diff --git a/compiler/test/fail_compilation/fail21045.d b/compiler/test/fail_compilation/fail21045.d index c43eda3f9729..0625780e3cba 100644 --- a/compiler/test/fail_compilation/fail21045.d +++ b/compiler/test/fail_compilation/fail21045.d @@ -1,11 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail21045.d(12): Error: unable to read module `__stdin` -fail_compilation/fail21045.d(12): Expected '__stdin.d' or '__stdin/package.d' in one of the following import paths: +fail_compilation/fail21045.d(11): Error: unable to read module `__stdin` +fail_compilation/fail21045.d(11): Expected '__stdin.d' or '__stdin/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ --- */ diff --git a/compiler/test/fail_compilation/fail21091a.d b/compiler/test/fail_compilation/fail21091a.d index c2bbe4d59e4d..5eebf872a3bc 100644 --- a/compiler/test/fail_compilation/fail21091a.d +++ b/compiler/test/fail_compilation/fail21091a.d @@ -3,11 +3,10 @@ /* TEST_OUTPUT: ---- -fail_compilation/fail21091a.d(16): Error: unable to read module `Ternary` -fail_compilation/fail21091a.d(16): Expected 'Ternary.d' or 'Ternary/package.d' in one of the following import paths: +fail_compilation/fail21091a.d(15): Error: unable to read module `Ternary` +fail_compilation/fail21091a.d(15): Expected 'Ternary.d' or 'Ternary/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ ---- */ diff --git a/compiler/test/fail_compilation/fail21091b.d b/compiler/test/fail_compilation/fail21091b.d index 3d7d6001bd2a..1bef94359894 100644 --- a/compiler/test/fail_compilation/fail21091b.d +++ b/compiler/test/fail_compilation/fail21091b.d @@ -3,11 +3,10 @@ /* TEST_OUTPUT: ---- -fail_compilation/fail21091b.d(16): Error: unable to read module `Tid` -fail_compilation/fail21091b.d(16): Expected 'Tid.d' or 'Tid/package.d' in one of the following import paths: +fail_compilation/fail21091b.d(15): Error: unable to read module `Tid` +fail_compilation/fail21091b.d(15): Expected 'Tid.d' or 'Tid/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ ---- */ diff --git a/compiler/test/fail_compilation/ice7782.d b/compiler/test/fail_compilation/ice7782.d index 551933b382d2..9fccbf821867 100644 --- a/compiler/test/fail_compilation/ice7782.d +++ b/compiler/test/fail_compilation/ice7782.d @@ -2,11 +2,10 @@ EXTRA_FILES: imports/ice7782algorithm.d imports/ice7782range.d TEST_OUTPUT: ---- -fail_compilation/ice7782.d(14): Error: unable to read module `ice7782math` -fail_compilation/ice7782.d(14): Expected 'imports/ice7782range/imports/ice7782math.d' or 'imports/ice7782range/imports/ice7782math/package.d' in one of the following import paths: +fail_compilation/ice7782.d(13): Error: unable to read module `ice7782math` +fail_compilation/ice7782.d(13): Expected 'imports/ice7782range/imports/ice7782math.d' or 'imports/ice7782range/imports/ice7782math/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ ---- */ diff --git a/compiler/test/fail_compilation/issue11070.d b/compiler/test/fail_compilation/issue11070.d index 07d69ee87ab5..fbba387d686c 100644 --- a/compiler/test/fail_compilation/issue11070.d +++ b/compiler/test/fail_compilation/issue11070.d @@ -1,14 +1,13 @@ /* TEST_OUTPUT: --- -fail_compilation/issue11070.d(16): Error: undefined identifier `x` +fail_compilation/issue11070.d(15): Error: undefined identifier `x` --- */ int get() { return 1; } void test() { - import std.stdio : writeln; switch (auto x = get()) { default: auto z = x; diff --git a/compiler/test/fail_compilation/test22361.d b/compiler/test/fail_compilation/test22361.d index 11255ff1f1b0..e375a29e648c 100644 --- a/compiler/test/fail_compilation/test22361.d +++ b/compiler/test/fail_compilation/test22361.d @@ -1,11 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/test22361.d(11): Error: unable to read module `this_module_does_not_exist` -fail_compilation/test22361.d(11): Expected 'this_module_does_not_exist.d' or 'this_module_does_not_exist/package.d' in one of the following import paths: +fail_compilation/test22361.d(10): Error: unable to read module `this_module_does_not_exist` +fail_compilation/test22361.d(10): Expected 'this_module_does_not_exist.d' or 'this_module_does_not_exist/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ -import path[2] = $p:phobos$ --- */ import this_module_does_not_exist; diff --git a/compiler/test/run.d b/compiler/test/run.d index 9ac44ffec768..f59fa7b3b3f8 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -145,6 +145,7 @@ Options: } verifyCompilerExists(env); + ensureDruntime(env); prepareOutputDirectory(env); if (runUnitTests) @@ -227,6 +228,30 @@ void verifyCompilerExists(const string[string] env) } } +/** +Builds (or rebuilds) the druntime library that the tests link against. + +Prevent test failures due to forgetting to make druntime manually. +*/ +void ensureDruntime(const string[string] env) +{ + const make = environment.get("MAKE", "make"); + const druntimeDir = environment.get("DRUNTIME_PATH", testPath(buildPath("..", "..", "druntime"))); + const buildCommand = [ + make, "-C", druntimeDir, + "MODEL=" ~ env["MODEL"], + "BUILD=" ~ env["BUILD"], + ]; + + writefln("Building druntime: %-(%s %)", buildCommand); + stdout.flush(); + if (spawnProcess(buildCommand, env, Config.none, scriptDir).wait) + { + stderr.writeln("failed to build druntime"); + quitSilently(1); + } +} + /// Creates the necessary directories and files for the test runner(s) void prepareOutputDirectory(const string[string] env) { @@ -286,29 +311,20 @@ void ensureToolsExists(const string[string] env, const TestTool[] tools ...) auto lastModifiedBin = targetBin.timeLastModified.ifThrown(SysTime.init); if (lastModifiedBin >= sourceFile.timeLastModified) { - auto lastModifiedDmd = env["DMD"].timeLastModified.ifThrown(SysTime.init); - if (!tool.linksWithTests || lastModifiedBin >= lastModifiedDmd) - { - log("%s is already up-to-date", tool); - continue; - } + log("%s is already up-to-date", tool); + continue; } string[] buildCommand; - bool overrideEnv; if (tool.linksWithTests) { - // This will compile the dshell library thus needs the actual - // DMD compiler under test buildCommand = [ - env["DMD"], - "-conf=", + hostDMD, "-m"~env["MODEL"], "-of" ~ targetBin, "-c", sourceFile ] ~ getPicFlags(env); - overrideEnv = true; } else { @@ -322,7 +338,7 @@ void ensureToolsExists(const string[string] env, const TestTool[] tools ...) writefln("Executing: %-(%s %)", buildCommand); stdout.flush(); - if (spawnProcess(buildCommand, overrideEnv ? env : null).wait) + if (spawnProcess(buildCommand, null).wait) { stderr.writefln("failed to build '%s'", targetBin); atomicOp!"+="(failCount, 1); @@ -551,20 +567,25 @@ string[string] getEnvironment() env["BUILD"] = build; env["EXE"] = exeExtension; env["DMD"] = dmdPath; + env["HOST_DMD"] = hostDMD; env.setDefault("DMD_TEST_COVERAGE", "0"); const generatedSuffix = "generated/%s/%s/%s".format(os, build, model); + const druntimePath = environment.get("DRUNTIME_PATH", projectRootDir.buildPath("druntime")); + const druntimeLibDir = generatedDir.buildPath(os, build, model); + + const phobosPath = environment.get("PHOBOS_PATH", projectRootDir.dirName.buildPath("phobos")); + version(Windows) { env.setDefault("ARGS", "-inline -release -g -O"); env["OBJ"] = ".obj"; env["DSEP"] = `\\`; env["SEP"] = `\`; - auto druntimePath = environment.get("DRUNTIME_PATH", testPath(`..\..\druntime`)); - auto phobosPath = environment.get("PHOBOS_PATH", testPath(`..\..\..\phobos`)); - env["DFLAGS"] = `-I"%s\import" -I"%s"`.format(druntimePath, phobosPath); - env["LIB"] = phobosPath ~ ";" ~ environment.get("LIB"); + env["DFLAGS"] = `-I"%s\import" -defaultlib=druntime -debuglib=druntime`.format(druntimePath); + env["LIB"] = druntimeLibDir ~ ";" ~ environment.get("LIB"); + env["PHOBOS_DFLAGS"] = `-I"%s\import" -I"%s"`.format(druntimePath, phobosPath); } else { @@ -572,8 +593,6 @@ string[string] getEnvironment() env["OBJ"] = ".o"; env["DSEP"] = "/"; env["SEP"] = "/"; - auto druntimePath = environment.get("DRUNTIME_PATH", testPath(`../../druntime`)); - auto phobosPath = environment.get("PHOBOS_PATH", testPath(`../../../phobos`)); // default to PIC, use PIC=1/0 to en-/disable PIC. // Note that shared libraries and C files are always compiled with PIC. @@ -582,11 +601,13 @@ string[string] getEnvironment() pic = false; env["PIC_FLAG"] = pic ? "-fPIC" : ""; - env["DFLAGS"] = "-I%s/import -I%s".format(druntimePath, phobosPath) - ~ " -L-L%s/%s".format(phobosPath, generatedSuffix); + env["DFLAGS"] = "-I%s/import -defaultlib=druntime -debuglib=druntime".format(druntimePath) + ~ " -L-L%s".format(druntimeLibDir); + + env["PHOBOS_DFLAGS"] = "-I%s/import -I%s -L-L%s/%s".format(druntimePath, phobosPath, phobosPath, generatedSuffix); bool isShared = environment.get("SHARED") != "0" && os.among("linux", "freebsd", "hurd") > 0; if (isShared) - env["DFLAGS"] = env["DFLAGS"] ~ " -defaultlib=libphobos2.so -L-rpath=%s/%s".format(phobosPath, generatedSuffix); + env["PHOBOS_DFLAGS"] = env["PHOBOS_DFLAGS"] ~ " -defaultlib=libphobos2.so -L-rpath=%s/%s".format(phobosPath, generatedSuffix); if (pic) env["REQUIRED_ARGS"] = environment.get("REQUIRED_ARGS") ~ " " ~ env["PIC_FLAG"]; diff --git a/compiler/test/runnable/argufilem.d b/compiler/test/runnable/argufilem.d index f4636365c6ca..a2c015ec3162 100644 --- a/compiler/test/runnable/argufilem.d +++ b/compiler/test/runnable/argufilem.d @@ -15,12 +15,13 @@ why is 8 scared of 7? because789 // main.d ------------------------------------------------------- import argufile; +import core.stdc.stdio : printf; int main(string[] args) { string message = arguments("bob is ", 7, " years old"); - writefln(message); + printf("%.*s\n", cast(int) message.length, message.ptr); argufile.useargs(); // will crash here diff --git a/compiler/test/runnable/arrayop.d b/compiler/test/runnable/arrayop.d index 4c0836a96dc8..11bf910846a5 100644 --- a/compiler/test/runnable/arrayop.d +++ b/compiler/test/runnable/arrayop.d @@ -1,5 +1,4 @@ // RUNNABLE_PHOBOS_TEST -import std.math; extern(C) int printf(const char*, ...); diff --git a/compiler/test/runnable/ctortests.d b/compiler/test/runnable/ctortests.d new file mode 100644 index 000000000000..74f0608dec5f --- /dev/null +++ b/compiler/test/runnable/ctortests.d @@ -0,0 +1,119 @@ +// PERMUTE_ARGS: +// +// CTFE constructor / copy-constructor tests. +// The `^^` (pow) parts that used to live here are in runnable/pow.d. + +int magicVariable() +{ + if (__ctfe) + return 3; + + shared int var = 2; + return var; +} + +static assert(magicVariable()==3); + +// https://issues.dlang.org/show_bug.cgi?id=3535 +struct StructWithCtor +{ + this(int _n) { + n = _n; x = 5; + } + this(int _n, float _x) { + n = _n; x = _x; + } + int n; + float x; +} + +enum A = StructWithCtor(1); +enum B = StructWithCtor(7, 2.3); + +static assert(A.n == 1); +static assert(A.x == 5.0); +static assert(B.n == 7); +static assert(B.x == 2.3); + +// Test copy constructors +struct CopyTest { + double x; + this(double a) { x = a * 10.0;} + this(this) { x+=2.0;} +} + +struct CopyTest2 +{ + int x; int x1; int x2; int x3; + this(int a) { x = a * 2; x1 = 3;} + this(this) { x1+=17;} +} + + +const CopyTest z = CopyTest(5.3); +/+ +// TODO: This is not yet supported. But it +// generates an error message instead of wrong-code. +const CopyTest w = z; +static assert(z.x==55.0); ++/ + +int copytest1() +{ + CopyTest z = CopyTest(3.4); + CopyTest w = z; + assert(w.x == 36.0); + CopyTest2 q = CopyTest2(7); + CopyTest2 q2 = q; + CopyTest2 q3 = q2; + assert(q3.x1 == 37); + + return 123; +} +static assert(copytest1()==123); + +// This must not cause a segfault +alias int FILTH; +struct Filth +{ + struct Impl + { + FILTH * handle = null; + this(FILTH* h, uint r, string n) + { + handle = h; + } + } + Impl * p; + + this(string name, in char[] stdioOpenmode = "rb") + { + } + + ~this() + { + if (!p) return; + } + + this(this) + { + if (!p) return; + } + } + struct InputByChar + { + private Filth _f; + + this(Filth f) + { + _f = f; + } +} + +/************************************/ + +void main() +{ + assert(!__ctfe); + assert(magicVariable()==2); +} diff --git a/compiler/test/runnable/dhry.d b/compiler/test/runnable/dhry.d index 598feda290d6..3c5a8cb3fad4 100644 --- a/compiler/test/runnable/dhry.d +++ b/compiler/test/runnable/dhry.d @@ -343,7 +343,6 @@ Deprecation caused by https://issues.dlang.org/show_bug.cgi?id=20645 import core.stdc.stdio; import core.stdc.string; import core.stdc.stdlib; -import std.string; /* Compiler and system dependent definitions: */ diff --git a/compiler/test/runnable/extra-files/importc_main.d b/compiler/test/runnable/extra-files/importc_main.d index 2b36d3b09aba..ef1d18cad4c1 100644 --- a/compiler/test/runnable/extra-files/importc_main.d +++ b/compiler/test/runnable/extra-files/importc_main.d @@ -1,11 +1,11 @@ -import std.stdio; +import core.stdc.stdio; import importc_test; int main() { auto rc = someCodeInC(3, 4); - writeln("Result of someCodeInC(3,4) = ", rc ); + printf("Result of someCodeInC(3,4) = %d\n", rc); assert( rc == 7, "Wrong result"); return 0; } diff --git a/compiler/test/runnable/extra-files/importc_main2.d b/compiler/test/runnable/extra-files/importc_main2.d index 598d0f936d8b..f534840d7e3c 100644 --- a/compiler/test/runnable/extra-files/importc_main2.d +++ b/compiler/test/runnable/extra-files/importc_main2.d @@ -1,5 +1,4 @@ -import std.stdio; import importc_test; int main() diff --git a/compiler/test/runnable/imports/argufile.d b/compiler/test/runnable/imports/argufile.d index 47a047494b47..fa397e8b5c32 100644 --- a/compiler/test/runnable/imports/argufile.d +++ b/compiler/test/runnable/imports/argufile.d @@ -3,17 +3,16 @@ public: import core.vararg; -import std.stdio; -import std.utf; +import core.stdc.stdio : printf; -dstring formatstring(TypeInfo[] arguments, va_list argptr) +string formatstring(TypeInfo[] arguments, va_list argptr) { - dstring message = null; + string message = null; void putc(dchar c) { - message ~= c; + message ~= cast(char) c; } @@ -25,15 +24,14 @@ dstring formatstring(TypeInfo[] arguments, va_list argptr) string arguments(...) // turns a bunch of arguments into a formatted char[] string { - return std.utf.toUTF8(formatstring(_arguments, _argptr)); + return formatstring(_arguments, _argptr); } void useargs(...) { string crashage = arguments("why is 8 scared of 7? because", 7,8,9); - //printf("%.*s\n", crashage); - writefln(crashage); + printf("%.*s\n", cast(int) crashage.length, crashage.ptr); } enum Mangle : char diff --git a/compiler/test/runnable/link11069b.d b/compiler/test/runnable/link11069b.d index 33992d177b37..b734d93439b0 100644 --- a/compiler/test/runnable/link11069b.d +++ b/compiler/test/runnable/link11069b.d @@ -1,3 +1,4 @@ +// RUNNABLE_PHOBOS_TEST // COMPILE_SEPARATELY: // EXTRA_SOURCES: imports/link11069x.d // EXTRA_SOURCES: imports/link11069y.d diff --git a/compiler/test/runnable/paranoia.d b/compiler/test/runnable/paranoia.d index 24d26546ba0a..7466616eabeb 100644 --- a/compiler/test/runnable/paranoia.d +++ b/compiler/test/runnable/paranoia.d @@ -1,3 +1,4 @@ +// RUNNABLE_PHOBOS_TEST /* EXTRA_SOURCES: extra-files/paranoia.d PERMUTE_ARGS: diff --git a/compiler/test/runnable/ctorpowtests.d b/compiler/test/runnable/pow.d similarity index 58% rename from compiler/test/runnable/ctorpowtests.d rename to compiler/test/runnable/pow.d index 1df39babc812..f002e3e0a457 100644 --- a/compiler/test/runnable/ctorpowtests.d +++ b/compiler/test/runnable/pow.d @@ -1,18 +1,89 @@ +// Consolidated tests for the `^^` (pow) operator. +// +// `^^` lowers to `object._d_pow`, which still imports `std.math` internally +// (see the TODO in druntime's object.d), so Phobos is needed on the link line +// until that hook is moved into druntime. // RUNNABLE_PHOBOS_TEST // PERMUTE_ARGS: -int magicVariable() +// runtime `^^` with a runtime exponent + +__gshared uint x0 = 0; +__gshared uint x1 = 1; +__gshared uint x2 = 2; +__gshared uint x3 = 3; +__gshared uint x4 = 4; +__gshared uint x5 = 5; +__gshared uint x6 = 6; +__gshared uint x7 = 7; +__gshared uint x10 = 10; +__gshared uint x15 = 15; +__gshared uint x31 = 31; +__gshared uint x32 = 32; + +void test5943() { - if (__ctfe) - return 3; - - shared int var = 2; - return var; + assert(2 ^^ x0 == 1); + assert(2 ^^ x1 == 2); + assert(2 ^^ x31 == 0x80000000); + assert(4 ^^ x0 == 1); + assert(4 ^^ x1 == 4); + assert(4 ^^ x15 == 0x40000000); + assert(8 ^^ x0 == 1); + assert(8 ^^ x1 == 8); + assert(8 ^^ x10 == 0x40000000); + assert(16 ^^ x0 == 1); + assert(16 ^^ x1 == 16); + assert(16 ^^ x7 == 0x10000000); + assert(32 ^^ x0 == 1); + assert(32 ^^ x1 == 32); + assert(32 ^^ x6 == 0x40000000); + assert(64 ^^ x0 == 1); + assert(64 ^^ x1 == 64); + assert(64 ^^ x5 == 0x40000000); + assert(128 ^^ x0 == 1); + assert(128 ^^ x1 == 128); + assert(128 ^^ x4 == 0x10000000); + assert(256 ^^ x0 == 1); + assert(256 ^^ x1 == 256); + assert(256 ^^ x3 == 0x1000000); + assert(512 ^^ x0 == 1); + assert(512 ^^ x1 == 512); + assert(512 ^^ x3 == 0x8000000); + assert(1024 ^^ x0 == 1); + assert(1024 ^^ x1 == 1024); + assert(1024 ^^ x3 == 0x40000000); + assert(2048 ^^ x0 == 1); + assert(2048 ^^ x1 == 2048); + assert(2048 ^^ x2 == 0x400000); + assert(4096 ^^ x0 == 1); + assert(4096 ^^ x1 == 4096); + assert(4096 ^^ x2 == 0x1000000); + assert(8192 ^^ x0 == 1); + assert(8192 ^^ x1 == 8192); + assert(8192 ^^ x2 == 0x4000000); + assert(16384 ^^ x0 == 1); + assert(16384 ^^ x1 == 16384); + assert(16384 ^^ x2 == 0x10000000); + assert(32768 ^^ x0 == 1); + assert(32768 ^^ x1 == 32768); + assert(32768 ^^ x2 == 0x40000000); + assert(65536 ^^ x0 == 1); + assert(65536 ^^ x1 == 65536); + assert(131072 ^^ x0 == 1); + assert(131072 ^^ x1 == 131072); + assert(262144 ^^ x0 == 1); + assert(262144 ^^ x1 == 262144); + assert(524288 ^^ x0 == 1); + assert(524288 ^^ x1 == 524288); + assert(1048576 ^^ x0 == 1); + assert(1048576 ^^ x1 == 1048576); + assert(2097152 ^^ x0 == 1); + assert(2097152 ^^ x1 == 2097152); + assert(4194304 ^^ x0 == 1); + assert(4194304 ^^ x1 == 4194304); } -static assert(magicVariable()==3); - -/************************************/ // https://issues.dlang.org/show_bug.cgi?id=11159 void test11159() @@ -27,13 +98,7 @@ void test11159() assert(e_10_pow_20 == pow(10uL, 20)); } -// https://issues.dlang.org/show_bug.cgi?id=991 -- invalid. -// https://issues.dlang.org/show_bug.cgi?id=3500 -- is this related to 2127? - -// Tests for ^^ -// TODO: These tests should not require import std.math. - -import std.math; +// CTFE / typing rules for `^^` // Test float ^^ int static assert( 27.0 ^^ 5 == 27.0 * 27.0 * 27.0 * 27.0 * 27.0); static assert( 2.0 ^^ 3 == 8.0); @@ -153,14 +218,6 @@ int containsAsm() return 0; } -enum A = StructWithCtor(1); -enum B = StructWithCtor(7, 2.3); - -static assert(A.n == 1); -static assert(A.x == 5.0); -static assert(B.n == 7); -static assert(B.x == 2.3); - int bazra(int x) { StructWithCtor p = StructWithCtor(4); @@ -177,82 +234,6 @@ void moreCommaTests() for (int i=0; i< k^^2; i+=StructWithCtor(1).n) {} } -// Test copy constructors -struct CopyTest { - double x; - this(double a) { x = a * 10.0;} - this(this) { x+=2.0;} -} - -struct CopyTest2 -{ - int x; int x1; int x2; int x3; - this(int a) { x = a * 2; x1 = 3;} - this(this) { x1+=17;} -} - - -const CopyTest z = CopyTest(5.3); -/+ -// TODO: This is not yet supported. But it -// generates an error message instead of wrong-code. -const CopyTest w = z; -static assert(z.x==55.0); -+/ - -int copytest1() -{ - CopyTest z = CopyTest(3.4); - CopyTest w = z; - assert(w.x == 36.0); - CopyTest2 q = CopyTest2(7); - CopyTest2 q2 = q; - CopyTest2 q3 = q2; - assert(q3.x1 == 37); - - return 123; -} -static assert(copytest1()==123); - -// This must not cause a segfault -alias int FILTH; -struct Filth -{ - struct Impl - { - FILTH * handle = null; - this(FILTH* h, uint r, string n) - { - handle = h; - } - } - Impl * p; - - this(string name, in char[] stdioOpenmode = "rb") - { - } - - ~this() - { - if (!p) return; - } - - this(this) - { - if (!p) return; - } - } - struct InputByChar - { - private Filth _f; - - this(Filth f) - { - _f = f; - } -} - - static int nastyForCtfe=4; // Can't use a global variable @@ -264,11 +245,8 @@ int anotherPowTest() return x^^4 > 2.0 ? 3: 2; } -/************************************/ - // https://github.com/dlang/dmd/issues/19075 // ^^= must compile for small integer types - void test19075() { byte bw = 10; @@ -288,12 +266,71 @@ void test19075() assert(usw == cast(ushort) 1_000_000); } +/************************************/ +// https://issues.dlang.org/show_bug.cgi?id=3841 +// `^^=` must compile for the type combinations below + +void powAssign(LHS, RHS)() +{ + LHS a; + RHS b; + a ^^= b; +} + +void testPowAssign() +{ + powAssign!(int, int)(); + powAssign!(long, int)(); + powAssign!(long, short)(); + powAssign!(float, long)(); + powAssign!(double, float)(); + powAssign!(float, double)(); +} + +// https://issues.dlang.org/show_bug.cgi?id=4465 +void bug4465() +{ + const a = 2 ^^ 2; + int b = a; +} + +// https://issues.dlang.org/show_bug.cgi?id=6228 +void test6228() +{ + int val; + const(int)* ptr = &val; + const(int) temp; + auto x = (*ptr) ^^ temp; +} + +// https://issues.dlang.org/show_bug.cgi?id=10682 +void text10682() +{ + ulong x = 1; + ulong y = 2 ^^ x; +} + +// https://issues.dlang.org/show_bug.cgi?id=14166 +struct S14166 +{ + int x; + double y; +} +S14166 s14166; + +static assert(is(typeof(s14166.x ^^ 2) == int)); +static assert(is(typeof(s14166.y ^^= 2.5) == double)); + /************************************/ void main() { - assert(!__ctfe); - assert(magicVariable()==2); + test5943(); test11159(); test19075(); + testPowAssign(); + moreCommaTests(); + bug4465(); + test6228(); + text10682(); } diff --git a/compiler/test/runnable/test13117.d b/compiler/test/runnable/test13117.d index 06563b8d1226..65c99b17d733 100644 --- a/compiler/test/runnable/test13117.d +++ b/compiler/test/runnable/test13117.d @@ -1,4 +1,5 @@ // PERMUTE_ARGS: -O -release -g +// RUNNABLE_PHOBOS_TEST import std.file, std.stdio; int main() diff --git a/compiler/test/runnable/test13117b.d b/compiler/test/runnable/test13117b.d index d14bd595c339..dd59f29523f6 100644 --- a/compiler/test/runnable/test13117b.d +++ b/compiler/test/runnable/test13117b.d @@ -1,5 +1,6 @@ // REQUIRED_ARGS: -inline // PERMUTE_ARGS: -O -release -g +// RUNNABLE_PHOBOS_TEST import std.file, std.stdio; int main() diff --git a/compiler/test/runnable/test18076.sh b/compiler/test/runnable/test18076.sh index f4622e5658fb..a8cf27aa9a0f 100755 --- a/compiler/test/runnable/test18076.sh +++ b/compiler/test/runnable/test18076.sh @@ -2,7 +2,7 @@ output_file=${OUTPUT_BASE}.log -echo 'import std.stdio; void main() { writeln("Success"); }' | \ +echo 'import core.stdc.stdio; void main() { puts("Success"); }' | \ $DMD -m${MODEL} -run - > ${output_file} grep -q 'Success' ${output_file} diff --git a/compiler/test/runnable/test19.d b/compiler/test/runnable/test19.d index eda6172abc04..d0d4db1d8188 100644 --- a/compiler/test/runnable/test19.d +++ b/compiler/test/runnable/test19.d @@ -1,7 +1,5 @@ // REQUIRED_ARGS: -unittest -import std.algorithm: cmp; - extern(C) int printf(const char*, ...); /* ================================ */ @@ -269,7 +267,7 @@ void test13() s1 = s1.dup; s2 = tolower13(s1); - assert(cmp(s2, "fol") == 0); + assert(s2 == "fol"); assert(s2 == s1); } diff --git a/compiler/test/runnable/test5943.d b/compiler/test/runnable/test5943.d deleted file mode 100644 index 0d6619872d78..000000000000 --- a/compiler/test/runnable/test5943.d +++ /dev/null @@ -1,77 +0,0 @@ -// test that the import of std.math is not needed - -__gshared uint x0 = 0; -__gshared uint x1 = 1; -__gshared uint x2 = 2; -__gshared uint x3 = 3; -__gshared uint x4 = 4; -__gshared uint x5 = 5; -__gshared uint x6 = 6; -__gshared uint x7 = 7; -__gshared uint x10 = 10; -__gshared uint x15 = 15; -__gshared uint x31 = 31; -__gshared uint x32 = 32; - -void main() -{ - assert(2 ^^ x0 == 1); - assert(2 ^^ x1 == 2); - assert(2 ^^ x31 == 0x80000000); - assert(4 ^^ x0 == 1); - assert(4 ^^ x1 == 4); - assert(4 ^^ x15 == 0x40000000); - assert(8 ^^ x0 == 1); - assert(8 ^^ x1 == 8); - assert(8 ^^ x10 == 0x40000000); - assert(16 ^^ x0 == 1); - assert(16 ^^ x1 == 16); - assert(16 ^^ x7 == 0x10000000); - assert(32 ^^ x0 == 1); - assert(32 ^^ x1 == 32); - assert(32 ^^ x6 == 0x40000000); - assert(64 ^^ x0 == 1); - assert(64 ^^ x1 == 64); - assert(64 ^^ x5 == 0x40000000); - assert(128 ^^ x0 == 1); - assert(128 ^^ x1 == 128); - assert(128 ^^ x4 == 0x10000000); - assert(256 ^^ x0 == 1); - assert(256 ^^ x1 == 256); - assert(256 ^^ x3 == 0x1000000); - assert(512 ^^ x0 == 1); - assert(512 ^^ x1 == 512); - assert(512 ^^ x3 == 0x8000000); - assert(1024 ^^ x0 == 1); - assert(1024 ^^ x1 == 1024); - assert(1024 ^^ x3 == 0x40000000); - assert(2048 ^^ x0 == 1); - assert(2048 ^^ x1 == 2048); - assert(2048 ^^ x2 == 0x400000); - assert(4096 ^^ x0 == 1); - assert(4096 ^^ x1 == 4096); - assert(4096 ^^ x2 == 0x1000000); - assert(8192 ^^ x0 == 1); - assert(8192 ^^ x1 == 8192); - assert(8192 ^^ x2 == 0x4000000); - assert(16384 ^^ x0 == 1); - assert(16384 ^^ x1 == 16384); - assert(16384 ^^ x2 == 0x10000000); - assert(32768 ^^ x0 == 1); - assert(32768 ^^ x1 == 32768); - assert(32768 ^^ x2 == 0x40000000); - assert(65536 ^^ x0 == 1); - assert(65536 ^^ x1 == 65536); - assert(131072 ^^ x0 == 1); - assert(131072 ^^ x1 == 131072); - assert(262144 ^^ x0 == 1); - assert(262144 ^^ x1 == 262144); - assert(524288 ^^ x0 == 1); - assert(524288 ^^ x1 == 524288); - assert(1048576 ^^ x0 == 1); - assert(1048576 ^^ x1 == 1048576); - assert(2097152 ^^ x0 == 1); - assert(2097152 ^^ x1 == 2097152); - assert(4194304 ^^ x0 == 1); - assert(4194304 ^^ x1 == 4194304); -} diff --git a/compiler/test/runnable/test8544.d b/compiler/test/runnable/test8544.d index 0b8b86bd99fc..6d92270e834a 100644 --- a/compiler/test/runnable/test8544.d +++ b/compiler/test/runnable/test8544.d @@ -1,7 +1,6 @@ // EXECUTE_ARGS: foo bar doo // PERMUTE_ARGS: -import core.stdc.stdio; -import std.conv; +import core.stdc.string : strlen; import core.runtime; void main(string[] args) @@ -12,6 +11,7 @@ void main(string[] args) assert(dArgs.length && cArgs.argc); // ensure we've passed some args assert(dArgs.length == cArgs.argc); - assert(dArgs[1] == to!string(cArgs.argv[1])); - assert(args[1] == to!string(cArgs.argv[1])); + const cArg = cArgs.argv[1][0 .. strlen(cArgs.argv[1])]; + assert(dArgs[1] == cArg); + assert(args[1] == cArg); } diff --git a/compiler/test/runnable/test9287.sh b/compiler/test/runnable/test9287.sh index bcee882c2c35..245690ad0653 100755 --- a/compiler/test/runnable/test9287.sh +++ b/compiler/test/runnable/test9287.sh @@ -2,7 +2,7 @@ set -e -echo 'import std.stdio; void main() { writeln("Success"); }' | \ +echo 'import core.stdc.stdio; void main() { puts("Success"); }' | \ $DMD -m${MODEL} -of${OUTPUT_BASE}${EXE} - ${OUTPUT_BASE}${EXE} diff --git a/compiler/test/runnable/testabi.d b/compiler/test/runnable/testabi.d index d84cf4f499d6..9f014af259f9 100644 --- a/compiler/test/runnable/testabi.d +++ b/compiler/test/runnable/testabi.d @@ -782,7 +782,7 @@ ulong mask(ulong n) { if ( n == ulong.sizeof ) // may wrap return ~0UL; - return 2^^( n*8 ) - 1; + return (1UL << (n*8)) - 1; } /************************************************************************/ diff --git a/compiler/test/runnable/xtest46.d b/compiler/test/runnable/xtest46.d index 5893560a6226..af0ed3c48b06 100644 --- a/compiler/test/runnable/xtest46.d +++ b/compiler/test/runnable/xtest46.d @@ -888,14 +888,6 @@ void test45() /***************************************************/ -void text10682() -{ - ulong x = 1; - ulong y = 2 ^^ x; -} - -/***************************************************/ - struct Test46 { int foo; @@ -2453,14 +2445,6 @@ void test118() {} /***************************************************/ -void bug4465() -{ - const a = 2 ^^ 2; - int b = a; -} - -/***************************************************/ - pure void foo(int *p) { *p = 3; @@ -4231,17 +4215,6 @@ void test1962() assert(C.classinfo.create() is null); } -/***************************************************/ -// https://issues.dlang.org/show_bug.cgi?id=6228 - -void test6228() -{ - int val; - const(int)* ptr = &val; - const(int) temp; - auto x = (*ptr) ^^ temp; -} - /***************************************************/ int test7544() @@ -8253,7 +8226,6 @@ int main() test1471(); test6335(); test1687(); - test6228(); test3733(); test4392(); test7942(); diff --git a/compiler/test/tools/d_do_test.d b/compiler/test/tools/d_do_test.d index 9a17a4359a69..655df062ef07 100755 --- a/compiler/test/tools/d_do_test.d +++ b/compiler/test/tools/d_do_test.d @@ -93,6 +93,7 @@ struct TestArgs bool compileSeparately; /// `COMPILE_SEPARATELY`: compile each source file separately bool link; /// `LINK`: force linking for `fail_compilation` & `compilable` tests bool clearDflags; /// `DFLAGS`: whether DFLAGS should be cleared before invoking dmd + bool requirePhobos; /// `RUNNABLE_PHOBOS_TEST`/`COMPILABLE_MATH_TEST`: test genuinely needs Phobos, so use it instead of druntime-only string executeArgs; /// `EXECUTE_ARGS`: arguments passed to the compiled executable (for `runnable[_cxx]`) string cxxflags; /// `CXXFLAGS`: arguments passed to $CXX when compiling `EXTRA_CPP_SOURCES` string[] sources; /// `EXTRA_SOURCES`: additional D sources (+ main source file) @@ -124,6 +125,8 @@ struct EnvData { string all_args; /// `ARGS`: arguments to test in permutations string dmd; /// `DMD`: compiler under test + string hostDmd; /// `HOST_DMD`: host compiler, used to build test-harness code (e.g. dshell scripts) + string phobosDflags; /// `PHOBOS_DFLAGS`: Phobos-based `DFLAGS` for dshell tests and `RUNNABLE_PHOBOS_TEST` opt-in tests string results_dir; /// `RESULTS_DIR`: directory for temporary files string sep; /// `SEP`: directory separator (`/` or `\`) string dsep; /// `DSEP`: double directory separator ( `/` or `\\`) @@ -171,6 +174,8 @@ immutable(EnvData) processEnvironment() envData.exe = envGetRequired ("EXE"); envData.os = environment.get("OS"); envData.dmd = replace(envGetRequired("DMD"), "/", envData.sep); + envData.hostDmd = replace(environment.get("HOST_DMD", "dmd"), "/", envData.sep); + envData.phobosDflags = environment.get("PHOBOS_DFLAGS", ""); envData.compiler = "dmd"; //should be replaced for other compilers envData.ccompiler = environment.get("CC"); envData.cxxcompiler = environment.get("CXX"); @@ -658,6 +663,10 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_ testArgs.clearDflags = findTestParameter(envData, file, "DFLAGS", dflagsStr); enforce(dflagsStr.empty, "The DFLAGS test argument must be empty: It is '" ~ dflagsStr ~ "'"); + // Tests are compiled/linked against druntime only; the rare test for which + // Phobos is essential opts in with one of these markers and gets Phobos. + testArgs.requirePhobos = file.canFind("RUNNABLE_PHOBOS_TEST") || file.canFind("COMPILABLE_MATH_TEST"); + findTestParameter(envData, file, "REQUIRED_ARGS", testArgs.requiredArgs); if (envData.required_args.length) { @@ -1722,6 +1731,8 @@ int tryMain(string[] args) // Clear the DFLAGS environment variable if it was specified in the test file if (testArgs.clearDflags) environment["DFLAGS"] = ""; + else if (testArgs.requirePhobos) + environment["DFLAGS"] = envData.phobosDflags; writef(" ... %-30s %s%s(%s)", input_file, @@ -2344,11 +2355,15 @@ static this() auto outfile = File(output_file, "w"); enum keepFilesOpen = Config.retainStdout | Config.retainStderr; + // dshell scripts are test-harness code that should not be driven by the + // compiler under test, but by a stable host compiler. + environment["DFLAGS"] = envData.phobosDflags; + // // compile the test // { - const compile = [envData.dmd, "-conf=", "-m"~envData.model] ~ + const compile = [envData.hostDmd, "-m"~envData.model] ~ envData.picFlag ~ [ "-od" ~ testOutDir, "-of" ~ testScriptExe, @@ -2362,9 +2377,13 @@ static this() ]; outfile.writeln("[COMPILE_TEST] ", escapeShellCommand(compile)); outfile.flush(); + // Build with the host compiler's own configuration: clear DFLAGS so the + // druntime-only flags (and the Phobos PHOBOS_DFLAGS) don't leak in here. + auto compileEnv = environment.toAA(); + compileEnv.remove("DFLAGS"); // Note that spawnprocess closes the file, so it will need to be re-opened // below when we run the test - auto compileProc = std.process.spawnProcess(compile, stdin, outfile, outfile, null, keepFilesOpen); + auto compileProc = std.process.spawnProcess(compile, stdin, outfile, outfile, compileEnv, keepFilesOpen); const exitCode = wait(compileProc); if (exitCode != 0) { From 390d6b1a2f7195fa86ad5142690c6b89f04eeae1 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 3 Jun 2026 12:58:12 +0200 Subject: [PATCH 2/7] test6716.d: link druntime instead of phobos --- compiler/test/runnable_cxx/test6716.d | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/test/runnable_cxx/test6716.d b/compiler/test/runnable_cxx/test6716.d index 6aeb25d6dcca..4cf6df8cadb0 100644 --- a/compiler/test/runnable_cxx/test6716.d +++ b/compiler/test/runnable_cxx/test6716.d @@ -4,13 +4,7 @@ version(Windows) { // without main, there is no implicit reference to the runtime library // other platforms pass the runtime library on the linker command line - version(CRuntime_Microsoft) - version(Win64) - pragma(lib, "phobos64"); - else - pragma(lib, "phobos32mscoff"); - else - pragma(lib, "phobos"); + pragma(lib, "druntime"); } extern(C++) int test6716(int magic) From 2a563b1e6f913e3676f4c7c83c58dda4148c2e09 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 3 Jun 2026 13:15:01 +0200 Subject: [PATCH 3/7] Pass absolute HOST_DMD path --- ci/run.sh | 2 +- compiler/test/run.d | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ci/run.sh b/ci/run.sh index 77b2d88bc948..2956e64817f1 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -128,7 +128,7 @@ test_dmd() { fi $build_path/dmd -g -i -Icompiler/test -release compiler/test/run.d -ofgenerated/run - generated/run -j$N --environment MODEL=$MODEL HOST_DMD=$build_path/dmd "${args[@]}" + generated/run -j$N --environment MODEL=$MODEL HOST_DMD="$PWD/$build_path/dmd" "${args[@]}" } # build and run druntime unit tests diff --git a/compiler/test/run.d b/compiler/test/run.d index f59fa7b3b3f8..c66657e40cda 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -128,6 +128,9 @@ Options: } // allow overwrites from the environment + // HOST_DMD must be either a command on PATH (e.g. `dmd`, `ldmd2`) or an + // absolute path; it is used to build test-harness code (dshell scripts, + // tools) which runs from various working directories. hostDMD = environment.get("HOST_DMD", "dmd"); unitTestRunnerCommand = resultsDir.buildPath("unit_test_runner").exeName; @@ -584,7 +587,7 @@ string[string] getEnvironment() env["DSEP"] = `\\`; env["SEP"] = `\`; env["DFLAGS"] = `-I"%s\import" -defaultlib=druntime -debuglib=druntime`.format(druntimePath); - env["LIB"] = druntimeLibDir ~ ";" ~ environment.get("LIB"); + env["LIB"] = druntimeLibDir ~ ";" ~ phobosPath ~ ";" ~ environment.get("LIB"); env["PHOBOS_DFLAGS"] = `-I"%s\import" -I"%s"`.format(druntimePath, phobosPath); } else From 7821338c8a38f4116e7fe9ee38375b63cf22fae7 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 3 Jun 2026 17:09:36 +0200 Subject: [PATCH 4/7] test runner: map BUILD config to a valid druntime build flavor VisualD passes the dmd build configuration (e.g. Debug, RelWithAsserts) as BUILD, but the druntime/phobos makefiles only accept debug or release. ensureDruntime then failed with "Unrecognized BUILD=Debug". Normalize the runtime library flavor independently from the compiler configuration. Co-Authored-By: Claude Opus 4.8 --- compiler/test/run.d | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/test/run.d b/compiler/test/run.d index c66657e40cda..b743e0831a86 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -231,6 +231,20 @@ void verifyCompilerExists(const string[string] env) } } +/** +Maps the test runner's `BUILD` to a runtime library build flavor. + +`BUILD` may be a dmd build configuration that only describes the compiler +binary (e.g. VisualD passes `Debug` or `RelWithAsserts`), whereas the +druntime/phobos makefiles only accept `debug` or `release`. The runtime +library flavor is independent from the compiler's configuration, so anything +that isn't explicitly a debug build maps to `release`. +*/ +string runtimeBuild(string build) +{ + return build.toLower == "debug" ? "debug" : "release"; +} + /** Builds (or rebuilds) the druntime library that the tests link against. @@ -243,7 +257,7 @@ void ensureDruntime(const string[string] env) const buildCommand = [ make, "-C", druntimeDir, "MODEL=" ~ env["MODEL"], - "BUILD=" ~ env["BUILD"], + "BUILD=" ~ runtimeBuild(env["BUILD"]), ]; writefln("Building druntime: %-(%s %)", buildCommand); @@ -576,7 +590,7 @@ string[string] getEnvironment() const generatedSuffix = "generated/%s/%s/%s".format(os, build, model); const druntimePath = environment.get("DRUNTIME_PATH", projectRootDir.buildPath("druntime")); - const druntimeLibDir = generatedDir.buildPath(os, build, model); + const druntimeLibDir = generatedDir.buildPath(os, runtimeBuild(build), model); const phobosPath = environment.get("PHOBOS_PATH", projectRootDir.dirName.buildPath("phobos")); From 8fd6a10de14ca56c3087f990e0e4f10fe46248c7 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 5 Jun 2026 13:00:36 +0200 Subject: [PATCH 5/7] test runner: pass DMD to druntime build so it isn't rebuilt ensureDruntime invoked the druntime Makefile without DMD=, so its default DMD path (generated////dmd) pointed at a nonexistent binary on the VisualD CI (dmd lives under RelWithAsserts/), triggering a "make -C .. dmd" that failed compiling build.d against absent Phobos. Pass the existing compiler explicitly. Co-Authored-By: Claude Opus 4.8 --- compiler/test/run.d | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/test/run.d b/compiler/test/run.d index b743e0831a86..150cc0a2161b 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -258,6 +258,10 @@ void ensureDruntime(const string[string] env) make, "-C", druntimeDir, "MODEL=" ~ env["MODEL"], "BUILD=" ~ runtimeBuild(env["BUILD"]), + // Pass the existing compiler so the druntime Makefile doesn't try to + // (re)build dmd itself, which fails when the compiler lives in a + // different generated/ subdirectory (e.g. VisualD's RelWithAsserts). + "DMD=" ~ env["DMD"], ]; writefln("Building druntime: %-(%s %)", buildCommand); From 944bfba01d56fa495f82f035802883c7c4bb9b2a Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 12 Jun 2026 11:53:56 +0200 Subject: [PATCH 6/7] test runner: build dshell harness with compiler under test The dshell prebuilt library and per-test driver were built with the host compiler (e.g. LDC 1.23 on the VisualD CI), which is too old to parse the current druntime/Phobos sources (short '=>' function bodies etc.), causing parse errors when their imports were analyzed. They also import Phobos and link against each other, so build both with the compiler under test using PHOBOS_DFLAGS instead, matching the pre-druntime-only behaviour. Also strip the benign LNK4255 'multiple objects of the same name' linker warning: druntime.lib is built as one 'dmd -lib' archive and contains several objects sharing a basename (memory.obj etc.), so the MS linker warns when linking with -g, leaking into tests that expect no output. Co-Authored-By: Claude Opus 4.8 --- compiler/test/run.d | 12 ++++++++++-- compiler/test/tools/d_do_test.d | 21 +++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/test/run.d b/compiler/test/run.d index 150cc0a2161b..8fc386b5e542 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -337,15 +337,23 @@ void ensureToolsExists(const string[string] env, const TestTool[] tools ...) } string[] buildCommand; + string[string] buildEnv = null; if (tool.linksWithTests) { + // The dshell library imports Phobos and is linked against the tests, + // so it must be built by the compiler under test (the host compiler + // may be too old to parse the current druntime/Phobos) using the + // Phobos DFLAGS rather than the druntime-only default. buildCommand = [ - hostDMD, + env["DMD"], + "-conf=", "-m"~env["MODEL"], "-of" ~ targetBin, "-c", sourceFile ] ~ getPicFlags(env); + buildEnv = env.dup; + buildEnv["DFLAGS"] = env.get("PHOBOS_DFLAGS", ""); } else { @@ -359,7 +367,7 @@ void ensureToolsExists(const string[string] env, const TestTool[] tools ...) writefln("Executing: %-(%s %)", buildCommand); stdout.flush(); - if (spawnProcess(buildCommand, null).wait) + if (spawnProcess(buildCommand, buildEnv).wait) { stderr.writefln("failed to build '%s'", targetBin); atomicOp!"+="(failCount, 1); diff --git a/compiler/test/tools/d_do_test.d b/compiler/test/tools/d_do_test.d index 655df062ef07..6ac5a67ce0b5 100755 --- a/compiler/test/tools/d_do_test.d +++ b/compiler/test/tools/d_do_test.d @@ -1845,6 +1845,12 @@ int tryMain(string[] args) compile_output = compile_output.unifyNewLine(); compile_output = std.regex.replaceAll(compile_output, regex(`^DMD v2\.[0-9]+.*\n? DEBUG$`, "m"), ""); + // druntime.lib is built as a single library with `dmd -lib`, so + // it contains several object files with the same basename (e.g. + // memory.obj from core.memory and rt.memory). The MS linker emits + // a benign LNK4255 warning about this when linking with `-g`; + // strip it so it doesn't fail tests that expect no output. + compile_output = std.regex.replaceAll(compile_output, regex(`^.*warning LNK4255:.*\n?`, "m"), ""); compile_output = std.string.strip(compile_output); // replace test_result path with fixed ones compile_output = compile_output.replace(result_path, resultsDirReplacement); @@ -2355,15 +2361,18 @@ static this() auto outfile = File(output_file, "w"); enum keepFilesOpen = Config.retainStdout | Config.retainStderr; - // dshell scripts are test-harness code that should not be driven by the - // compiler under test, but by a stable host compiler. + // dshell scripts import Phobos (std.process etc.) and link against the + // prebuilt dshell library, so build them with the compiler under test and + // the Phobos DFLAGS: the host compiler may be too old to parse the current + // druntime/Phobos sources, and the prebuilt library is built by the + // compiler under test too, so both must agree. environment["DFLAGS"] = envData.phobosDflags; // // compile the test // { - const compile = [envData.hostDmd, "-m"~envData.model] ~ + const compile = [envData.dmd, "-conf=", "-m"~envData.model] ~ envData.picFlag ~ [ "-od" ~ testOutDir, "-of" ~ testScriptExe, @@ -2377,13 +2386,9 @@ static this() ]; outfile.writeln("[COMPILE_TEST] ", escapeShellCommand(compile)); outfile.flush(); - // Build with the host compiler's own configuration: clear DFLAGS so the - // druntime-only flags (and the Phobos PHOBOS_DFLAGS) don't leak in here. - auto compileEnv = environment.toAA(); - compileEnv.remove("DFLAGS"); // Note that spawnprocess closes the file, so it will need to be re-opened // below when we run the test - auto compileProc = std.process.spawnProcess(compile, stdin, outfile, outfile, compileEnv, keepFilesOpen); + auto compileProc = std.process.spawnProcess(compile, stdin, outfile, outfile, null, keepFilesOpen); const exitCode = wait(compileProc); if (exitCode != 0) { From e35773ca1d32296aca67624907e21e98023d84cc Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Sat, 13 Jun 2026 20:27:04 +0200 Subject: [PATCH 7/7] test runner: fix const AA copy for dshell build env `const(string)[string].dup` yields `const(string)[string]`, which doesn't implicitly convert to the mutable `string[string]` spawnProcess wants. Copy the entries into a mutable AA explicitly instead. Co-Authored-By: Claude Opus 4.8 --- compiler/test/run.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/test/run.d b/compiler/test/run.d index 8fc386b5e542..5122fb4e0b40 100755 --- a/compiler/test/run.d +++ b/compiler/test/run.d @@ -352,7 +352,8 @@ void ensureToolsExists(const string[string] env, const TestTool[] tools ...) "-c", sourceFile ] ~ getPicFlags(env); - buildEnv = env.dup; + foreach (k, v; env) + buildEnv[k] = v; buildEnv["DFLAGS"] = env.get("PHOBOS_DFLAGS", ""); } else