From 1b2b3dd7639a14e819d3576d9660d6eec7f4a019 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 1 Jun 2024 00:47:19 +0100 Subject: [PATCH] Fix Issue 24135 - Eponymous template member overloads not shown as call candidates (#15572) --- compiler/src/dmd/dtemplate.d | 11 ++++ compiler/src/dmd/frontend.h | 6 +- compiler/src/dmd/funcsem.d | 66 ++++++++++++------- compiler/src/dmd/hdrgen.d | 3 +- .../fail_compilation/onemember_overloads.d | 38 +++++++++++ 5 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 compiler/test/fail_compilation/onemember_overloads.d diff --git a/compiler/src/dmd/dtemplate.d b/compiler/src/dmd/dtemplate.d index fb11821f42..33ec6b1849 100644 --- a/compiler/src/dmd/dtemplate.d +++ b/compiler/src/dmd/dtemplate.d @@ -751,6 +751,17 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol return buf.extractChars(); } + /**************************** + * Similar to `toChars`, but does not print the template constraints + */ + const(char)* toCharsNoConstraints() const + { + HdrGenState hgs = { skipConstraints: true }; + OutBuffer buf; + toCharsMaybeConstraints(this, buf, hgs); + return buf.extractChars(); + } + override Visibility visible() pure nothrow @nogc @safe { return visibility; diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 19b28fa0a1..d3fd859561 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -1487,6 +1487,7 @@ public: bool hasStaticCtorOrDtor() override; const char* kind() const override; const char* toChars() const override; + const char* toCharsNoConstraints() const; Visibility visible() override; const char* getConstraintEvalError(const char*& tip); TemplateDeclaration* isTemplateDeclaration() override; @@ -3981,6 +3982,7 @@ struct HdrGenState final bool doFuncBodies; bool vcg_ast; bool skipConstraints; + bool showOneMember; bool fullQual; int32_t tpltMember; int32_t autoMember; @@ -3997,6 +3999,7 @@ struct HdrGenState final doFuncBodies(), vcg_ast(), skipConstraints(), + showOneMember(true), fullQual(), tpltMember(), autoMember(), @@ -4007,7 +4010,7 @@ struct HdrGenState final inEnumDecl() { } - HdrGenState(bool hdrgen, bool ddoc = false, bool fullDump = false, bool importcHdr = false, bool doFuncBodies = false, bool vcg_ast = false, bool skipConstraints = false, bool fullQual = false, int32_t tpltMember = 0, int32_t autoMember = 0, int32_t forStmtInit = 0, int32_t insideFuncBody = 0, int32_t insideAggregate = 0, bool declstring = false, EnumDeclaration* inEnumDecl = nullptr) : + HdrGenState(bool hdrgen, bool ddoc = false, bool fullDump = false, bool importcHdr = false, bool doFuncBodies = false, bool vcg_ast = false, bool skipConstraints = false, bool showOneMember = true, bool fullQual = false, int32_t tpltMember = 0, int32_t autoMember = 0, int32_t forStmtInit = 0, int32_t insideFuncBody = 0, int32_t insideAggregate = 0, bool declstring = false, EnumDeclaration* inEnumDecl = nullptr) : hdrgen(hdrgen), ddoc(ddoc), fullDump(fullDump), @@ -4015,6 +4018,7 @@ struct HdrGenState final doFuncBodies(doFuncBodies), vcg_ast(vcg_ast), skipConstraints(skipConstraints), + showOneMember(showOneMember), fullQual(fullQual), tpltMember(tpltMember), autoMember(autoMember), diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d index 94d94e63ba..8e0ffa6127 100644 --- a/compiler/src/dmd/funcsem.d +++ b/compiler/src/dmd/funcsem.d @@ -1746,10 +1746,24 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) // max num of overloads to print (-v or -verror-supplements overrides this). const uint DisplayLimit = global.params.v.errorSupplementCount(); const(char)* constraintsTip; - // determine if the first candidate was printed - int printed; - bool matchSymbol(Dsymbol s, bool print, bool single_candidate = false) + int printed = 0; // number of candidates printed + int count = 0; // total candidates + bool child; // true if inside an eponymous template + const(char)* errorPrefix() @safe + { + if (child) + return " - Containing: "; + + // align with blank spaces after first message + enum plural = "Candidates are: "; + enum spaces = " "; + if (printed) + return spaces; + + return (count == 1) ? "Candidate is: " : plural; + } + bool matchSymbol(Dsymbol s, bool print) { if (auto fd = s.isFuncDeclaration()) { @@ -1765,16 +1779,14 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) return true; auto tf = cast(TypeFunction) fd.type; OutBuffer buf; - buf.writestring(fd.toPrettyChars()); + buf.writestring(child ? fd.toChars() : fd.toPrettyChars()); buf.writestring(parametersTypeToChars(tf.parameterList)); if (tf.mod) { buf.writeByte(' '); buf.MODtoBuffer(tf.mod); } - .errorSupplemental(fd.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", buf.peekChars()); + .errorSupplemental(fd.loc, "%s`%s`", errorPrefix(), buf.peekChars()); } else if (auto td = s.isTemplateDeclaration()) { @@ -1782,35 +1794,43 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) if (!print) return true; + + // td.onemember may not have overloads set + // (see fail_compilation/onemember_overloads.d) + // assume if more than one member it is overloaded internally + bool recurse = td.onemember && td.members.length > 1; OutBuffer buf; HdrGenState hgs; hgs.skipConstraints = true; + hgs.showOneMember = !recurse; toCharsMaybeConstraints(td, buf, hgs); const tmsg = buf.peekChars(); - const cmsg = td.getConstraintEvalError(constraintsTip); - - // add blank space if there are multiple candidates - // the length of the blank space is `strlen("Candidates are: ")` + const cmsg = child ? null : td.getConstraintEvalError(constraintsTip); if (cmsg) - { - .errorSupplemental(td.loc, - printed ? " `%s`\n%s" : - single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s", - tmsg, cmsg); - } + .errorSupplemental(td.loc, "%s`%s`\n%s", errorPrefix(), tmsg, cmsg); else + .errorSupplemental(td.loc, "%s`%s`", errorPrefix(), tmsg); + + if (recurse) { - .errorSupplemental(td.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", - tmsg); + child = true; + foreach (d; *td.members) + { + if (d.ident != td.ident) + continue; + + if (auto fd2 = d.isFuncDeclaration()) + matchSymbol(fd2, print); + else if (auto td2 = d.isTemplateDeclaration()) + matchSymbol(td2, print); + } + child = false; } } return true; } // determine if there's > 1 candidate - int count = 0; overloadApply(declaration, (s) { if (matchSymbol(s, false)) count++; @@ -1820,7 +1840,7 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) overloadApply(declaration, (s) { if (global.params.v.verbose || printed < DisplayLimit) { - if (matchSymbol(s, true, count == 1)) + if (matchSymbol(s, true)) printed++; } else diff --git a/compiler/src/dmd/hdrgen.d b/compiler/src/dmd/hdrgen.d index 41da11dedb..1e72cf709c 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -62,6 +62,7 @@ struct HdrGenState bool doFuncBodies; /// include function bodies in output bool vcg_ast; /// write out codegen-ast bool skipConstraints; // skip constraints when doing templates + bool showOneMember = true; bool fullQual; /// fully qualify types when printing int tpltMember; @@ -1974,7 +1975,7 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re } buf.writeByte(')'); - if (td.onemember) + if (hgs.showOneMember && td.onemember) { if (const fd = td.onemember.isFuncDeclaration()) { diff --git a/compiler/test/fail_compilation/onemember_overloads.d b/compiler/test/fail_compilation/onemember_overloads.d new file mode 100644 index 0000000000..40d23b3f01 --- /dev/null +++ b/compiler/test/fail_compilation/onemember_overloads.d @@ -0,0 +1,38 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/onemember_overloads.d(29): Error: none of the overloads of `skipOver` are callable using argument types `()` +fail_compilation/onemember_overloads.d(25): Candidates are: `onemember_overloads.skipOver(string)` +fail_compilation/onemember_overloads.d(18): `skipOver(alias pred = (a, b) => a == b)` +fail_compilation/onemember_overloads.d(20): - Containing: `skipOver(Haystack, Needles...)(ref Haystack haystack, Needles needles)` +fail_compilation/onemember_overloads.d(21): - Containing: `skipOver(R)(ref R r1)` +fail_compilation/onemember_overloads.d(22): - Containing: `skipOver(R, Es...)(ref R r, Es es)` +fail_compilation/onemember_overloads.d(30): Error: template `t2` is not callable using argument types `!()()` +fail_compilation/onemember_overloads.d(33): Candidate is: `t2(T)` +fail_compilation/onemember_overloads.d(35): - Containing: `t2(string)` +fail_compilation/onemember_overloads.d(36): - Containing: `t2(int[])` +fail_compilation/onemember_overloads.d(37): - Containing: `t2(R)(R)` +--- +*/ + +template skipOver(alias pred = (a, b) => a == b) +{ + bool skipOver(Haystack, Needles...)(ref Haystack haystack, Needles needles) => true; + bool skipOver(R)(ref R r1) => true; + bool skipOver(R, Es...)(ref R r, Es es) => true; +} + +void skipOver(string); + +void main() +{ + skipOver(); + t2(); +} + +template t2(T) +{ + bool t2(string); + bool t2(int[]); + bool t2(R)(R); +}