Skip to content

Commit e30bd8a

Browse files
committed
[clang] Implement evaluation context for checking template parameters
Instead of manually adding a note pointing to the relevant template parameter to every relevant error, which is very easy to miss, this patch adds a new instantiation context note, so that this can work using RAII magic. This fixes a bunch of places where these notes were missing, and is more future-proof. Some diagnostics are reworked to make better use of this note: - Errors about missing template arguments now refer to the parameter which is missing an argument. - Template Template parameter mismatches now refer to template parameters as parameters instead of arguments. It's likely this will add the note to some diagnostics where the parameter is not super relevant, but this can be reworked with time and the decrease in maintenance burden makes up for it. This bypasses the templight dumper for the new context entry, as the tests are very hard to update. This depends on #125453, which is needed to avoid losing the context note for errors occuring during template argument deduction.
1 parent 9c26e34 commit e30bd8a

File tree

97 files changed

+648
-575
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+648
-575
lines changed

clang/docs/ReleaseNotes.rst

+10
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,16 @@ Bug Fixes to C++ Support
236236
direct-list-initialized from an array is corrected to direct-initialization.
237237
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
238238

239+
Improvements to C++ diagnostics
240+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
241+
242+
- Clang now more consistently adds a note pointing to the relevant template
243+
parameter. Some diagnostics are reworded to better take advantage of this.
244+
- Template Template Parameter diagnostics now stop referring to template
245+
parameters as template arguments, in some circumstances, better hiding
246+
from the users template template parameter partial ordering arcana.
247+
248+
239249
Bug Fixes to AST Handling
240250
^^^^^^^^^^^^^^^^^^^^^^^^^
241251
- Fixed type checking when a statement expression ends in an l-value of atomic type. (#GH106576)

clang/include/clang/Basic/DiagnosticSemaKinds.td

+9-18
Original file line numberDiff line numberDiff line change
@@ -5193,16 +5193,11 @@ def err_template_unnamed_class : Error<
51935193
def err_template_param_list_different_arity : Error<
51945194
"%select{too few|too many}0 template parameters in template "
51955195
"%select{|template parameter }1redeclaration">;
5196-
def note_template_param_list_different_arity : Note<
5197-
"%select{too few|too many}0 template parameters in template template "
5198-
"argument">;
51995196
def note_template_prev_declaration : Note<
52005197
"previous template %select{declaration|template parameter}0 is here">;
52015198
def err_template_param_different_kind : Error<
52025199
"template parameter has a different kind in template "
52035200
"%select{|template parameter }0redeclaration">;
5204-
def note_template_param_different_kind : Note<
5205-
"template parameter has a different kind in template argument">;
52065201

52075202
def err_invalid_decl_specifier_in_nontype_parm : Error<
52085203
"invalid declaration specifier in template non-type parameter">;
@@ -5211,8 +5206,6 @@ def err_template_nontype_parm_different_type : Error<
52115206
"template non-type parameter has a different type %0 in template "
52125207
"%select{|template parameter }1redeclaration">;
52135208

5214-
def note_template_nontype_parm_different_type : Note<
5215-
"template non-type parameter has a different type %0 in template argument">;
52165209
def note_template_nontype_parm_prev_declaration : Note<
52175210
"previous non-type template parameter with type %0 is here">;
52185211
def err_template_nontype_parm_bad_type : Error<
@@ -5303,10 +5296,15 @@ def err_template_missing_args : Error<
53035296
"%select{class template|function template|variable template|alias template|"
53045297
"template template parameter|concept|template}0 %1 requires template "
53055298
"arguments">;
5306-
def err_template_arg_list_different_arity : Error<
5307-
"%select{too few|too many}0 template arguments for "
5299+
def err_template_param_missing_arg : Error<
5300+
"missing template argument for template parameter">;
5301+
def err_template_template_param_missing_param : Error<
5302+
"no template parameter in this template template parameter "
5303+
"corresponds to non-defaulted template parameter of argument template">;
5304+
def err_template_too_many_args : Error<
5305+
"too many template arguments for "
53085306
"%select{class template|function template|variable template|alias template|"
5309-
"template template parameter|concept|template}1 %2">;
5307+
"template template parameter|concept|template}0 %1">;
53105308
def note_template_decl_here : Note<"template is declared here">;
53115309
def note_template_decl_external : Note<
53125310
"template declaration from hidden source: %0">;
@@ -5344,11 +5342,8 @@ def err_template_arg_not_valid_template : Error<
53445342
"template parameter">;
53455343
def note_template_arg_refers_here_func : Note<
53465344
"template argument refers to function template %0, here">;
5347-
def err_template_arg_template_params_mismatch : Error<
5348-
"template template argument has different template parameters than its "
5349-
"corresponding template template parameter">;
53505345
def note_template_arg_template_params_mismatch : Note<
5351-
"template template argument has different template parameters than its "
5346+
"template template argument is incompatible with its "
53525347
"corresponding template template parameter">;
53535348
def err_non_deduced_mismatch : Error<
53545349
"could not match %diff{$ against $|types}0,1">;
@@ -5910,10 +5905,6 @@ def err_template_parameter_pack_non_pack : Error<
59105905
"%select{template type|non-type template|template template}0 parameter"
59115906
"%select{| pack}1 conflicts with previous %select{template type|"
59125907
"non-type template|template template}0 parameter%select{ pack|}1">;
5913-
def note_template_parameter_pack_non_pack : Note<
5914-
"%select{template type|non-type template|template template}0 parameter"
5915-
"%select{| pack}1 does not match %select{template type|non-type template"
5916-
"|template template}0 parameter%select{ pack|}1 in template argument">;
59175908
def note_template_parameter_pack_here : Note<
59185909
"previous %select{template type|non-type template|template template}0 "
59195910
"parameter%select{| pack}1 declared here">;

clang/include/clang/Sema/Sema.h

+28-9
Original file line numberDiff line numberDiff line change
@@ -11820,7 +11820,7 @@ class Sema final : public SemaBase {
1182011820
bool *ConstraintsNotSatisfied = nullptr);
1182111821

1182211822
bool CheckTemplateTypeArgument(
11823-
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
11823+
TemplateArgumentLoc &Arg,
1182411824
SmallVectorImpl<TemplateArgument> &SugaredConverted,
1182511825
SmallVectorImpl<TemplateArgument> &CanonicalConverted);
1182611826

@@ -11856,9 +11856,13 @@ class Sema final : public SemaBase {
1185611856
bool PartialOrdering,
1185711857
bool *StrictPackMatch);
1185811858

11859+
/// Print the given named declaration to a string,
11860+
/// using the current PrintingPolicy, except that
11861+
/// TerseOutput will always be set.
11862+
SmallString<128> toTerseString(const NamedDecl &D) const;
11863+
1185911864
void NoteTemplateLocation(const NamedDecl &Decl,
1186011865
std::optional<SourceRange> ParamRange = {});
11861-
void NoteTemplateParameterLocation(const NamedDecl &Decl);
1186211866

1186311867
/// Given a non-type template argument that refers to a
1186411868
/// declaration and the type of its corresponding non-type template
@@ -11973,15 +11977,13 @@ class Sema final : public SemaBase {
1197311977
bool TemplateParameterListsAreEqual(
1197411978
const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New,
1197511979
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
11976-
TemplateParameterListEqualKind Kind,
11977-
SourceLocation TemplateArgLoc = SourceLocation());
11980+
TemplateParameterListEqualKind Kind);
1197811981

11979-
bool TemplateParameterListsAreEqual(
11980-
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
11981-
TemplateParameterListEqualKind Kind,
11982-
SourceLocation TemplateArgLoc = SourceLocation()) {
11982+
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
11983+
TemplateParameterList *Old, bool Complain,
11984+
TemplateParameterListEqualKind Kind) {
1198311985
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
11984-
Kind, TemplateArgLoc);
11986+
Kind);
1198511987
}
1198611988

