[clang] e1aa528 - Handle unexpanded packs appearing in type-constraints.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed May 12 18:45:56 PDT 2021
Author: Richard Smith
Date: 2021-05-12T18:45:34-07:00
New Revision: e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d
URL: https://github.com/llvm/llvm-project/commit/e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d
DIFF: https://github.com/llvm/llvm-project/commit/e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d.diff
LOG: Handle unexpanded packs appearing in type-constraints.
For a type-constraint in a lambda signature, this makes the lambda
contain an unexpanded pack; for requirements in a requires-expressions
it makes the requires-expression contain an unexpanded pack; otherwise
it's invalid.
Added:
Modified:
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/Type.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaType.cpp
clang/test/SemaTemplate/concepts.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 0cd8f00c623b..cbaa287f225a 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -160,9 +160,7 @@ class TemplateParameterList final
/// Determine whether this template parameter list contains an
/// unexpanded parameter pack.
- bool containsUnexpandedParameterPack() const {
- return ContainsUnexpandedParameterPack;
- }
+ bool containsUnexpandedParameterPack() const;
/// Determine whether this template parameter list contains a parameter pack.
bool hasParameterPack() const {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 9499564e46e0..4770a2fd42a2 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -481,6 +481,8 @@ template <typename Derived> class RecursiveASTVisitor {
template <typename T>
bool TraverseDeclTemplateParameterLists(T *D);
+ bool TraverseTemplateTypeParamDeclConstraints(const TemplateTypeParmDecl *D);
+
bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
unsigned Count);
bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
@@ -658,8 +660,14 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
// As a syntax visitor, by default we want to ignore declarations for
// implicit declarations (ones not typed explicitly by the user).
- if (!getDerived().shouldVisitImplicitCode() && D->isImplicit())
+ if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
+ // For an implicit template type parameter, its type constraints are not
+ // implicit and are not represented anywhere else. We still need to visit
+ // them.
+ if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
+ return TraverseTemplateTypeParamDeclConstraints(TTPD);
return true;
+ }
switch (D->getKind()) {
#define ABSTRACT_DECL(DECL)
@@ -1779,10 +1787,9 @@ DEF_TRAVERSE_DECL(BuiltinTemplateDecl, {
TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
})
-DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
- // D is the "T" in something like "template<typename T> class vector;"
- if (D->getTypeForDecl())
- TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints(
+ const TemplateTypeParmDecl *D) {
if (const auto *TC = D->getTypeConstraint()) {
if (Expr *IDC = TC->getImmediatelyDeclaredConstraint()) {
TRY_TO(TraverseStmt(IDC));
@@ -1794,6 +1801,14 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
TRY_TO(TraverseConceptReference(*TC));
}
}
+ return true;
+}
+
+DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
+ // D is the "T" in something like "template<typename T> class vector;"
+ if (D->getTypeForDecl())
+ TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+ TRY_TO(TraverseTemplateTypeParamDeclConstraints(D));
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc()));
})
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 53ff0f1e634d..135da703c4df 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7455,6 +7455,11 @@ class Sema final {
TemplateIdAnnotation *TypeConstraint,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc);
+ bool BuildTypeConstraint(const CXXScopeSpec &SS,
+ TemplateIdAnnotation *TypeConstraint,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc,
+ bool AllowUnexpandedPack);
bool AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index ba4a5359f937..e5b1833a02f2 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -69,12 +69,6 @@ TemplateParameterList::TemplateParameterList(const ASTContext& C,
TTP->getTemplateParameters()->containsUnexpandedParameterPack())
ContainsUnexpandedParameterPack = true;
} else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {
- // FIXME: The type parameter might have a constraint that has not been
- // attached yet. If so, we can compute the wrong value for
- // 'ContainsUnexpandedParameterPack' here. Note that this only happens
- // for implicit parameters, for which the ParmVarDecl will correctly
- // track that it contains an unexpanded parameter pack, so this should
- // not be problematic in practice.
if (const TypeConstraint *TC = TTP->getTypeConstraint()) {
if (TC->getImmediatelyDeclaredConstraint()
->containsUnexpandedParameterPack())
@@ -95,6 +89,30 @@ TemplateParameterList::TemplateParameterList(const ASTContext& C,
}
}
+bool TemplateParameterList::containsUnexpandedParameterPack() const {
+ if (ContainsUnexpandedParameterPack)
+ return true;
+ if (!HasConstrainedParameters)
+ return false;
+
+ // An implicit constrained parameter might have had a use of an unexpanded
+ // pack added to it after the template parameter list was created. All
+ // implicit parameters are at the end of the parameter list.
+ for (const NamedDecl *Param : llvm::reverse(asArray())) {
+ if (!Param->isImplicit())
+ break;
+
+ if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
+ const auto *TC = TTP->getTypeConstraint();
+ if (TC && TC->getImmediatelyDeclaredConstraint()
+ ->containsUnexpandedParameterPack())
+ return true;
+ }
+ }
+
+ return false;
+}
+
TemplateParameterList *
TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 611c30d9c767..d58cb43b6e7b 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4397,8 +4397,8 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
if (TypeConstraintConcept) {
TemplateArgument *ArgBuffer = getArgBuffer();
for (const TemplateArgument &Arg : TypeConstraintArgs) {
- addDependence(toTypeDependence(
- Arg.getDependence() & TemplateArgumentDependence::UnexpandedPack));
+ addDependence(
+ toSyntacticDependence(toTypeDependence(Arg.getDependence())));
new (ArgBuffer++) TemplateArgument(Arg);
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 4a4c8116d6f1..b31c484b1170 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8629,8 +8629,9 @@ Sema::ActOnCompoundRequirement(
/*ParameterPack=*/false,
/*HasTypeConstraint=*/true);
- if (ActOnTypeConstraint(SS, TypeConstraint, TParam,
- /*EllpsisLoc=*/SourceLocation()))
+ if (BuildTypeConstraint(SS, TypeConstraint, TParam,
+ /*EllpsisLoc=*/SourceLocation(),
+ /*AllowUnexpandedPack=*/true))
// Just produce a requirement with no type requirements.
return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {});
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index dfd4297bbc8b..ae368ba75291 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1105,8 +1105,17 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS,
TemplateIdAnnotation *TypeConstr,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc) {
- ConceptDecl *CD =
- cast<ConceptDecl>(TypeConstr->Template.get().getAsTemplateDecl());
+ return BuildTypeConstraint(SS, TypeConstr, ConstrainedParameter, EllipsisLoc,
+ false);
+}
+
+bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
+ TemplateIdAnnotation *TypeConstr,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc,
+ bool AllowUnexpandedPack) {
+ TemplateName TN = TypeConstr->Template.get();
+ ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
// C++2a [temp.param]p4:
// [...] The concept designated by a type-constraint shall be a type
@@ -1126,15 +1135,24 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS,
return true;
}
+ DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
+ TypeConstr->TemplateNameLoc);
+
TemplateArgumentListInfo TemplateArgs;
if (TypeConstr->LAngleLoc.isValid()) {
TemplateArgs =
makeTemplateArgumentListInfo(*this, *TypeConstr);
+
+ if (EllipsisLoc.isInvalid() && !AllowUnexpandedPack) {
+ for (TemplateArgumentLoc Arg : TemplateArgs.arguments()) {
+ if (DiagnoseUnexpandedParameterPack(Arg, UPPC_TypeConstraint))
+ return true;
+ }
+ }
}
return AttachTypeConstraint(
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(),
- DeclarationNameInfo(DeclarationName(TypeConstr->Name),
- TypeConstr->TemplateNameLoc), CD,
+ ConceptName, CD,
TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
ConstrainedParameter, EllipsisLoc);
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 9801dd6ffb66..e1c5d3367425 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -3223,31 +3223,52 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
// extract its type constraints to attach to the template parameter.
AutoTypeLoc AutoLoc = TrailingTSI->getTypeLoc().getContainedAutoTypeLoc();
TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc());
- for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx)
+ bool Invalid = false;
+ for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) {
+ if (D.getEllipsisLoc().isInvalid() && !Invalid &&
+ S.DiagnoseUnexpandedParameterPack(AutoLoc.getArgLoc(Idx),
+ Sema::UPPC_TypeConstraint))
+ Invalid = true;
TAL.addArgument(AutoLoc.getArgLoc(Idx));
+ }
- S.AttachTypeConstraint(AutoLoc.getNestedNameSpecifierLoc(),
- AutoLoc.getConceptNameInfo(),
- AutoLoc.getNamedConcept(),
- AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
- InventedTemplateParam, D.getEllipsisLoc());
+ if (!Invalid) {
+ S.AttachTypeConstraint(
+ AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(),
+ AutoLoc.getNamedConcept(),
+ AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
+ InventedTemplateParam, D.getEllipsisLoc());
+ }
} else {
// The 'auto' appears in the decl-specifiers; we've not finished forming
// TypeSourceInfo for it yet.
TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId();
TemplateArgumentListInfo TemplateArgsInfo;
+ bool Invalid = false;
if (TemplateId->LAngleLoc.isValid()) {
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo);
+
+ if (D.getEllipsisLoc().isInvalid()) {
+ for (TemplateArgumentLoc Arg : TemplateArgsInfo.arguments()) {
+ if (S.DiagnoseUnexpandedParameterPack(Arg,
+ Sema::UPPC_TypeConstraint)) {
+ Invalid = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!Invalid) {
+ S.AttachTypeConstraint(
+ D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
+ DeclarationNameInfo(DeclarationName(TemplateId->Name),
+ TemplateId->TemplateNameLoc),
+ cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()),
+ TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
+ InventedTemplateParam, D.getEllipsisLoc());
}
- S.AttachTypeConstraint(
- D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
- DeclarationNameInfo(DeclarationName(TemplateId->Name),
- TemplateId->TemplateNameLoc),
- cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()),
- TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
- InventedTemplateParam, D.getEllipsisLoc());
}
}
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 753a84c35e1f..01a2618d0ab1 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -74,3 +74,55 @@ namespace PR50306 {
template void f<char>(); // OK
template void f<int>(); // expected-note {{in instantiation of}}
}
+
+namespace PackInTypeConstraint {
+ template<typename T, typename U> concept C = sizeof(T) == sizeof(int); // expected-note 3{{}}
+
+ template<typename ...T, C<T> U> void h1(); // expected-error {{type constraint contains unexpanded parameter pack 'T'}}
+ template<typename ...T, C<T> ...U> void h2();
+ template<typename ...T> void h3(C<T> auto); // expected-error {{type constraint contains unexpanded parameter pack 'T'}}
+ template<typename ...T> void h4(C<T> auto...);
+
+ template<typename ...T> void f1() {
+ []<C<T> U>(U u){}(T()); // expected-error {{unexpanded parameter pack 'T'}}
+ }
+ template<typename ...T> void f2() {
+ ([]<C<T> U>(U u){}(T()), ...); // expected-error {{no match}} expected-note 2{{}}
+ }
+ template void f2<int, int, int>(); // OK
+ template void f2<int, char, double>(); // expected-note {{in instantiation of}}
+ void f3() {
+ ([]<typename ...T, C<T> U>(U u){}(0), // expected-error {{type constraint contains unexpanded parameter pack 'T'}}
+ ...); // expected-error {{does not contain any unexpanded}}
+ }
+
+ template<typename ...T> void g1() {
+ [](C<T> auto){}(T()); // expected-error {{expression contains unexpanded parameter pack 'T'}}
+ }
+ template<typename ...T> void g2() {
+ ([](C<T> auto){}(T()), ...); // expected-error {{no matching function}} expected-note {{constraints not satisfied}} expected-note {{because}}
+ }
+ template void g2<int, int, int>(); // OK
+ template void g2<int, char, double>(); // expected-note {{in instantiation of}}
+ void g3() {
+ ([]<typename ...T>(C<T> auto){}(1), // expected-error {{type constraint contains unexpanded parameter pack 'T'}}
+ ...); // expected-error {{does not contain any unexpanded}}
+ }
+
+ template<typename ...T> void g4() {
+ []() -> C<T> auto{ return T(); }(); // expected-error {{expression contains unexpanded parameter pack 'T'}}
+ }
+ template<typename ...T> void g5() {
+ ([]() -> C<T> auto{ // expected-error-re {{deduced type {{.*}} does not satisfy}}
+ return T();
+ }(), ...);
+ }
+ template void g5<int, int, int>(); // OK
+ template void g5<int, char, double>(); // expected-note {{in instantiation of}}
+ void g6() {
+ ([]<typename ...T>() -> C<T> auto{ // expected-error {{declaration type contains unexpanded parameter pack 'T'}}
+ return T(); // expected-error {{expression contains unexpanded parameter pack 'T'}}
+ }(),
+ ...); // expected-error {{does not contain any unexpanded}}
+ }
+}
More information about the cfe-commits
mailing list