Skip to content

fix issue 21429 . Allow mixed-in opDispatch overloads #21444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 8, 2025
Merged
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
9 changes: 6 additions & 3 deletions compiler/src/dmd/opover.d
Original file line number Diff line number Diff line change
Expand Up @@ -551,14 +551,15 @@ Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop)

// Try opBinary and opBinaryRight
Dsymbol s = search_function(ad1, Id.opBinary);
if (s && !s.isTemplateDeclaration())

if (s && !(s.isTemplateDeclaration() || s.isOverloadSet))
{
error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
return ErrorExp.get();
}

Dsymbol s_r = search_function(ad2, Id.opBinaryRight);
if (s_r && !s_r.isTemplateDeclaration())
if (s_r && !(s_r.isTemplateDeclaration() || s_r.isOverloadSet()))
{
error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
return ErrorExp.get();
Expand Down Expand Up @@ -999,7 +1000,7 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt

AggregateDeclaration ad1 = isAggregate(e.e1.type);
Dsymbol s = search_function(ad1, Id.opOpAssign);
if (s && !s.isTemplateDeclaration())
if (s && !(s.isTemplateDeclaration() || s.isOverloadSet()))
{
error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars());
return ErrorExp.get();
Expand Down Expand Up @@ -1187,6 +1188,8 @@ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
return fd;
if (TemplateDeclaration td = s2.isTemplateDeclaration())
return td;
if (OverloadSet os = s2.isOverloadSet())
return os;
}
return null;
}
Expand Down
31 changes: 23 additions & 8 deletions compiler/src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -5013,17 +5013,32 @@
/* Rewrite e.ident as:
* e.opDispatch!("ident")
*/
TemplateDeclaration td = fd.isTemplateDeclaration();
if (!td)
{
.error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s", fd.kind, fd.toPrettyChars, fd.kind());
return returnExp(ErrorExp.get());
}
auto se = new StringExp(e.loc, ident.toString());

auto tiargs = new Objects();
auto se = new StringExp(e.loc, ident.toString());
tiargs.push(se);
auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
dti.ti.tempdecl = td;

if (OverloadSet os = fd.isOverloadSet())
{
if (!findTempDecl(dti, sc))
{
.error(fd.loc, "Couldn't find template declaration for opDispatch");
return returnExp(ErrorExp.get());
}
}
else
{
TemplateDeclaration td = fd.isTemplateDeclaration();

Check warning on line 5032 in compiler/src/dmd/typesem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/typesem.d#L5031-L5032

Added lines #L5031 - L5032 were not covered by tests
if (!td)
{
.error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s",
fd.kind, fd.toPrettyChars, fd.kind());
return returnExp(ErrorExp.get());
}
dti.ti.tempdecl = td;
}

Check warning on line 5040 in compiler/src/dmd/typesem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/typesem.d#L5040

Added line #L5040 was not covered by tests

/* opDispatch, which doesn't need IFTI, may occur instantiate error.
* e.g.
* template opDispatch(name) if (isValid!name) { ... }
Expand Down
21 changes: 21 additions & 0 deletions compiler/test/fail_compilation/test21429.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
TEST_OUTPUT:
---
fail_compilation/test21429.d(20): Error: no property `z` for `s` of type `test21429.S`
fail_compilation/test21429.d(13): struct `S` defined here
---
*/
//make sure this fails properly after fixing bug 21429.d
template mixinOpDispatch(string id){
string opDispatch(string s)() if(s == id){ return id; }
}

struct S {
mixin mixinOpDispatch!"x";
mixin mixinOpDispatch!"y";
}

void main(){
S s;
auto fail = s.z;
}
131 changes: 131 additions & 0 deletions compiler/test/runnable/test21429.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
mixin template OD(string s)
{

string opDispatch(string name)() if (name == s)
{
return name;
}
}

mixin template ODA(string s)
{
void opDispatch(string name)(int x) if (name == s)
{
this.val = x;
}
}

struct T
{
mixin OD!"x";
mixin OD!"y";
}

struct TAssign
{
int val;
mixin ODA!"x";
mixin ODA!"y";
}

struct U
{
mixin OD!"z";
}


template adder()
{
int opBinary(string s : "+")(int x) { return x; }
}

template subtracter()
{
int opBinary(string s : "-")(int x) { return -x; }
}


struct Arithmetic
{
mixin adder;
mixin subtracter;

}

template adderRight()
{
int opBinaryRight(string s : "+")(int x){ return x; }
}


template subtracterRight()
{
int opBinaryRight(string s: "-")(int x){ return -x; }
}

struct ArithmeticRight
{
mixin adderRight;
mixin subtracterRight;
}

template mixinOpAssign(string op)
{
void opOpAssign(string s)(int x) if(s == op)
{
val = x;
lastOp = s;
}
}

struct AssignOverloads
{
int val;
string lastOp;
mixin mixinOpAssign!"+";
mixin mixinOpAssign!"-";
}


void main()
{

T t;
string s = t.x();
assert(s == "x");
assert(t.y == "y");

//explicit call should work
assert(t.opDispatch!"x" == "x");


//TODO: fix these
Arithmetic a;
assert((a + 5) == 5);
assert((a - 7) == -7);


ArithmeticRight ar;
assert((5 + ar) == 5);
assert((7 - ar) == -7);


U u;
//should work for a single mixin
assert(u.z == "z");

TAssign ta;
ta.x = 5;
assert(ta.val == 5);
ta.y = 10;
assert(ta.val == 10);

AssignOverloads oa;
oa += 5;
assert(oa.val == 5);
assert(oa.lastOp == "+");
oa -= 10;
assert(oa.val == 10);
assert(oa.lastOp == "-");

}
Loading