Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 compiler/src/dmd/backend/drtlsym.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
46 changes: 44 additions & 2 deletions compiler/src/dmd/mangle/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/test/runnable/imports/issue22688/bar.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module issue22688.bar;

int zen() => 1;
28 changes: 28 additions & 0 deletions compiler/test/runnable/imports/issue22688/package.d
Original file line number Diff line number Diff line change
@@ -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);
}
10 changes: 10 additions & 0 deletions compiler/test/runnable/issue22688.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// EXTRA_FILES: imports/issue22688/package.d imports/issue22688/bar.d

module issue22688_test;

import issue22688;

void main()
{
test22688();
}
28 changes: 14 additions & 14 deletions compiler/test/runnable/mangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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()()
{
Expand Down Expand Up @@ -304,27 +304,27 @@ 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()
{
class C { int var; void func() {} }

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
Expand Down Expand Up @@ -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"); // <-
}

/*******************************************/
Expand Down Expand Up @@ -582,7 +582,7 @@ class CC
}
}

static assert(CC.member.mangleof == "_D6mangle2CC6memberMFNlZPi");
static assert(CC.member.mangleof == "_D6mangle02CC6memberMFNlZPi");

/***************************************************/

Expand Down
15 changes: 10 additions & 5 deletions druntime/src/core/demangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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", ""],
Expand Down Expand Up @@ -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()`],
Expand Down
2 changes: 1 addition & 1 deletion druntime/test/profile/bothnew.def.exp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
FUNCTIONS
_Dmain
_D4both3fooFkZPSQo3Num
_D4both3Num6__ctorMFNckZSQxQu
_D4both03Num6__ctorMFNckZSQyQv
_D4core8internal8lifetime__T18emplaceInitializerTS4both3NumZQBgFNaNbNiNeMKQzZv
_D4core8lifetime__T11_d_newitemTTS4both3NumZQzFNaNbNeZPQw
_D4core8lifetime__T16_d_newitemTTraceTS4both3NumZQBeFNaNbNeAyaiQeZPQBd
Expand Down
10 changes: 10 additions & 0 deletions spec/abi.dd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading