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
66 changes: 63 additions & 3 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -6787,7 +6787,7 @@ private OverloadSet mergeOverloadSet(ScopeDsymbol sds, Identifier ident, Overloa
for (size_t j = 0; j < os.a.length; j++)
{
Dsymbol s2 = os.a[j];
if (s.toAlias() == s2.toAlias())
if (s.toAlias() == s2.toAlias() || areEquivalentMixinMembers(s, s2))
{
if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none))
{
Expand All @@ -6800,6 +6800,63 @@ private OverloadSet mergeOverloadSet(ScopeDsymbol sds, Identifier ident, Overloa
return os;
}

private bool templateMixinArgsEqual(RootObject o1, RootObject o2)
{
if (const t1 = isType(o1))
{
const t2 = isType(o2);
return t2 && t1.equals(t2);
}
if (const e1 = isExpression(o1))
{
const e2 = isExpression(o2);
return e2 && dmd.expressionsem.equals(e1, e2);
}
if (const s1 = isDsymbol(o1))
{
const s2 = isDsymbol(o2);
return s1 == s2;
}
return o1 is o2;
}

private bool isAnonymousTemplateMixin(TemplateMixin tm)
{
return tm && tm.ident && strncmp(tm.ident.toChars(), "__mixin", 7) == 0;
}

private bool areEquivalentMixinMembers(Dsymbol s1, Dsymbol s2)
{
s1 = s1.toAlias();
s2 = s2.toAlias();

auto f1 = s1.isFuncDeclaration();
auto f2 = s2.isFuncDeclaration();
if (!f1 || !f2 || !f1.ident.equals(f2.ident))
return false;

auto tm1 = f1.parent ? f1.parent.isTemplateMixin() : null;
auto tm2 = f2.parent ? f2.parent.isTemplateMixin() : null;
if (!tm1 || !tm2 || tm1.tempdecl != tm2.tempdecl)
return false;

if (isAnonymousTemplateMixin(tm1) == isAnonymousTemplateMixin(tm2))
return false;

if (!f1.type || !f2.type || !f1.type.equals(f2.type))
return false;

if (tm1.tiargs.length != tm2.tiargs.length)
return false;

foreach (i; 0 .. tm1.tiargs.length)
{
if (!templateMixinArgsEqual((*tm1.tiargs)[i], (*tm2.tiargs)[i]))
return false;
}
return true;
}

/***
* Returns true if any of the symbols `p1` or `p2` resides in the enclosing
* instantiation scope of `this`.
Expand Down Expand Up @@ -7802,7 +7859,8 @@ private extern(C++) class SearchVisitor : Visitor
}
else if (s2 && s != s2)
{
if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType())
if (s.toAlias() == s2.toAlias() || areEquivalentMixinMembers(s, s2)
|| s.getType() == s2.getType() && s.getType())
{
/* After following aliases, we found the same
* symbol, so it's not an ambiguity. But if one
Expand All @@ -7828,6 +7886,8 @@ private extern(C++) class SearchVisitor : Visitor
*/
s = s.toAlias();
s2 = s2.toAlias();
if (areEquivalentMixinMembers(s, s2))
continue;
/* If both s2 and s are overloadable (though we only
* need to check s once)
*/
Expand Down Expand Up @@ -7868,7 +7928,7 @@ private extern(C++) class SearchVisitor : Visitor

/* If two imports from C import files, pick first one, as C has global name space
*/
if (s.isCsymbol() && s2.isCsymbol())
if ((s.isCsymbol() && s2.isCsymbol()) || areEquivalentMixinMembers(s, s2))
continue;

if (!(flags & SearchOpt.ignoreErrors))
Expand Down
56 changes: 56 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -8014,6 +8014,57 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
FuncDeclaration resolveOverloadSet(Loc loc, Scope* sc,
OverloadSet os, Objects* tiargs, Type tthis, ArgumentList argumentList)
{
static bool templateMixinArgsEqual(RootObject o1, RootObject o2)
{
if (const t1 = isType(o1))
{
const t2 = isType(o2);
return t2 && t1.equals(t2);
}
if (const e1 = isExpression(o1))
{
const e2 = isExpression(o2);
return e2 && equals(e1, e2);
}
if (const s1 = isDsymbol(o1))
{
const s2 = isDsymbol(o2);
return s1 == s2;
}
return o1 is o2;
}

static bool areEquivalentMixinFunctions(FuncDeclaration f1, FuncDeclaration f2)
{
static bool isAnonymousTemplateMixin(TemplateMixin tm)
{
import core.stdc.string;

return tm && tm.ident && strncmp(tm.ident.toChars(), "__mixin", 7) == 0;
}

if (!f1 || !f2 || !f1.ident.equals(f2.ident) || !f1.type || !f2.type || !f1.type.equals(f2.type))
return false;

auto tm1 = f1.parent ? f1.parent.isTemplateMixin() : null;
auto tm2 = f2.parent ? f2.parent.isTemplateMixin() : null;
if (!tm1 || !tm2 || tm1.tempdecl != tm2.tempdecl)
return false;

if (isAnonymousTemplateMixin(tm1) == isAnonymousTemplateMixin(tm2))
return false;

if (tm1.tiargs.length != tm2.tiargs.length)
return false;

foreach (i; 0 .. tm1.tiargs.length)
{
if (!templateMixinArgsEqual((*tm1.tiargs)[i], (*tm2.tiargs)[i]))
return false;
}
return true;
}

FuncDeclaration f = null;
foreach (s; os.a)
{
Expand All @@ -8038,6 +8089,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// Same function found via two different paths (e.g. direct import and
// selective import alias), not an actual conflict.
}
else if (areEquivalentMixinFunctions(f, f2))
{
// Same mixin member found via named and unnamed instances of
// the same template mixin in the same scope.
}
else if (f.isCsymbol() && f2.isCsymbol())
{
/* C has global name space, so just pick one, such as f.
Expand Down
27 changes: 27 additions & 0 deletions compiler/test/compilable/issue17548.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mixin template T()
{
void f() {}
}

mixin T t0;
mixin T;

struct A
{
mixin T t;
mixin T;

void g()
{
f();
t.f();
}
}

void main()
{
A a;
a.g();
f();
t0.f();
}
Loading