diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7185a4d6cab058..659f1d6df39e46 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5046,8 +5046,8 @@ def err_unexpanded_parameter_pack : Error< "size|static assertion|fixed underlying type|enumerator value|" "using declaration|friend declaration|qualifier|initializer|default argument|" "non-type template parameter type|exception type|partial specialization|" - "__if_exists name|__if_not_exists name|lambda|block|type constraint}0 " - "contains%plural{0: an|:}1 unexpanded parameter pack" + "__if_exists name|__if_not_exists name|lambda|block|type constraint|" + "requirement}0 contains%plural{0: an|:}1 unexpanded parameter pack" "%plural{0:|1: %2|2:s %2 and %3|:s %2, %3, ...}1">; def err_pack_expansion_without_parameter_packs : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b88e5578c1140c..09e4c6743efc9e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7760,11 +7760,14 @@ class Sema final { /// Lambda expression. UPPC_Lambda, - /// Block expression, + /// Block expression. UPPC_Block, - /// A type constraint, - UPPC_TypeConstraint + /// A type constraint. + UPPC_TypeConstraint, + + // A requirement in a requires-expression. + UPPC_Requirement, }; /// Diagnose unexpanded parameter packs. @@ -7803,6 +7806,15 @@ class Sema final { bool DiagnoseUnexpandedParameterPack(Expr *E, UnexpandedParameterPackContext UPPC = UPPC_Expression); + /// If the given requirees-expression contains an unexpanded reference to one + /// of its own parameter packs, diagnose the error. + /// + /// \param RE The requiress-expression that is being checked for unexpanded + /// parameter packs. + /// + /// \returns true if an error occurred, false otherwise. + bool DiagnoseUnexpandedParameterPackInRequiresExpr(RequiresExpr *RE); + /// If the given nested-name-specifier contains an unexpanded /// parameter pack, diagnose the error. /// diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index fe5ba6a5e87f60..335ee95715d0ce 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8650,6 +8650,9 @@ Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc, ArrayRef LocalParameters, ArrayRef Requirements, SourceLocation ClosingBraceLoc) { - return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters, - Requirements, ClosingBraceLoc); + auto *RE = RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters, + Requirements, ClosingBraceLoc); + if (DiagnoseUnexpandedParameterPackInRequiresExpr(RE)) + return ExprError(); + return RE; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5d037e58de51e8..d63d307b9ab7c0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8476,6 +8476,9 @@ Decl *Sema::ActOnConceptDefinition(Scope *S, return nullptr; } + if (DiagnoseUnexpandedParameterPack(ConstraintExpr)) + return nullptr; + ConceptDecl *NewDecl = ConceptDecl::Create(Context, DC, NameLoc, Name, TemplateParameterLists.front(), ConstraintExpr); diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index c95d67b7c73201..623d808b59f68c 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -408,6 +408,29 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E, return DiagnoseUnexpandedParameterPacks(E->getBeginLoc(), UPPC, Unexpanded); } +bool Sema::DiagnoseUnexpandedParameterPackInRequiresExpr(RequiresExpr *RE) { + if (!RE->containsUnexpandedParameterPack()) + return false; + + SmallVector Unexpanded; + CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(RE); + assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); + + // We only care about unexpanded references to the RequiresExpr's own + // parameter packs. + auto Parms = RE->getLocalParameters(); + llvm::SmallPtrSet ParmSet(Parms.begin(), Parms.end()); + SmallVector UnexpandedParms; + for (auto Parm : Unexpanded) + if (ParmSet.contains(Parm.first.dyn_cast())) + UnexpandedParms.push_back(Parm); + if (UnexpandedParms.empty()) + return false; + + return DiagnoseUnexpandedParameterPacks(RE->getBeginLoc(), UPPC_Requirement, + UnexpandedParms); +} + bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS, UnexpandedParameterPackContext UPPC) { // C++0x [temp.variadic]p5: diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp new file mode 100644 index 00000000000000..db154a9a0f5b51 --- /dev/null +++ b/clang/test/SemaTemplate/concepts.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +namespace PR47043 { + template concept True = true; + template concept AllTrue1 = True; // expected-error {{expression contains unexpanded parameter pack 'T'}} + template concept AllTrue2 = (True && ...); + static_assert(AllTrue2); +} + +namespace PR47025 { + template concept AllAddable1 = requires(T ...t) { (void(t + 1), ...); }; + template concept AllAddable2 = (requires(T ...t) { (t + 1); } && ...); // expected-error {{requirement contains unexpanded parameter pack 't'}} + template concept AllAddable3 = (requires(T t) { (t + 1); } && ...); + template concept AllAddable4 = requires(T t) { (t + 1); }; // expected-error {{expression contains unexpanded parameter pack 'T'}} + template concept AllAddable5 = requires(T t) { (void(t + 1), ...); }; // expected-error {{does not contain any unexpanded}} + template concept AllAddable6 = (requires { (T() + 1); } && ...); + template concept AllAddable7 = requires { (T() + 1); }; // expected-error {{expression contains unexpanded parameter pack 'T'}} + + static_assert(AllAddable1); + static_assert(AllAddable3); + static_assert(AllAddable6); + static_assert(!AllAddable1); + static_assert(!AllAddable3); + static_assert(!AllAddable6); +}