Fix Issue 24135 - Eponymous template member overloads not shown as call candidates (#15572)

This commit is contained in:
Nick Treleaven 2024-06-01 00:47:19 +01:00 committed by GitHub
parent 61b95f3584
commit 1b2b3dd763
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 99 additions and 25 deletions

View file

@ -751,6 +751,17 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
return buf.extractChars(); 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 override Visibility visible() pure nothrow @nogc @safe
{ {
return visibility; return visibility;

View file

@ -1487,6 +1487,7 @@ public:
bool hasStaticCtorOrDtor() override; bool hasStaticCtorOrDtor() override;
const char* kind() const override; const char* kind() const override;
const char* toChars() const override; const char* toChars() const override;
const char* toCharsNoConstraints() const;
Visibility visible() override; Visibility visible() override;
const char* getConstraintEvalError(const char*& tip); const char* getConstraintEvalError(const char*& tip);
TemplateDeclaration* isTemplateDeclaration() override; TemplateDeclaration* isTemplateDeclaration() override;
@ -3981,6 +3982,7 @@ struct HdrGenState final
bool doFuncBodies; bool doFuncBodies;
bool vcg_ast; bool vcg_ast;
bool skipConstraints; bool skipConstraints;
bool showOneMember;
bool fullQual; bool fullQual;
int32_t tpltMember; int32_t tpltMember;
int32_t autoMember; int32_t autoMember;
@ -3997,6 +3999,7 @@ struct HdrGenState final
doFuncBodies(), doFuncBodies(),
vcg_ast(), vcg_ast(),
skipConstraints(), skipConstraints(),
showOneMember(true),
fullQual(), fullQual(),
tpltMember(), tpltMember(),
autoMember(), autoMember(),
@ -4007,7 +4010,7 @@ struct HdrGenState final
inEnumDecl() 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), hdrgen(hdrgen),
ddoc(ddoc), ddoc(ddoc),
fullDump(fullDump), fullDump(fullDump),
@ -4015,6 +4018,7 @@ struct HdrGenState final
doFuncBodies(doFuncBodies), doFuncBodies(doFuncBodies),
vcg_ast(vcg_ast), vcg_ast(vcg_ast),
skipConstraints(skipConstraints), skipConstraints(skipConstraints),
showOneMember(showOneMember),
fullQual(fullQual), fullQual(fullQual),
tpltMember(tpltMember), tpltMember(tpltMember),
autoMember(autoMember), autoMember(autoMember),

View file

@ -1746,10 +1746,24 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
// max num of overloads to print (-v or -verror-supplements overrides this). // max num of overloads to print (-v or -verror-supplements overrides this).
const uint DisplayLimit = global.params.v.errorSupplementCount(); const uint DisplayLimit = global.params.v.errorSupplementCount();
const(char)* constraintsTip; 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()) if (auto fd = s.isFuncDeclaration())
{ {
@ -1765,16 +1779,14 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
return true; return true;
auto tf = cast(TypeFunction) fd.type; auto tf = cast(TypeFunction) fd.type;
OutBuffer buf; OutBuffer buf;
buf.writestring(fd.toPrettyChars()); buf.writestring(child ? fd.toChars() : fd.toPrettyChars());
buf.writestring(parametersTypeToChars(tf.parameterList)); buf.writestring(parametersTypeToChars(tf.parameterList));
if (tf.mod) if (tf.mod)
{ {
buf.writeByte(' '); buf.writeByte(' ');
buf.MODtoBuffer(tf.mod); buf.MODtoBuffer(tf.mod);
} }
.errorSupplemental(fd.loc, .errorSupplemental(fd.loc, "%s`%s`", errorPrefix(), buf.peekChars());
printed ? " `%s`" :
single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", buf.peekChars());
} }
else if (auto td = s.isTemplateDeclaration()) else if (auto td = s.isTemplateDeclaration())
{ {
@ -1782,35 +1794,43 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
if (!print) if (!print)
return true; 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; OutBuffer buf;
HdrGenState hgs; HdrGenState hgs;
hgs.skipConstraints = true; hgs.skipConstraints = true;
hgs.showOneMember = !recurse;
toCharsMaybeConstraints(td, buf, hgs); toCharsMaybeConstraints(td, buf, hgs);
const tmsg = buf.peekChars(); const tmsg = buf.peekChars();
const cmsg = td.getConstraintEvalError(constraintsTip); const cmsg = child ? null : td.getConstraintEvalError(constraintsTip);
// add blank space if there are multiple candidates
// the length of the blank space is `strlen("Candidates are: ")`
if (cmsg) if (cmsg)
{ .errorSupplemental(td.loc, "%s`%s`\n%s", errorPrefix(), tmsg, cmsg);
.errorSupplemental(td.loc,
printed ? " `%s`\n%s" :
single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s",
tmsg, cmsg);
}
else else
.errorSupplemental(td.loc, "%s`%s`", errorPrefix(), tmsg);
if (recurse)
{ {
.errorSupplemental(td.loc, child = true;
printed ? " `%s`" : foreach (d; *td.members)
single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", {
tmsg); 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; return true;
} }
// determine if there's > 1 candidate // determine if there's > 1 candidate
int count = 0;
overloadApply(declaration, (s) { overloadApply(declaration, (s) {
if (matchSymbol(s, false)) if (matchSymbol(s, false))
count++; count++;
@ -1820,7 +1840,7 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
overloadApply(declaration, (s) { overloadApply(declaration, (s) {
if (global.params.v.verbose || printed < DisplayLimit) if (global.params.v.verbose || printed < DisplayLimit)
{ {
if (matchSymbol(s, true, count == 1)) if (matchSymbol(s, true))
printed++; printed++;
} }
else else

View file

@ -62,6 +62,7 @@ struct HdrGenState
bool doFuncBodies; /// include function bodies in output bool doFuncBodies; /// include function bodies in output
bool vcg_ast; /// write out codegen-ast bool vcg_ast; /// write out codegen-ast
bool skipConstraints; // skip constraints when doing templates bool skipConstraints; // skip constraints when doing templates
bool showOneMember = true;
bool fullQual; /// fully qualify types when printing bool fullQual; /// fully qualify types when printing
int tpltMember; int tpltMember;
@ -1974,7 +1975,7 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re
} }
buf.writeByte(')'); buf.writeByte(')');
if (td.onemember) if (hgs.showOneMember && td.onemember)
{ {
if (const fd = td.onemember.isFuncDeclaration()) if (const fd = td.onemember.isFuncDeclaration())
{ {

View file

@ -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);
}