Skip to content

Commit 9e485f1

Browse files
committed
[Clang] Fix missing source location in template diagnostic for local classes
Fixes #147324 When diagnosing templates inside local classes with abbreviated function templates (auto parameters in C++20+), the error message was missing file name and line number information. This patch improves CheckTemplateDeclScope to detect abbreviated function templates and emit a more specific diagnostic with proper source location.
1 parent ace77c2 commit 9e485f1

File tree

6 files changed

+187
-58
lines changed

6 files changed

+187
-58
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 130 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
318318
}
319319
}
320320

321-
if (isPackProducingBuiltinTemplateName(Template) && S &&
321+
if (isPackProducingBuiltinTemplateName(Template) &&
322322
S->getTemplateParamParent() == nullptr)
323323
Diag(Name.getBeginLoc(), diag::err_builtin_pack_outside_template) << TName;
324324
// Recover by returning the template, even though we would never be able to
@@ -408,7 +408,9 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
408408
IsDependent = !LookupCtx && ObjectType->isDependentType();
409409
assert((IsDependent || !ObjectType->isIncompleteType() ||
410410
!ObjectType->getAs<TagType>() ||
411-
ObjectType->castAs<TagType>()->getDecl()->isEntityBeingDefined()) &&
411+
ObjectType->castAs<TagType>()
412+
->getOriginalDecl()
413+
->isEntityBeingDefined()) &&
412414
"Caller should have completed object type");
413415

414416
// Template names cannot appear inside an Objective-C class or object type
@@ -949,11 +951,11 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
949951