1198711989
/// Check whether a template can be declared within this scope.
@@ -12860,6 +12862,11 @@ class Sema final : public SemaBase {
1286012862

1286112863
/// We are performing partial ordering for template template parameters.
1286212864
PartialOrderingTTP,
12865+
12866+
/// We are Checking a Template Parameter, so for any diagnostics which
12867+
/// occur in this scope, we will add a context note which points to this
12868+
/// template parameter.
12869+
CheckTemplateParameter,
1286312870
} Kind;
1286412871

1286512872
/// Was the enclosing context a non-instantiation SFINAE context?
@@ -13087,6 +13094,11 @@ class Sema final : public SemaBase {
1308713094
PartialOrderingTTP, TemplateDecl *PArg,
1308813095
SourceRange InstantiationRange = SourceRange());
1308913096

13097+
struct CheckTemplateParameter {};
13098+
/// \brief Note that we are checking a template parameter.
13099+
InstantiatingTemplate(Sema &SemaRef, CheckTemplateParameter,
13100+
NamedDecl *Param);
13101+
1309013102
/// Note that we have finished instantiating this template.
1309113103
void Clear();
1309213104

@@ -13120,6 +13132,13 @@ class Sema final : public SemaBase {
1312013132
InstantiatingTemplate &operator=(const InstantiatingTemplate &) = delete;
1312113133
};
1312213134

13135+
/// For any diagnostics which occur within its scope, adds a context note
13136+
/// pointing to the declaration of the template parameter.
13137+
struct CheckTemplateParameterRAII : InstantiatingTemplate {
13138+
CheckTemplateParameterRAII(Sema &S, NamedDecl *Param)
13139+
: InstantiatingTemplate(S, CheckTemplateParameter(), Param) {}
13140+
};
13141+
1312313142
bool SubstTemplateArgument(const TemplateArgumentLoc &Input,
1312413143
const MultiLevelTemplateArgumentList &TemplateArgs,
1312513144
TemplateArgumentLoc &Output,

clang/lib/Frontend/FrontendActions.cpp

+16-8
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
403403
}
404404

405405
private:
406-
static std::string toString(CodeSynthesisContext::SynthesisKind Kind) {
406+
static std::optional<std::string>
407+
toString(CodeSynthesisContext::SynthesisKind Kind) {
407408
switch (Kind) {
408409
case CodeSynthesisContext::TemplateInstantiation:
409410
return "TemplateInstantiation";
@@ -461,21 +462,25 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
461462
return "TypeAliasTemplateInstantiation";
462463
case CodeSynthesisContext::PartialOrderingTTP:
463464
return "PartialOrderingTTP";
465+
case CodeSynthesisContext::CheckTemplateParameter:
466+
return std::nullopt;
464467
}
465-
return "";
468+
return std::nullopt;
466469
}
467470

468471
template <bool BeginInstantiation>
469472
static void displayTemplightEntry(llvm::raw_ostream &Out, const Sema &TheSema,
470473
const CodeSynthesisContext &Inst) {
471474
std::string YAML;
472475
{
476+
std::optional<TemplightEntry> Entry =
477+
getTemplightEntry<BeginInstantiation>(TheSema, Inst);
478+
if (!Entry)
479+
return;
473480
llvm::raw_string_ostream OS(YAML);
474481
llvm::yaml::Output YO(OS);
475-
TemplightEntry Entry =
476-
getTemplightEntry<BeginInstantiation>(TheSema, Inst);
477482
llvm::yaml::EmptyContext Context;
478-
llvm::yaml::yamlize(YO, Entry, true, Context);
483+
llvm::yaml::yamlize(YO, *Entry, true, Context);
479484
}
480485
Out << "---" << YAML << "\n";
481486
}
@@ -555,10 +560,13 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
555560
}
556561

