-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[clang] simplify placeholder type deduction for constant template parameters #160439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] simplify placeholder type deduction for constant template parameters #160439
Conversation
|
@llvm/pr-subscribers-clang-modules @llvm/pr-subscribers-clang Author: Matheus Izvekov (mizvekov) ChangesThis 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. Full diff: https://github.com/llvm/llvm-project/pull/160439.diff 4 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 70c82b090107a..1932135ceed1b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -292,7 +292,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..5a48ba0c344b7 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(
@@ -8595,6 +8588,11 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
continue;
}
+ if (isa<RecoveryExpr>(ArgExpr)) {
+ HasError = true;
+ continue;
+ }
+
// C++ [temp.class.spec]p9:
// Within the argument list of a class template partial
// specialization, the following restrictions apply:
@@ -8638,7 +8636,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 b0d2554d819cb..3e3670b755727 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5267,18 +5267,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;
@@ -5328,9 +5316,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() &&
@@ -5352,12 +5340,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) {
@@ -5371,7 +5359,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);
}
@@ -5395,7 +5383,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA);
TDK != TemplateDeductionResult::Success) {
Result = QualType();
- return DeductionFailed(TDK);
+ return TDK;
}
}
@@ -5417,13 +5405,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@-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@-1 {{different template parameters}}
int n;
|
erichkeane
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give others some time with this, but I think we need to do a better job with figuring out what that diagnostic is trying to say, vs leaving empty quotes.
Yes, pre-existing, the file is riddled with those. It's just that the template parameters are anonymous, it's not a thing super likely in user code. |
Ah, urgh... would still be nice to see if we could come up with SOME way of doing 'template-parameter-0' sorta stuff here. Still a little subtle of a patch for me to be confident doing the only approve, so give the othertwo a chance. |
The best solution is to not try to print the names of entities which might be anonymous, we can point to them in source code instead. I have a patch that has been dormant for a while which does that, I'll eventually get back to it. |
87c8359 to
96ca19a
Compare
09539af to
d45e9e3
Compare
96ca19a to
8c6ea05
Compare
8c6ea05 to
21fe7ee
Compare
…ameters 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.
40f248a to
30da2b4
Compare
…ameters (llvm#160439) 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.
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.