950952
switch (Arg.getKind()) {
951953
case ParsedTemplateArgument::Type: {
952-
TypeSourceInfo *TSI;
953-
QualType T = SemaRef.GetTypeFromParser(Arg.getAsType(), &TSI);
954-
if (!TSI)
955-
TSI = SemaRef.Context.getTrivialTypeSourceInfo(T, Arg.getNameLoc());
956-
return TemplateArgumentLoc(TemplateArgument(T), TSI);
954+
TypeSourceInfo *DI;
955+
QualType T = SemaRef.GetTypeFromParser(Arg.getAsType(), &DI);
956+
if (!DI)
957+
DI = SemaRef.Context.getTrivialTypeSourceInfo(T, Arg.getNameLoc());
958+
return TemplateArgumentLoc(TemplateArgument(T), DI);
957959
}
958960

959961
case ParsedTemplateArgument::NonType: {
@@ -1817,7 +1819,7 @@ class ConstraintRefersToContainingTemplateChecker
18171819
}
18181820

18191821
bool VisitTagType(const TagType *T) override {
1820-
return TraverseDecl(T->getDecl());
1822+
return TraverseDecl(T->getOriginalDecl());
18211823
}
18221824

18231825
bool TraverseDecl(const Decl *D) override {
@@ -2788,7 +2790,7 @@ struct DependencyChecker : DynamicRecursiveASTVisitor {
27882790
// An InjectedClassNameType will never have a dependent template name,
27892791
// so no need to traverse it.
27902792
return TraverseTemplateArguments(
2791-
T->getTemplateArgs(T->getDecl()->getASTContext()));
2793+
T->getTemplateArgs(T->getOriginalDecl()->getASTContext()));
27922794
}
27932795
};
27942796
} // end anonymous namespace
@@ -2912,7 +2914,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
29122914
if (const EnumType *EnumT = T->getAsCanonical<EnumType>()) {
29132915
// FIXME: Forward-declared enums require a TSK_ExplicitSpecialization
29142916
// check here.
2915-
EnumDecl *Enum = EnumT->getDecl();
2917+
EnumDecl *Enum = EnumT->getOriginalDecl();
29162918

29172919
// Get to the parent type.
29182920
if (TypeDecl *Parent = dyn_cast<TypeDecl>(Enum->getParent()))
@@ -3350,7 +3352,7 @@ static QualType builtinCommonTypeImpl(Sema &S, ElaboratedTypeKeyword Keyword,
33503352
}
33513353

33523354
static bool isInVkNamespace(const RecordType *RT) {
3353-
DeclContext *DC = RT->getDecl()->getDeclContext();
3355+
DeclContext *DC = RT->getOriginalDecl()->getDeclContext();
33543356
if (!DC)
33553357
return false;
33563358

@@ -3367,8 +3369,9 @@ static SpirvOperand checkHLSLSpirvTypeOperand(Sema &SemaRef,
33673369
if (auto *RT = OperandArg->getAsCanonical<RecordType>()) {
33683370
bool Literal = false;
33693371
SourceLocation LiteralLoc;
3370-
if (isInVkNamespace(RT) && RT->getDecl()->getName() == "Literal") {
3371-
auto SpecDecl = dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
3372+
if (isInVkNamespace(RT) && RT->getOriginalDecl()->getName() == "Literal") {
3373+
auto SpecDecl =
3374+
dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl());
33723375
assert(SpecDecl);
33733376

33743377
const TemplateArgumentList &LiteralArgs = SpecDecl->getTemplateArgs();
@@ -3379,8 +3382,9 @@ static SpirvOperand checkHLSLSpirvTypeOperand(Sema &SemaRef,
33793382
}
33803383

33813384
if (RT && isInVkNamespace(RT) &&
3382-
RT->getDecl()->getName() == "integral_constant") {
3383-
auto SpecDecl = dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
3385+
RT->getOriginalDecl()->getName() == "integral_constant") {
3386+
auto SpecDecl =
3387+
dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl());
33843388
assert(SpecDecl);
33853389

33863390
const TemplateArgumentList &ConstantArgs = SpecDecl->getTemplateArgs();
@@ -3846,14 +3850,13 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
38463850
// within enable_if in a SFINAE context, dig out the specific
38473851
// enable_if condition that failed and present that instead.
38483852
if (isEnableIfAliasTemplate(AliasTemplate)) {
3849-
if (SFINAETrap *Trap = getSFINAEContext();
3850-
TemplateDeductionInfo *DeductionInfo =
3851-
Trap ? Trap->getDeductionInfo() : nullptr) {
3852-
if (DeductionInfo->hasSFINAEDiagnostic() &&
3853-
DeductionInfo->peekSFINAEDiagnostic().second.getDiagID() ==
3854-
diag::err_typename_nested_not_found_enable_if &&
3855-
TemplateArgs[0].getArgument().getKind() ==
3856-
TemplateArgument::Expression) {
3853+
if (auto DeductionInfo = isSFINAEContext()) {
3854+
if (*DeductionInfo &&
3855+
(*DeductionInfo)->hasSFINAEDiagnostic() &&
3856+
(*DeductionInfo)->peekSFINAEDiagnostic().second.getDiagID() ==
3857+
diag::err_typename_nested_not_found_enable_if &&
3858+
TemplateArgs[0].getArgument().getKind()
3859+
== TemplateArgument::Expression) {
38573860
Expr *FailedCond;
38583861
std::string FailedDescription;
38593862
std::tie(FailedCond, FailedDescription) =
@@ -3862,14 +3865,15 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
38623865
// Remove the old SFINAE diagnostic.
38633866
PartialDiagnosticAt OldDiag =
38643867
{SourceLocation(), PartialDiagnostic::NullDiagnostic()};
3865-
DeductionInfo->takeSFINAEDiagnostic(OldDiag);
3868+
(*DeductionInfo)->takeSFINAEDiagnostic(OldDiag);
38663869

38673870
// Add a new SFINAE diagnostic specifying which condition
38683871
// failed.
3869-
DeductionInfo->addSFINAEDiagnostic(
3870-
OldDiag.first,
3871-
PDiag(diag::err_typename_nested_not_found_requirement)
3872-
<< FailedDescription << FailedCond->getSourceRange());
3872+
(*DeductionInfo)->addSFINAEDiagnostic(
3873+
OldDiag.first,
3874+
PDiag(diag::err_typename_nested_not_found_requirement)
3875+
<< FailedDescription
3876+
<< FailedCond->getSourceRange());
38733877
}
38743878
}
38753879
}
@@ -3955,7 +3959,6 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
39553959

39563960
if (Decl->getSpecializationKind() == TSK_Undeclared &&
39573961
ClassTemplate->getTemplatedDecl()->hasAttrs()) {
3958-
NonSFINAEContext _(*this);
39593962
InstantiatingTemplate Inst(*this, TemplateLoc, Decl);
39603963
if (!Inst.isInvalid()) {
39613964
MultiLevelTemplateArgumentList TemplateArgLists(Template,
@@ -4107,7 +4110,7 @@ TypeResult Sema::ActOnTagTemplateIdType(TagUseKind TUK,
41074110

41084111
// Check the tag kind
41094112
if (const RecordType *RT = Result->getAs<RecordType>()) {
4110-
RecordDecl *D = RT->getDecl();
4113+
RecordDecl *D = RT->getOriginalDecl();
41114114

41124115
IdentifierInfo *Id = D->getIdentifier();
41134116
assert(Id && "templated class must have an identifier");
@@ -4330,7 +4333,7 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) {
43304333
}
43314334

43324335
DeclResult Sema::ActOnVarTemplateSpecialization(
4333-
Scope *S, Declarator &D, TypeSourceInfo *TSI, LookupResult &Previous,
4336+
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
43344337
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
43354338
StorageClass SC, bool IsPartialSpecialization) {
43364339
// D must be variable template id.
@@ -4456,8 +4459,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
44564459
VarTemplatePartialSpecializationDecl *Partial =
44574460
VarTemplatePartialSpecializationDecl::Create(
44584461
Context, VarTemplate->getDeclContext(), TemplateKWLoc,
4459-
TemplateNameLoc, TemplateParams, VarTemplate, TSI->getType(), TSI,
4460-
SC, CTAI.CanonicalConverted);
4462+
TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC,
4463+
CTAI.CanonicalConverted);
44614464
Partial->setTemplateArgsAsWritten(TemplateArgs);
44624465

44634466
if (!PrevPartial)
@@ -4475,7 +4478,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
44754478
// this explicit specialization or friend declaration.
44764479
Specialization = VarTemplateSpecializationDecl::Create(
44774480
Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc,
4478-
VarTemplate, TSI->getType(), TSI, SC, CTAI.CanonicalConverted);
4481+
VarTemplate, DI->getType(), DI, SC, CTAI.CanonicalConverted);
44794482
Specialization->setTemplateArgsAsWritten(TemplateArgs);
44804483

44814484
if (!PrevDecl)
@@ -5566,11 +5569,12 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
55665569

55675570
auto checkExpr = [&](Expr *E) -> Expr * {
55685571
TemplateArgument SugaredResult, CanonicalResult;
5572+
unsigned CurSFINAEErrors = NumSFINAEErrors;
55695573
ExprResult Res = CheckTemplateArgument(
55705574
NTTP, NTTPType, E, SugaredResult, CanonicalResult,
55715575
/*StrictCheck=*/CTAI.MatchingTTP || CTAI.PartialOrdering, CTAK);
55725576
// If the current template argument causes an error, give up now.
5573-
if (Res.isInvalid())
5577+
if (Res.isInvalid() || CurSFINAEErrors < NumSFINAEErrors)
55745578
return nullptr;
55755579
CTAI.SugaredConverted.push_back(SugaredResult);
55765580
CTAI.CanonicalConverted.push_back(CanonicalResult);
@@ -6379,11 +6383,11 @@ bool UnnamedLocalNoLinkageFinder::VisitDeducedTemplateSpecializationType(
63796383
}
63806384

63816385
bool UnnamedLocalNoLinkageFinder::VisitRecordType(const RecordType* T) {
6382-
return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
6386+
return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
63836387
}
63846388

63856389
bool UnnamedLocalNoLinkageFinder::VisitEnumType(const EnumType* T) {
6386-
return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
6390+
return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
63876391
}
63886392

63896393
bool UnnamedLocalNoLinkageFinder::VisitTemplateTypeParmType(
@@ -6408,7 +6412,7 @@ bool UnnamedLocalNoLinkageFinder::VisitTemplateSpecializationType(
64086412

64096413
bool UnnamedLocalNoLinkageFinder::VisitInjectedClassNameType(
64106414
const InjectedClassNameType* T) {
6411-
return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
6415+
return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
64126416
}
64136417

64146418
bool UnnamedLocalNoLinkageFinder::VisitDependentNameType(
@@ -8421,7 +8425,7 @@ bool Sema::TemplateParameterListsAreEqual(
84218425
return true;
84228426
}
84238427

8424-
bool
8428+
bool
84258429
Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
84268430
if (!S)
84278431
return false;
@@ -8445,35 +8449,103 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
84458449
}
84468450
Ctx = Ctx ? Ctx->getRedeclContext() : nullptr;
84478451

8448-
// C++ [temp]p2:
8449-
// A template-declaration can appear only as a namespace scope or
8450-
// class scope declaration.
8451-
// C++ [temp.expl.spec]p3:
8452-
// An explicit specialization may be declared in any scope in which the
8453-
// corresponding primary template may be defined.
8454-
// C++ [temp.class.spec]p6: [P2096]
8455-
// A partial specialization may be declared in any scope in which the
8456-
// corresponding primary template may be defined.
8452+
// Compute a SourceLocation to use for diagnostics. Prefer the explicit
8453+
// template location, but fall back to nearby Decl locations when needed.
8454+
SourceLocation Loc = TemplateParams->getTemplateLoc();
8455+
if (Loc.isInvalid())
8456+
Loc = TemplateParams->getSourceRange().getBegin();
8457+
8458+
if (Loc.isInvalid() && Ctx) {
8459+
if (const Decl *D = dyn_cast<Decl>(Ctx))
8460+
Loc = D->getBeginLoc();
8461+
}
8462+
8463+
CXXRecordDecl *RD = Ctx ? dyn_cast<CXXRecordDecl>(Ctx) : nullptr;
8464+
if (Loc.isInvalid() && RD)
8465+
Loc = RD->getLocation();
8466+
84578467
if (Ctx) {
84588468
if (Ctx->isFileContext())
84598469
return false;
8460-
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Ctx)) {
8470+
8471+
if (RD) {
84618472
// C++ [temp.mem]p2:
84628473
// A local class shall not have member templates.
8463-
if (RD->isLocalClass())
8464-
return Diag(TemplateParams->getTemplateLoc(),
8465-
diag::err_template_inside_local_class)
8466-
<< TemplateParams->getSourceRange();
8467-
else
8474+
if (RD->isLocalClass()) {
8475+
// check if this is an abbreviated function template (C++20 auto params).
8476+
// If so, we want to emit the 'auto not allowed' diagnostic instead.
8477+
bool isAbbrev = false;
8478+
8479+
// find the enclosing function and check for auto parameters.
8480+
{
8481+
const Decl *d = dyn_cast<Decl>(Ctx);
8482+
const DeclContext *dc = d ? d->getDeclContext() : nullptr;
8483+
while (dc && !dc->isFunctionOrMethod())
8484+
dc = dc->getParent();
8485+
8486+
if (dc && dc->isFunctionOrMethod()) {
8487+
if (const FunctionDecl *fd = dyn_cast<FunctionDecl>(
8488+
dc->getRedeclContext()->getPrimaryContext())) {
8489+
for (const ParmVarDecl *p : fd->parameters()) {
8490+
if (p && p->getType()->getAs<AutoType>()) {
8491+
isAbbrev = true;
8492+
break;
8493+
}
8494+
}
8495+
}
8496+
8497+
// check function templates in this context.
8498+
if (!isAbbrev) {
8499+
for (Decl *d : dc->decls()) {
8500+
if (const FunctionTemplateDecl *ftd = dyn_cast<FunctionTemplateDecl>(d)) {
8501+
if (const FunctionDecl *fd = ftd->getTemplatedDecl()) {
8502+
for (const ParmVarDecl *p : fd->parameters()) {
8503+
if (p && p->getType()->getAs<AutoType>()) {
8504+
isAbbrev = true;
8505+
break;
8506+
}
8507+
}
8508+
if (isAbbrev) break;
8509+
}
8510+
}
8511+
}
8512+
}
8513+
}
8514+
}
8515+
8516+
// fallback check methods of the local class itself.
8517+
if (!isAbbrev) {
8518+
for (const CXXMethodDecl *m : RD->methods()) {
8519+
for (const ParmVarDecl *p : m->parameters()) {
8520+
if (p && p->getType()->getAs<AutoType>()) {
8521+
isAbbrev = true;
8522+
break;
8523+
}
8524+
}
8525+
if (isAbbrev)
8526+
break;
8527+
}
8528+
}
8529+
8530+
if (Loc.isInvalid())
8531+
Loc = TemplateParams->getSourceRange().getBegin();
8532+
8533+
if (isAbbrev)
8534+
return Diag(Loc, diag::err_auto_not_allowed);
8535+
8536+
return Diag(Loc, diag::err_template_inside_local_class)
8537+
<< TemplateParams->getSourceRange();
8538+
} else
84688539
return false;
84698540
}
84708541
}
84718542

8472-
return Diag(TemplateParams->getTemplateLoc(),
8473-
diag::err_template_outside_namespace_or_class_scope)
8474-
<< TemplateParams->getSourceRange();
8475-
}
8543+
if (Loc.isInvalid())
8544+
Loc = TemplateParams->getSourceRange().getBegin();
84768545

8546+
return Diag(Loc, diag::err_template_outside_namespace_or_class_scope)
8547+
<< TemplateParams->getSourceRange();
8548+
}
84778549
/// Determine what kind of template specialization the given declaration
84788550
/// is.
84798551
static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
2+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s
3+
4+
void top_fun(auto x) { }
5+
// CHECK17: {{.+}}:[[@LINE-1]]:14: error: 'auto' not allowed in function prototype
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK20
2+
// RUN: not %clang_cc1 -std=c++23 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK23
3+
// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
4+
5+
int main() {
6+
struct A {
7+
void foo(auto x) {}
8+
// CHECK20: {{.+}}:[[@LINE-2]]:3: error: templates cannot be declared inside of a local class
9+
// CHECK23: {{.+}}:[[@LINE-3]]:3: error: templates cannot be declared inside of a local class
10+
// CHECK17: {{.+}}:[[@LINE-3]]:14: error: 'auto' not allowed in function prototype
11+
};
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
2+
// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK20
3+
// RUN: not %clang_cc1 -std=c++23 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK23
4+
5+
int main() {
6+
struct Local {
7+
// CHECK20: {{.+}}:[[@LINE-1]]:3: error: templates cannot be declared inside of a local class
8+
// CHECK23: {{.+}}:[[@LINE-2]]:3: error: templates cannot be declared inside of a local class
9+
void mem(auto x) {}
10+
// CHECK17: {{.+}}:[[@LINE-1]]:14: error: 'auto' not allowed in function prototype
11+
};
12+
}

0 commit comments

Comments
 (0)