557562
template <bool BeginInstantiation>
558-
static TemplightEntry getTemplightEntry(const Sema &TheSema,
559-
const CodeSynthesisContext &Inst) {
563+
static std::optional<TemplightEntry>
564+
getTemplightEntry(const Sema &TheSema, const CodeSynthesisContext &Inst) {
560565
TemplightEntry Entry;
561-
Entry.Kind = toString(Inst.Kind);
566+
std::optional<std::string> Kind = toString(Inst.Kind);
567+
if (!Kind)
568+
return std::nullopt;
569+
Entry.Kind = *Kind;
562570
Entry.Event = BeginInstantiation ? "Begin" : "End";
563571
llvm::raw_string_ostream OS(Entry.Name);
564572
printEntryName(TheSema, Inst.Entity, OS);

clang/lib/Sema/SemaInit.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -7269,7 +7269,7 @@ static void CheckCXX98CompatAccessibleCopy(Sema &S,
72697269

72707270
void InitializationSequence::PrintInitLocationNote(Sema &S,
72717271
const InitializedEntity &Entity) {
7272-
if (Entity.isParamOrTemplateParamKind() && Entity.getDecl()) {
7272+
if (Entity.isParameterKind() && Entity.getDecl()) {
72737273
if (Entity.getDecl()->getLocation().isInvalid())
72747274
return;
72757275

@@ -7278,9 +7278,8 @@ void InitializationSequence::PrintInitLocationNote(Sema &S,
72787278
<< Entity.getDecl()->getDeclName();
72797279
else
72807280
S.Diag(Entity.getDecl()->getLocation(), diag::note_parameter_here);
7281-
}
7282-
else if (Entity.getKind() == InitializedEntity::EK_RelatedResult &&
7283-
Entity.getMethodDecl())
7281+
} else if (Entity.getKind() == InitializedEntity::EK_RelatedResult &&
7282+
Entity.getMethodDecl())
72847283
S.Diag(Entity.getMethodDecl()->getLocation(),
72857284
diag::note_method_return_type_change)
72867285
<< Entity.getMethodDecl()->getDeclName();

clang/lib/Sema/SemaLambda.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -1506,14 +1506,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
15061506
TemplateParameterList *TemplateParams =
15071507
getGenericLambdaTemplateParameterList(LSI, *this);
15081508
if (TemplateParams) {
1509-
for (const auto *TP : TemplateParams->asArray()) {
1509+
for (auto *TP : TemplateParams->asArray()) {
15101510
if (!TP->getIdentifier())
15111511
continue;
1512+
CheckTemplateParameterRAII CTP(*this, TP);
15121513
for (const auto &Capture : Intro.Captures) {
1513-
if (Capture.Id == TP->getIdentifier()) {
1514+
if (Capture.Id == TP->getIdentifier())
15141515
Diag(Capture.Loc, diag::err_template_param_shadow) << Capture.Id;
1515-
NoteTemplateParameterLocation(*TP);
1516-
}
15171516
}
15181517
}
15191518
}

clang/lib/Sema/SemaLookup.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -1580,9 +1580,13 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() {
15801580
unsigned N = CodeSynthesisContexts.size();
15811581
for (unsigned I = CodeSynthesisContextLookupModules.size();
15821582
I != N; ++I) {
1583-
Module *M = CodeSynthesisContexts[I].Entity ?
1584-
getDefiningModule(*this, CodeSynthesisContexts[I].Entity) :
1585-
nullptr;
1583+
auto &Ctx = CodeSynthesisContexts[I];
1584+
// FIXME: Are there any other context kinds that shouldn't be looked at
1585+
// here?
1586+
if (Ctx.Kind == CodeSynthesisContext::PartialOrderingTTP ||
1587+
Ctx.Kind == CodeSynthesisContext::CheckTemplateParameter)
1588+
continue;
1589+
Module *M = Ctx.Entity ? getDefiningModule(*this, Ctx.Entity) : nullptr;
15861590
if (M && !LookupModulesCache.insert(M).second)
15871591
M = nullptr;
15881592
CodeSynthesisContextLookupModules.push_back(M);
@@ -3703,7 +3707,8 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
37033707
TemplateParameterList *Params = FD->getTemplateParameters();
37043708
if (Params->size() == 1) {
37053709
IsTemplate = true;
3706-
if (!Params->getParam(0)->isTemplateParameterPack() && !StringLit) {
3710+
NamedDecl *Param = Params->getParam(0);
3711+
if (!Param->isTemplateParameterPack() && !StringLit) {
37073712
// Implied but not stated: user-defined integer and floating literals
37083713
// only ever use numeric literal operator templates, not templates
37093714
// taking a parameter of class type.
@@ -3716,6 +3721,7 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
37163721
if (StringLit) {
37173722
SFINAETrap Trap(*this);
37183723
CheckTemplateArgumentInfo CTAI;
3724+
CheckTemplateParameterRAII CTP(*this, Param);
37193725
TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit);
37203726
if (CheckTemplateArgument(
37213727
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),

0 commit comments

Comments
 (0)