diff --git a/compiler/src/dmd/backend/drtlsym.d b/compiler/src/dmd/backend/drtlsym.d index 50c377f98d04..a65ac7b5de65 100644 --- a/compiler/src/dmd/backend/drtlsym.d +++ b/compiler/src/dmd/backend/drtlsym.d @@ -90,7 +90,7 @@ Symbol* getRtlsym(RTLSYM i) @trusted case RTLSYM.DARRAY_SLICEP: symbolz(ps,FL.func,FREGSAVED,"_d_arraybounds_slicep", SFLexit, t); break; case RTLSYM.DARRAY_INDEXP: symbolz(ps,FL.func,FREGSAVED,"_d_arraybounds_indexp", SFLexit, t); break; case RTLSYM.DNULLP: symbolz(ps,FL.func,FREGSAVED,"_d_nullpointerp", SFLexit, t); break; - case RTLSYM.DINVARIANT: symbolz(ps,FL.func,FREGSAVED,"_D2rt10invariant_12_d_invariantFC6ObjectZv", 0, tsdlib); break; + case RTLSYM.DINVARIANT: symbolz(ps,FL.func,FREGSAVED,"_D2rt10invariant_12_d_invariantFC06ObjectZv", 0, tsdlib); break; case RTLSYM.MEMCMP: symbolz(ps,FL.func,FREGSAVED,"memcmp", 0, t); break; case RTLSYM.MEMCPY: symbolz(ps,FL.func,FREGSAVED,"memcpy", 0, t); break; case RTLSYM.MEMSET8: symbolz(ps,FL.func,FREGSAVED,"memset", 0, t); break; diff --git a/compiler/src/dmd/mangle/package.d b/compiler/src/dmd/mangle/package.d index 8a3f59cae9d6..d5d7617f926d 100644 --- a/compiler/src/dmd/mangle/package.d +++ b/compiler/src/dmd/mangle/package.d @@ -547,6 +547,29 @@ public: assert(0); } + /*********************************************************** + * Returns: `true` if `p` is a non-module, non-package symbol, + * i.e. a struct, class, enum, or other aggregate that could + * collide with a module name in the mangling. + */ + static bool isNonModuleNonPackage(Dsymbol p) + { + if (!p) + return false; + if (p.isModule() || p.isPackage()) + return false; + // Template instances, functions, and other symbols that + // appear as parents in the qualified name are also not + // modules/packages. However, template instances are handled + // separately via mangleTemplateInstance, and functions + // are followed by their type signature (M + TypeFunction), + // so there is no ambiguity for those. Only aggregate types + // (struct, class, enum, union) that appear as plain LNames + // can collide with module names. + return p.isAggregateDeclaration() !is null + || p.isEnumDeclaration() !is null; + } + void mangleParent(Dsymbol s) { //printf("mangleParent() %s %s\n", s.kind(), s.toChars()); @@ -567,7 +590,18 @@ public: } else if (p.getIdent()) { - mangleIdentifier(p.ident, s); + /* Use a leading zero in the length prefix for non-module, + * non-package parent symbols (e.g. structs, classes, enums) + * to disambiguate them from module names. + * For example, struct `bar` is mangled as `03bar` while + * module `bar` stays as `3bar`. Leading zeros don't change + * the numeric value, so existing demanglers handle this + * correctly. + * See: https://github.com/dlang/dmd/issues/22688 + */ + const zeroPad = isNonModuleNonPackage(p); + if (!backref.addRefToIdentifier(*buf, p.ident)) + toBuffer(*buf, p.ident.toString(), s, zeroPad); if (FuncDeclaration f = p.isFuncDeclaration()) mangleFunc(f, true); } @@ -1225,15 +1259,23 @@ void writeBackRef(ref OutBuffer buf, size_t pos) @safe /************************************************************ * Write length prefixed string to buf. + * Params: + * buf = buffer to write to + * id = identifier string to write + * s = symbol for error reporting + * zeroPad = if true, prefix the length with a leading '0' + * to disambiguate non-module symbols from modules */ private -extern (D) void toBuffer(ref OutBuffer buf, const(char)[] id, Dsymbol s) +extern (D) void toBuffer(ref OutBuffer buf, const(char)[] id, Dsymbol s, bool zeroPad = false) { const len = id.length; if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone error(s.loc, "%s `%s` excessive length %llu for symbol, possible recursive expansion?", s.kind, s.toPrettyChars, cast(ulong)(buf.length + len)); else { + if (zeroPad) + buf.writeByte('0'); buf.print(len); buf.writestring(id); } diff --git a/compiler/test/runnable/imports/issue22688/bar.d b/compiler/test/runnable/imports/issue22688/bar.d new file mode 100644 index 000000000000..53f845682260 --- /dev/null +++ b/compiler/test/runnable/imports/issue22688/bar.d @@ -0,0 +1,3 @@ +module issue22688.bar; + +int zen() => 1; diff --git a/compiler/test/runnable/imports/issue22688/package.d b/compiler/test/runnable/imports/issue22688/package.d new file mode 100644 index 000000000000..092407d2cb8c --- /dev/null +++ b/compiler/test/runnable/imports/issue22688/package.d @@ -0,0 +1,28 @@ +module issue22688; + +import issue22688.bar; + +struct bar +{ + static int zen() => 2; +} + +void test22688() +{ + // The module function and the struct function must have + // different mangled names to avoid linker collisions. + // The struct parent component gets a leading zero prefix: + // module bar -> "3bar", struct bar -> "03bar". + static assert(zen.mangleof != bar.zen.mangleof, + "Module and struct functions must have different mangles"); + + // Module-level zen: issue22688.bar.zen (bar is a module) + // Struct-level zen: issue22688.bar.zen (bar is a struct, gets leading zero) + static assert(zen.mangleof == "_D10issue226883bar3zenFZi", + "Module function zen should use normal module mangling"); + static assert(bar.zen.mangleof == "_D10issue2268803bar3zenFZi", + "Struct function zen should use zero-padded struct mangling"); + + assert(zen() == 1); + assert(bar.zen() == 2); +} diff --git a/compiler/test/runnable/issue22688.d b/compiler/test/runnable/issue22688.d new file mode 100644 index 000000000000..7c8ec94483cb --- /dev/null +++ b/compiler/test/runnable/issue22688.d @@ -0,0 +1,10 @@ +// EXTRA_FILES: imports/issue22688/package.d imports/issue22688/bar.d + +module issue22688_test; + +import issue22688; + +void main() +{ + test22688(); +} diff --git a/compiler/test/runnable/mangle.d b/compiler/test/runnable/mangle.d index 76a4adfab7fa..3953de598e7d 100644 --- a/compiler/test/runnable/mangle.d +++ b/compiler/test/runnable/mangle.d @@ -90,7 +90,7 @@ class C2774 { int foo2774() { return 0; } } -static assert(C2774.foo2774.mangleof == "_D6mangle5C27747foo2774MFZi"); +static assert(C2774.foo2774.mangleof == "_D6mangle05C27747foo2774MFZi"); template TFoo2774(T) {} static assert(TFoo2774!int.mangleof == "6mangle"~tl!"15"~"__T8TFoo2774TiZ"); @@ -189,8 +189,8 @@ void test8847b() struct Test8847 { - enum result1 = "S6mangle8Test8847"~tl!("8")~"__T3fooZ"~id!("3foo","Qf")~"MFZ6Result"; - enum result2 = "S6mangle8Test8847"~tl!("8")~"__T3fooZ"~id!("3foo","Qf")~"MxFiZ6Result"; + enum result1 = "S6mangle08Test8847"~tl!("8")~"__T3fooZ"~id!("3foo","Qf")~"MFZ6Result"; + enum result2 = "S6mangle08Test8847"~tl!("8")~"__T3fooZ"~id!("3foo","Qf")~"MxFiZ6Result"; auto foo()() { @@ -304,13 +304,13 @@ auto bar12352() static assert(!__traits(compiles, bar12352.mangleof)); // forward reference to bar static assert(S .mangleof == "S6mangle8bar12352FZ1S"); - static assert(S.func.mangleof == "_D6mangle8bar12352FZ1S4funcMFZv"); + static assert(S.func.mangleof == "_D6mangle8bar12352FZ01S4funcMFZv"); return S(); } static assert( bar12352 .mangleof == "_D6mangle8bar12352FNaNbNiNfZS"~id!("6mangle8bar12352FZ","QBbQxFZ","QL2H")~"1S"); static assert(typeof(bar12352()) .mangleof == "S6mangle8bar12352FZ1S"); -static assert(typeof(bar12352()).func.mangleof == "_D6mangle8bar12352FZ1S4funcMFZv"); +static assert(typeof(bar12352()).func.mangleof == "_D6mangle8bar12352FZ01S4funcMFZv"); auto baz12352() { @@ -318,13 +318,13 @@ auto baz12352() static assert(!__traits(compiles, baz12352.mangleof)); // forward reference to baz static assert(C .mangleof == "C6mangle8baz12352FZ1C"); - static assert(C.func.mangleof == "_D6mangle8baz12352FZ1C4funcMFZv"); + static assert(C.func.mangleof == "_D6mangle8baz12352FZ01C4funcMFZv"); return new C(); } static assert( baz12352 .mangleof == "_D6mangle8baz12352FNaNbNfZC"~id!("6mangle8baz12352FZ","QzQuFZ","QL2F")~"1C"); static assert(typeof(baz12352()) .mangleof == "C6mangle8baz12352FZ1C"); -static assert(typeof(baz12352()).func.mangleof == "_D6mangle8baz12352FZ1C4funcMFZv"); +static assert(typeof(baz12352()).func.mangleof == "_D6mangle8baz12352FZ01C4funcMFZv"); /*******************************************/ // https://issues.dlang.org/show_bug.cgi?id=9525 @@ -376,21 +376,21 @@ class C10249 mixin Func10249!long; mixin Func10249!string; static assert(Seq10249!(.func10249)[0].mangleof == "6mangle9func10249"); // <- 9func10249 - static assert(Seq10249!( func10249)[0].mangleof == "6mangle6C102499func10249"); // <- 9func10249 + static assert(Seq10249!( func10249)[0].mangleof == "6mangle06C102499func10249"); // <- 9func10249 static: // necessary to make overloaded symbols accessible via __traits(getOverloads, C10249) void foo(long) {} void foo(string) {} - static assert(Seq10249!(foo)[0].mangleof == "6mangle6C102493foo"); // <- _D6mangle6C102493fooFlZv - static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[0].mangleof == "_D6mangle6C102493fooFlZv"); // <- - static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[1].mangleof == "_D6mangle6C102493fooFAyaZv"); // <- + static assert(Seq10249!(foo)[0].mangleof == "6mangle06C102493foo"); // <- _D6mangle06C102493fooFlZv + static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[0].mangleof == "_D6mangle06C102493fooFlZv"); // <- + static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[1].mangleof == "_D6mangle06C102493fooFAyaZv"); // <- void g(string) {} alias bar = .f10249; alias bar = g; - static assert(Seq10249!(bar)[0].mangleof == "6mangle6C102493bar"); // <- _D6mangle1fFlZv + static assert(Seq10249!(bar)[0].mangleof == "6mangle06C102493bar"); // <- _D6mangle06C102491fFlZv static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[0].mangleof == "_D6mangle6f10249FlZv"); // <- - static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[1].mangleof == "_D6mangle6C102491gFAyaZv"); // <- + static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[1].mangleof == "_D6mangle06C102491gFAyaZv"); // <- } /*******************************************/ @@ -582,7 +582,7 @@ class CC } } -static assert(CC.member.mangleof == "_D6mangle2CC6memberMFNlZPi"); +static assert(CC.member.mangleof == "_D6mangle02CC6memberMFNlZPi"); /***************************************************/ diff --git a/druntime/src/core/demangle.d b/druntime/src/core/demangle.d index e7efc8c3ec9a..e9bed7d82709 100644 --- a/druntime/src/core/demangle.d +++ b/druntime/src/core/demangle.d @@ -2571,15 +2571,15 @@ private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] = assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi"); static if (hasTypeBackRef) { - assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi"); - assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi"); + assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object06Object8opEqualsFCQsZi"); + assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object06Object8opEqualsFCQsQdZi"); } else { auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals"); - assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi"); + assert(mngl == "_D6object06Object8opEqualsFC6ObjectZi"); auto remngl = reencodeMangled(mngl); - assert(remngl == "_D6object6Object8opEqualsFCQsZi"); + assert(remngl == "_D6object06Object8opEqualsFCQsZi"); } // trigger back tracking with ambiguity on '__T', template or identifier assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); @@ -2653,7 +2653,7 @@ else ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ], ["_D4test3fooAa", "char[] test.foo"], ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"], - ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"], + ["_D6object06Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"], ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"], ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"], //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""], @@ -2700,6 +2700,11 @@ else ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"], ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"], ["_D3foo7__arrayZ", "foo.__array"], + // https://github.com/dlang/dmd/issues/22688 + // Zero-padded length prefix for non-module symbols (struct/class/enum) + // to disambiguate from module names with the same identifier. + ["_D10issue226883bar3zenFZi", "int issue22688.bar.zen()"], // module bar (no leading zero) + ["_D10issue2268803bar3zenFZi", "int issue22688.bar.zen()"], // struct bar (leading zero in length) ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`], diff --git a/druntime/test/profile/bothnew.def.exp b/druntime/test/profile/bothnew.def.exp index 91683fc1487d..fe6aa22a2f5b 100644 --- a/druntime/test/profile/bothnew.def.exp +++ b/druntime/test/profile/bothnew.def.exp @@ -2,7 +2,7 @@ FUNCTIONS _Dmain _D4both3fooFkZPSQo3Num - _D4both3Num6__ctorMFNckZSQxQu + _D4both03Num6__ctorMFNckZSQyQv _D4core8internal8lifetime__T18emplaceInitializerTS4both3NumZQBgFNaNbNiNeMKQzZv _D4core8lifetime__T11_d_newitemTTS4both3NumZQzFNaNbNeZPQw _D4core8lifetime__T16_d_newitemTTraceTS4both3NumZQBeFNaNbNeAyaiQeZPQBd diff --git a/spec/abi.dd b/spec/abi.dd index 4562a043ca5b..16379e1070c4 100644 --- a/spec/abi.dd +++ b/spec/abi.dd @@ -410,6 +410,16 @@ $(GNAME Digit): the number of characters in the $(GLINK Name). ) + $(P When a non-module, non-package symbol such as a `struct`, `class`, + or `enum` appears as a parent component in a $(GLINK QualifiedName), + its $(GLINK LName) is encoded with a leading $(B 0) before the + $(GLINK Number) length prefix. This disambiguates it from a module + name with the same identifier, since leading zeros do not change + the numeric value of the length. For example, a module named $(D bar) + is mangled as $(D 3bar), while a `struct` named $(D bar) in the same + scope is mangled as $(D 03bar). + ) + $(H3 $(LNAME2 back_ref, Back references)) $(P Any $(GLINK LName) or non-basic $(GLINK Type) (i.e. any type