[clang] [clang] simplify placeholder type deduction for constant template parameters (PR #160439)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 27 17:36:55 PDT 2025
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/160439
>From 30da2b4136d5ebcb726446b18a6d675ea075785b Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Wed, 24 Sep 2025 00:21:01 -0300
Subject: [PATCH] [clang] simplify placeholder type deduction for constant
template parameters
This makes the deduction for dependent types operate in more similar ways
to the non-dependent one, such as when matching template template parameters,
making errors in those generate similar diagnostics to the non-dependent ones.
This also removes some superfluous implicit casts, simplifying the resulting
AST a little bit.
---
clang/docs/ReleaseNotes.rst | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 67 +++++++++----------
clang/lib/Sema/SemaTemplateDeduction.cpp | 32 ++++-----
.../SemaTemplate/temp_arg_template_p0522.cpp | 10 +--
4 files changed, 51 insertions(+), 61 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 977396e249622..6cfe42b5dc9f8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -299,7 +299,8 @@ Improvements to Clang's diagnostics
"format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness [-Wformat-signedness]"
"signedness of format specifier 'u' is incompatible with 'c' [-Wformat-signedness]"
and the API-visible diagnostic id will be appropriate.
-
+- Clang now produces better diagnostics for template template parameter matching
+ involving 'auto' template parameters.
- Fixed false positives in ``-Waddress-of-packed-member`` diagnostics when
potential misaligned members get processed before they can get discarded.
(#GH144729)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index f051a246f954f..91e4dc8a2d039 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7068,22 +7068,8 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// If the parameter type somehow involves auto, deduce the type now.
DeducedType *DeducedT = ParamType->getContainedDeducedType();
- if (getLangOpts().CPlusPlus17 && DeducedT && !DeducedT->isDeduced()) {
- // During template argument deduction, we allow 'decltype(auto)' to
- // match an arbitrary dependent argument.
- // FIXME: The language rules don't say what happens in this case.
- // FIXME: We get an opaque dependent type out of decltype(auto) if the
- // expression is merely instantiation-dependent; is this enough?
- if (DeductionArg->isTypeDependent()) {
- auto *AT = dyn_cast<AutoType>(DeducedT);
- if (AT && AT->isDecltypeAuto()) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
- CanonicalConverted = TemplateArgument(
- Context.getCanonicalTemplateArgument(SugaredConverted));
- return Arg;
- }
- }
-
+ bool IsDeduced = DeducedT && !DeducedT->isDeduced();
+ if (IsDeduced) {
// When checking a deduced template argument, deduce from its type even if
// the type is dependent, in order to check the types of non-type template
// arguments line up properly in partial ordering.
@@ -7112,17 +7098,21 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// along with the other associated constraints after
// checking the template argument list.
/*IgnoreConstraints=*/true);
- if (Result == TemplateDeductionResult::AlreadyDiagnosed) {
- return ExprError();
- } else if (Result != TemplateDeductionResult::Success) {
- if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
- Diag(Arg->getExprLoc(),
- diag::err_non_type_template_parm_type_deduction_failure)
- << Param->getDeclName() << NTTP->getType() << Arg->getType()
- << Arg->getSourceRange();
+ if (Result != TemplateDeductionResult::Success) {
+ ParamType = TSI->getType();
+ if (StrictCheck || !DeductionArg->isTypeDependent()) {
+ if (Result == TemplateDeductionResult::AlreadyDiagnosed)
+ return ExprError();
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))
+ Diag(Arg->getExprLoc(),
+ diag::err_non_type_template_parm_type_deduction_failure)
+ << Param->getDeclName() << NTTP->getType() << Arg->getType()
+ << Arg->getSourceRange();
+ NoteTemplateParameterLocation(*Param);
+ return ExprError();
}
- NoteTemplateParameterLocation(*Param);
- return ExprError();
+ ParamType = SubstAutoTypeDependent(ParamType);
+ assert(!ParamType.isNull() && "substituting DependentTy can't fail");
}
}
// CheckNonTypeTemplateParameterType will produce a diagnostic if there's
@@ -7144,14 +7134,16 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// type-dependent, there's nothing we can check now.
if (ParamType->isDependentType() || DeductionArg->isTypeDependent()) {
// Force the argument to the type of the parameter to maintain invariants.
- ExprResult E = ImpCastExprToType(
- DeductionArg, ParamType.getNonLValueExprType(Context), CK_Dependent,
- ParamType->isLValueReferenceType() ? VK_LValue
- : ParamType->isRValueReferenceType() ? VK_XValue
- : VK_PRValue);
- if (E.isInvalid())
- return ExprError();
- setDeductionArg(E.get());
+ if (!IsDeduced) {
+ ExprResult E = ImpCastExprToType(
+ DeductionArg, ParamType.getNonLValueExprType(Context), CK_Dependent,
+ ParamType->isLValueReferenceType() ? VK_LValue
+ : ParamType->isRValueReferenceType() ? VK_XValue
+ : VK_PRValue);
+ if (E.isInvalid())
+ return ExprError();
+ setDeductionArg(E.get());
+ }
SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
CanonicalConverted = TemplateArgument(
Context.getCanonicalTemplateArgument(SugaredConverted));
@@ -8555,6 +8547,7 @@ static SourceRange findTemplateParameter(unsigned Depth, TypeLoc TL) {
static bool CheckNonTypeTemplatePartialSpecializationArgs(
Sema &S, SourceLocation TemplateNameLoc, NonTypeTemplateParmDecl *Param,
const TemplateArgument *Args, unsigned NumArgs, bool IsDefaultArgument) {
+ bool HasError = false;
for (unsigned I = 0; I != NumArgs; ++I) {
if (Args[I].getKind() == TemplateArgument::Pack) {
if (CheckNonTypeTemplatePartialSpecializationArgs(
@@ -8569,6 +8562,10 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
continue;
Expr *ArgExpr = Args[I].getAsExpr();
+ if (ArgExpr->containsErrors()) {
+ HasError = true;
+ continue;
+ }
// We can have a pack expansion of any of the bullets below.
if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(ArgExpr))
@@ -8638,7 +8635,7 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
}
}
- return false;
+ return HasError;
}
bool Sema::CheckTemplatePartialSpecializationArgs(
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 962fa4da75946..f6ee7452c2f9a 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5262,18 +5262,6 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
SmallVector<DeducedTemplateArgument, 1> Deduced;
Deduced.resize(1);
- // If deduction failed, don't diagnose if the initializer is dependent; it
- // might acquire a matching type in the instantiation.
- auto DeductionFailed = [&](TemplateDeductionResult TDK) {
- if (Init->isTypeDependent()) {
- Result =
- SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type);
- assert(!Result.isNull() && "substituting DependentTy can't fail");
- return TemplateDeductionResult::Success;
- }
- return TDK;
- };
-
SmallVector<OriginalCallArg, 4> OriginalCallArgs;
QualType DeducedType;
@@ -5323,9 +5311,9 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
Diag(Info.getLocation(), diag::err_auto_inconsistent_deduction)
<< Info.FirstArg << Info.SecondArg << DeducedFromInitRange
<< Init->getSourceRange();
- return DeductionFailed(TemplateDeductionResult::AlreadyDiagnosed);
+ return TemplateDeductionResult::AlreadyDiagnosed;
}
- return DeductionFailed(TDK);
+ return TDK;
}
if (DeducedFromInitRange.isInvalid() &&
@@ -5347,12 +5335,12 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
OriginalCallArgs,
/*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0, FailedTSC);
TDK != TemplateDeductionResult::Success)
- return DeductionFailed(TDK);
+ return TDK;
}
// Could be null if somehow 'auto' appears in a non-deduced context.
if (Deduced[0].getKind() != TemplateArgument::Type)
- return DeductionFailed(TemplateDeductionResult::Incomplete);
+ return TemplateDeductionResult::Incomplete;
DeducedType = Deduced[0].getAsType();
if (InitList) {
@@ -5366,7 +5354,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
if (!Context.hasSameType(DeducedType, Result)) {
Info.FirstArg = Result;
Info.SecondArg = DeducedType;
- return DeductionFailed(TemplateDeductionResult::Inconsistent);
+ return TemplateDeductionResult::Inconsistent;
}
DeducedType = Context.getCommonSugaredType(Result, DeducedType);
}
@@ -5390,7 +5378,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA);
TDK != TemplateDeductionResult::Success) {
Result = QualType();
- return DeductionFailed(TDK);
+ return TDK;
}
}
@@ -5412,13 +5400,17 @@ TypeSourceInfo *Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto,
}
QualType Sema::SubstAutoTypeDependent(QualType TypeWithAuto) {
- return SubstituteDeducedTypeTransform(*this, DependentAuto{false})
+ return SubstituteDeducedTypeTransform(
+ *this,
+ DependentAuto{/*IsPack=*/isa<PackExpansionType>(TypeWithAuto)})
.TransformType(TypeWithAuto);
}
TypeSourceInfo *
Sema::SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto) {
- return SubstituteDeducedTypeTransform(*this, DependentAuto{false})
+ return SubstituteDeducedTypeTransform(
+ *this, DependentAuto{/*IsPack=*/isa<PackExpansionType>(
+ TypeWithAuto->getType())})
.TransformType(TypeWithAuto);
}
diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index d8a81bb363112..60d98a653ff02 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -83,11 +83,11 @@ namespace DependentType {
namespace Auto {
template<template<int> typename T> struct TInt {}; // #TInt
template<template<int*> typename T> struct TIntPtr {}; // #TIntPtr
- template<template<auto> typename T> struct TAuto {};
+ template<template<auto> typename T> struct TAuto {}; // #TAuto
template<template<auto*> typename T> struct TAutoPtr {};
- template<template<decltype(auto)> typename T> struct TDecltypeAuto {};
+ template<template<decltype(auto)> typename T> struct TDecltypeAuto {}; // #TDecltypeAuto
template<auto> struct Auto;
- template<auto*> struct AutoPtr; // #AutoPtr
+ template<auto*> struct AutoPtr;
template<decltype(auto)> struct DecltypeAuto;
template<int> struct Int;
template<int*> struct IntPtr;
@@ -108,7 +108,7 @@ namespace Auto {
TIntPtr<IntPtr> ipip;
TAuto<Auto> aa;
- TAuto<AutoPtr> aap; // expected-error@#AutoPtr {{could not match 'auto *' against 'auto'}}
+ TAuto<AutoPtr> aap; // expected-error@#TAuto {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'auto'}}
// expected-note at -1 {{different template parameters}}
TAuto<Int> ai; // FIXME: ill-formed (?)
TAuto<IntPtr> aip; // FIXME: ill-formed (?)
@@ -130,7 +130,7 @@ namespace Auto {
// parameters (such as 'user-defined-type &') that are not valid 'auto'
// parameters.
TDecltypeAuto<Auto> daa;
- TDecltypeAuto<AutoPtr> daap; // expected-error@#AutoPtr {{could not match 'auto *' against 'decltype(auto)'}}
+ TDecltypeAuto<AutoPtr> daap; // expected-error@#TDecltypeAuto {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'decltype(auto)'}}
// expected-note at -1 {{different template parameters}}
int n;
More information about the cfe-commits
mailing list