saar.raz created this revision.
saar.raz added reviewers: hubert.reinterpretcast, rsmith, nwilson, changyu,
faisalv.
Herald added a subscriber: cfe-commits.
- Associated constraints (requires clauses, currently) are now enforced when
instantiating/specializing templates and when considering partial
specializations and function overloads.
- Elaborated diagnostics give helpful insight as to why the constraints were
not satisfied.
This patch depends upon https://reviews.llvm.org/D41284
Repository:
rC Clang
https://reviews.llvm.org/D41569
Files:
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
include/clang/Sema/TemplateDeduction.h
lib/AST/ExprCXX.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateDeduction.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+namespace class_templates
+{
+ template<typename T, typename U> requires sizeof(T) >= 4 // expected-note {{'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
+ struct is_same { static constexpr bool value = false; };
+
+ template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
+ struct is_same<T*, T*> { static constexpr bool value = true; };
+
+ static_assert(!is_same<char*, char*>::value);
+ static_assert(!is_same<short*, short*>::value);
+ static_assert(is_same<int*, int*>::value);
+ static_assert(is_same<char, char>::value); // expected-error {{constraints not satisfied for class template 'is_same' [with T = char, U = char], because:}}
+}
+
+namespace variable_templates
+{
+ template<typename T, typename U> requires sizeof(T) >= 4
+ constexpr bool is_same_v = false;
+
+ template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
+ constexpr bool is_same_v<T*, T*> = true;
+
+ static_assert(!is_same_v<char*, char*>);
+ static_assert(!is_same_v<short*, short*>);
+ static_assert(is_same_v<int*, int*>);
+}
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T> requires sizeof(T) >= 2 // expected-note{{'sizeof(char) >= 2' (1 >= 2) evaluated to false}}
+struct A {
+ static constexpr int value = sizeof(T);
+};
+
+static_assert(A<int>::value == 4);
+static_assert(A<char>::value == 1); // expected-error{{constraints not satisfied for class template 'A' [with T = char], because:}}
+
+template<typename T, typename U>
+ requires sizeof(T) != sizeof(U) // expected-note{{'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}
+ && sizeof(T) >= 4 // expected-note{{'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
+constexpr int SizeDiff = sizeof(T) > sizeof(U) ? sizeof(T) - sizeof(U) : sizeof(U) - sizeof(T);
+
+static_assert(SizeDiff<int, char> == 3);
+static_assert(SizeDiff<int, char[4]> == 0); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = int, U = char [4]], because:}}
+static_assert(SizeDiff<char, int> == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int], because:}}
+
+template<typename... Ts>
+ requires ((sizeof(Ts) == 4) || ...) // expected-note{{'sizeof(char) == 4' (1 == 4) evaluated to false}} expected-note{{'sizeof(long long) == 4' (8 == 4) evaluated to false}} expected-note{{'sizeof(int [20]) == 4' (80 == 4) evaluated to false}}
+constexpr auto SumSizes = (sizeof(Ts) + ...);
+
+static_assert(SumSizes<char, long long, int> == 13);
+static_assert(SumSizes<char, long long, int[20]> == 89); // expected-error{{constraints not satisfied for variable template 'SumSizes' [with Ts = <char, long long, int [20]>], because:}}
+
+template<typename T>
+concept IsBig = sizeof(T) > 100; // expected-note{{'sizeof(int) > 100' (4 > 100) evaluated to false}}
+
+template<typename T>
+ requires IsBig<T> // expected-note{{'int' does not satisfy 'IsBig', because:}}
+using BigPtr = T*;
+
+static_assert(sizeof(BigPtr<int>)); // expected-error{{constraints not satisfied for alias template 'BigPtr' [with T = int], because:}}}}
+
+template<typename T> requires T::value // expected-note{{substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
+struct S { static constexpr bool value = true; };
+
+struct S2 { static constexpr bool value = true; };
+
+static_assert(S<int>::value); // expected-error{{constraints not satisfied for class template 'S' [with T = int], because:}}
+static_assert(S<S2>::value);
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T>
+constexpr bool is_ptr_v = false;
+
+template<typename T>
+constexpr bool is_ptr_v<T*> = true;
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T> requires is_ptr_v<T> // expected-note {{'is_ptr_v<int>' evaluated to false}} expected-note {{'is_ptr_v<char>' evaluated to false}}
+auto dereference(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}} expected-note {{candidate template ignored: constraints not satisfied [with T = char]}}
+ return *t;
+}
+
+static_assert(is_same_v<decltype(dereference<int*>(nullptr)), int>);
+static_assert(is_same_v<decltype(dereference(2)), int>); // expected-error {{no matching function for call to 'dereference'}}
+static_assert(is_same_v<decltype(dereference<char>('a')), char>); // expected-error {{no matching function for call to 'dereference'}}
+
+
+template<typename T> requires T{} + T{} // expected-note {{substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
+auto foo(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}
+ return t + t;
+}
+
+struct A { };
+
+static_assert(foo(A{})); // expected-error {{no matching function for call to 'foo'}}
\ No newline at end of file
Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
===================================================================
--- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
+++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
@@ -24,9 +24,9 @@
}
};
template<typename U>
-concept C4 = U::add(1, 2) == 3;
+concept C4 = U::add(1, 2) == 3; // expected-error {{substitution into constraint expression resulted in a non-constant expression 'B::add(1, 2) == 3'}}
static_assert(C4<A>);
-static_assert(!C4<B>); // expected-error {{concept specialization 'C4<B>' resulted in a non-constant expression 'B::add(1, 2) == 3'}}
+static_assert(!C4<B>); // expected-note {{in concept specialization 'C4<B>'}}
template<typename T, typename U>
constexpr bool is_same_v = false;
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -357,6 +357,7 @@
Record.AddTemplateArgumentLoc(TALI->getArgumentArray()[i]);
}
Record.push_back(E->isSatisfied());
+ Record.AddStmt(E->getSubstitutedConstraintExpr());
Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
}
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -624,6 +624,7 @@
ArgInfo.addArgument(Record.readTemplateArgumentLoc());
E->setTemplateArguments(/*Sema=*/nullptr, &ArgInfo);
E->setSatisfied(Record.readInt() == 1);
+ E->setSubstitutedConstraintExpr(Record.readExpr());
}
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2911,6 +2911,12 @@
Converted))
return nullptr;
+ if (SemaRef.EnsureTemplateArgumentListConstraints(InstClassTemplate,
+ Converted,
+ D->getLocation())) {
+ return nullptr;
+ }
+
// Figure out where to insert this class template explicit specialization
// in the member template's set of class template explicit specializations.
void *InsertPos = nullptr;
@@ -3032,6 +3038,11 @@
Converted))
return nullptr;
+ if (SemaRef.EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+ VarTemplate->getLocStart())){
+ return nullptr;
+ }
+
// Find the variable template specialization declaration that
// corresponds to these arguments.
void *InsertPos = nullptr;
Index: lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- lib/Sema/SemaTemplateDeduction.cpp
+++ lib/Sema/SemaTemplateDeduction.cpp
@@ -2658,6 +2658,27 @@
return Sema::TDK_Success;
}
+template<typename TemplateDeclT>
+static Sema::TemplateDeductionResult
+CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
+ ArrayRef<TemplateArgument> DeducedArgs,
+ TemplateDeductionInfo& Info) {
+ bool IsSatisfied = false;
+ ExprResult SubstitutedExpr;
+ llvm::Optional<PartialDiagnosticAt> SubstDiag;
+ if (S.CheckConstraintSatisfaction(Template,
+ Template->getAssociatedConstraints(),
+ DeducedArgs, Info.getLocation(),
+ IsSatisfied, SubstitutedExpr, SubstDiag)
+ || !IsSatisfied) {
+ if (SubstDiag)
+ Info.addSFINAEDiagnostic(SubstDiag->first, SubstDiag->second);
+ Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
+ Info.SubstitutedConstraintExpr = SubstitutedExpr;
+ return Sema::TDK_ConstraintsNotSatisfied;
+ }
+ return Sema::TDK_Success;
+}
/// \brief Perform template argument deduction to determine whether
/// the given template arguments match the given class template
@@ -2698,6 +2719,10 @@
if (Trap.hasErrorOccurred())
return Sema::TDK_SubstitutionFailure;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+ return Result;
+
return ::FinishTemplateArgumentDeduction(
*this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
}
@@ -2739,6 +2764,10 @@
if (Trap.hasErrorOccurred())
return Sema::TDK_SubstitutionFailure;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+ return Result;
+
return ::FinishTemplateArgumentDeduction(
*this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
}
@@ -2848,6 +2877,12 @@
= TemplateArgumentList::CreateCopy(Context, Builder);
Info.reset(ExplicitArgumentList);
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, FunctionTemplate,
+ ExplicitArgumentList->asArray(),
+ Info))
+ return Result;
+
// Template argument deduction and the final substitution should be
// done in the context of the templated declaration. Explicit
// argument substitution, on the other hand, needs to happen in the
@@ -3155,6 +3190,11 @@
PartialOverloading))
return Result;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, FunctionTemplate, DeducedArgs,
+ Info))
+ return Result;
+
// C++ [temp.deduct.call]p10: [DR1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
@@ -4911,6 +4951,7 @@
static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
TemplateLikeDecl *P2,
TemplateDeductionInfo &Info) {
+ // TODO: Concepts: Regard constraints
// C++ [temp.class.order]p1:
// For two class template partial specializations, the first is at least as
// specialized as the second if, given the following rewrite to two
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -2906,8 +2906,7 @@
TemplateDecl *Template = Name.getAsTemplateDecl();
if (!Template || isa<FunctionTemplateDecl>(Template) ||
- isa<VarTemplateDecl>(Template) ||
- isa<ConceptDecl>(Template)) {
+ isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(Template)) {
// We might have a substituted template template parameter pack. If so,
// build a template specialization type for it.
if (Name.getAsSubstTemplateTemplateParmPack())
@@ -2926,11 +2925,15 @@
false, Converted))
return QualType();
+ if (EnsureTemplateArgumentListConstraints(Template, Converted, TemplateLoc))
+ return QualType();
+
QualType CanonType;
bool InstantiationDependent = false;
if (TypeAliasTemplateDecl *AliasTemplate =
dyn_cast<TypeAliasTemplateDecl>(Template)) {
+
// Find the canonical type for this type alias template specialization.
TypeAliasDecl *Pattern = AliasTemplate->getTemplatedDecl();
if (Pattern->isInvalidDecl())
@@ -3521,6 +3524,10 @@
false, Converted))
return true;
+ if (EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+ TemplateNameLoc))
+ return true;
+
// Find the variable template (partial) specialization declaration that
// corresponds to these arguments.
if (IsPartialSpecialization) {
@@ -3699,6 +3706,10 @@
Converted))
return true;
+ if (EnsureTemplateArgumentListConstraints(Template, Converted,
+ TemplateNameLoc))
+ return true;
+
// Find the variable template specialization declaration that
// corresponds to these arguments.
void *InsertPos = nullptr;
@@ -4796,13 +4807,243 @@
return diagnoseArityMismatch(S, TD, Loc, Args);
}
+template<typename TemplateDeclT>
+bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+ if (!ConstraintExpr) {
+ IsSatisfied = true;
+ SubstitutedExpression.set(nullptr);
+ return false;
+ }
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(TemplateArgs);
+
+ {
+ // We do not want error diagnostics escaping here.
+ TemplateDeductionInfo Info(TemplateNameLoc);
+ Sema::InstantiatingTemplate Inst(S, TemplateNameLoc, Template, TemplateArgs,
+ Info);
+ Sema::SFINAETrap Trap(S);
+
+ SubstitutedExpression = S.SubstExpr(const_cast<Expr*>(ConstraintExpr),
+ MLTAL);
+ if (SubstitutedExpression.isInvalid() || !SubstitutedExpression.isUsable()
+ || Trap.hasErrorOccurred()) {
+ // C++2a [temp.constr.atomic]p1
+ // ...If substitution results in an invalid type or expression, the
+ // constraint is not satisfied.
+ if (Trap.hasErrorOccurred()) {
+ SubstDiag = PartialDiagnosticAt{ SourceLocation(),
+ PartialDiagnostic::NullDiagnostic() };
+ Info.takeSFINAEDiagnostic(SubstDiag.getValue());
+ }
+ IsSatisfied = false;
+ return false;
+ }
+ }
+
+ if (!S.CheckConstraintExpression(SubstitutedExpression.get())) {
+ return true;
+ }
+
+ if (SubstitutedExpression.get()->isInstantiationDependent()) {
+ IsSatisfied = true;
+ return false;
+ }
+
+ bool Satisfied = false;
+ if (!SubstitutedExpression.get()->EvaluateAsBooleanCondition(Satisfied,
+ S.Context)) {
+ // C++2a [temp.constr.atomic]p1
+ // ...E shall be a constant expression of type bool.
+ S.Diag(ConstraintExpr->getLocStart(),
+ diag::err_concept_non_constant_constraint_expression)
+ << SubstitutedExpression.get();
+ return true;
+ }
+ IsSatisfied = Satisfied;
+ return false;
+}
+
+bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+ return ::CheckConstraintSatisfaction(*this, Template, ConstraintExpr,
+ TemplateArgs, TemplateNameLoc,
+ IsSatisfied, SubstitutedExpression,
+ SubstDiag);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+ return ::CheckConstraintSatisfaction(*this, Part, ConstraintExpr,
+ TemplateArgs, TemplateNameLoc,
+ IsSatisfied, SubstitutedExpression,
+ SubstDiag);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+ return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExpr,
+ TemplateArgs, TemplateNameLoc,
+ IsSatisfied, SubstitutedExpression,
+ SubstDiag);
+}
+
+bool Sema::EnsureTemplateArgumentListConstraints(TemplateDecl *TD,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc) {
+ bool IsSatisfied;
+ ExprResult SubstitutedConstraintExpr;
+ llvm::Optional<PartialDiagnosticAt> SubstDiag;
+ if (CheckConstraintSatisfaction(TD, TD->getAssociatedConstraints(),
+ TemplateArgs, TemplateNameLoc, IsSatisfied,
+ SubstitutedConstraintExpr, SubstDiag))
+ return true;
+
+ if (!IsSatisfied) {
+ SmallString<128> TemplateArgString;
+ TemplateArgString = " ";
+ TemplateArgString += getTemplateArgumentBindingsText(
+ TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+ Diag(TemplateNameLoc, diag::err_template_arg_list_constraints_not_satisfied)
+ << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
+ << TemplateArgString;
+ DiagnoseUnsatisfiedConstraint(SubstitutedConstraintExpr,
+ SubstDiag.getPointer());
+ return true;
+ }
+ return false;
+}
+
+static void diagnoseUnsatisfiedConstraintExpr(Sema &S, Expr *E,
+ bool First = true) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ switch(BO->getOpcode()) {
+ case BO_LAnd: {
+ // Either LHS or RHS evaluated to false.
+ bool Result = false;
+ BO->getLHS()->EvaluateAsBooleanCondition(Result, S.Context);
+ if (Result) {
+ // LHS evaluated to true - therefore RHS evaluated to false.
+ diagnoseUnsatisfiedConstraintExpr(S, BO->getRHS(), First);
+ return;
+ }
+ // LHS evaluated to false - diagnose it.
+ diagnoseUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+ return;
+ }
+
+ case BO_LOr:
+ // Both LHS and RHS evaluated to false.
+ diagnoseUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+ diagnoseUnsatisfiedConstraintExpr(S, BO->getRHS(), /*First=*/false);
+ return;
+
+ case BO_GE:
+ case BO_LE:
+ case BO_GT:
+ case BO_LT:
+ case BO_EQ:
+ case BO_NE:
+ if (BO->getLHS()->getType()->isIntegerType() &&
+ BO->getRHS()->getType()->isIntegerType()) {
+ llvm::APSInt SimplifiedLHS;
+ llvm::APSInt SimplifiedRHS;
+ BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
+ BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
+ S.Diag(E->getSourceRange().getBegin(),
+ diag::note_atomic_constraint_evaluated_to_false_elaborated)
+ << (int) First << E << SimplifiedLHS.toString(10)
+ << BinaryOperator::getOpcodeStr(BO->getOpcode())
+ << SimplifiedRHS.toString(10);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
+ diagnoseUnsatisfiedConstraintExpr(S, PE->getSubExpr(), First);
+ return;
+ } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(E)) {
+ if (CSE->getTemplateArgumentListInfo()->size() == 1) {
+ S.Diag(E->getSourceRange().getBegin(),
+ diag::note_single_arg_concept_specialization_constraint_evaluated_to_false)
+ << (int)First
+ << CSE->getTemplateArgumentListInfo()->arguments()[0].getArgument()
+ << CSE->getSpecializedConcept();
+ } else {
+ S.Diag(E->getSourceRange().getBegin(),
+ diag::note_concept_specialization_constraint_evaluated_to_false)
+ << (int)First << E;
+ }
+ diagnoseUnsatisfiedConstraintExpr(S, CSE->getSubstitutedConstraintExpr(),
+ /*First=*/true);
+ return;
+ }
+ S.Diag(E->getSourceRange().getBegin(),
+ diag::note_atomic_constraint_evaluated_to_false) << (int)First << E;
+}
+
+void Sema::DiagnoseUnsatisfiedConstraint(ExprResult UnsatisfiedConstraintExpr,
+ PartialDiagnosticAt *PDiag) {
+ if (UnsatisfiedConstraintExpr.isInvalid()) {
+ SmallString<128> SFINAEArgString;
+ SourceRange R;
+ if (PDiag) {
+ SFINAEArgString = ": ";
+ R = SourceRange(PDiag->first, PDiag->first);
+ PDiag->second.EmitToString(getDiagnostics(), SFINAEArgString);
+ }
+ if (UnsatisfiedConstraintExpr.get())
+ Diag(UnsatisfiedConstraintExpr.get()->getLocStart(),
+ diag::note_substituted_constraint_expr_is_ill_formed)
+ << UnsatisfiedConstraintExpr.get() << SFINAEArgString << R;
+ else
+ Diag(R.getBegin(),
+ diag::note_substituted_constraint_expr_is_ill_formed_no_expr)
+ << SFINAEArgString << R;
+
+ return;
+ }
+ diagnoseUnsatisfiedConstraintExpr(*this, UnsatisfiedConstraintExpr.get());
+}
+
/// \brief Check that the given template argument list is well-formed
/// for specializing the given template.
bool Sema::CheckTemplateArgumentList(
TemplateDecl *Template, SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,
SmallVectorImpl<TemplateArgument> &Converted,
bool UpdateArgsWithConversions) {
+
// Make a copy of the template arguments for processing. Only make the
// changes at the end when successful in matching the arguments to the
// template.
@@ -7416,6 +7657,10 @@
TemplateArgs, false, Converted))
return true;
+ if (EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+ TemplateNameLoc))
+ return true;
+
// Find the class template (partial) specialization declaration that
// corresponds to these arguments.
if (isPartialSpecialization) {
@@ -8592,6 +8837,11 @@
TemplateArgs, false, Converted))
return true;
+ if (EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+ TemplateNameLoc)) {
+ return true;
+ }
+
// Find the class template specialization declaration that
// corresponds to these arguments.
void *InsertPos = nullptr;
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -578,6 +578,10 @@
TemplateArgumentList *TemplateArgs;
unsigned CallArgIndex;
};
+ struct CNSInfo {
+ TemplateArgumentList *TemplateArgs;
+ ExprResult SubstitutedConstraintExpr;
+ };
}
/// \brief Convert from Sema's representation of template deduction information
@@ -646,6 +650,21 @@
}
break;
+ case Sema::TDK_ConstraintsNotSatisfied: {
+ CNSInfo *Saved = new (Context) CNSInfo;
+ Saved->TemplateArgs = Info.take();
+ Saved->SubstitutedConstraintExpr = Info.SubstitutedConstraintExpr;
+ Result.Data = Saved;
+
+ if (Info.hasSFINAEDiagnostic()) {
+ PartialDiagnosticAt *Diag = new (Result.Diagnostic) PartialDiagnosticAt(
+ SourceLocation(), PartialDiagnostic::NullDiagnostic());
+ Info.takeSFINAEDiagnostic(*Diag);
+ Result.HasDiagnostic = true;
+ }
+ break;
+ }
+
case Sema::TDK_Success:
case Sema::TDK_NonDependentConversionFailure:
llvm_unreachable("not a deduction failure");
@@ -685,6 +704,15 @@
}
break;
+ case Sema::TDK_ConstraintsNotSatisfied:
+ // FIXME: Destroy the template argument list?
+ Data = nullptr;
+ if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
+ Diag->~PartialDiagnosticAt();
+ HasDiagnostic = false;
+ }
+ break;
+
// Unhandled
case Sema::TDK_MiscellaneousDeductionFailure:
break;
@@ -710,6 +738,7 @@
case Sema::TDK_NonDeducedMismatch:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return TemplateParameter();
case Sema::TDK_Incomplete:
@@ -751,6 +780,9 @@
case Sema::TDK_SubstitutionFailure:
return static_cast<TemplateArgumentList*>(Data);
+ case Sema::TDK_ConstraintsNotSatisfied:
+ return static_cast<CNSInfo*>(Data)->TemplateArgs;
+
// Unhandled
case Sema::TDK_MiscellaneousDeductionFailure:
break;
@@ -771,6 +803,7 @@
case Sema::TDK_SubstitutionFailure:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return nullptr;
case Sema::TDK_Inconsistent:
@@ -800,6 +833,7 @@
case Sema::TDK_SubstitutionFailure:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return nullptr;
case Sema::TDK_Inconsistent:
@@ -9865,6 +9899,22 @@
MaybeEmitInheritedConstructorNote(S, Found);
return;
+ case Sema::TDK_ConstraintsNotSatisfied: {
+ // Format the template argument list into the argument string.
+ SmallString<128> TemplateArgString;
+ TemplateArgumentList *Args = DeductionFailure.getTemplateArgumentList();
+ TemplateArgString = " ";
+ TemplateArgString += S.getTemplateArgumentBindingsText(
+ getDescribedTemplate(Templated)->getTemplateParameters(), *Args);
+ S.Diag(Templated->getLocation(),
+ diag::note_ovl_candidate_unsatisfied_constraints)
+ << TemplateArgString;
+
+ S.DiagnoseUnsatisfiedConstraint(
+ static_cast<CNSInfo*>(DeductionFailure.Data)->SubstitutedConstraintExpr,
+ DeductionFailure.getSFINAEDiagnostic());
+ return;
+ }
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
DiagnoseArityMismatch(S, Found, Templated, NumArgs);
@@ -10271,15 +10321,18 @@
case Sema::TDK_CUDATargetMismatch:
return 3;
- case Sema::TDK_InstantiationDepth:
+ case Sema::TDK_ConstraintsNotSatisfied:
return 4;
- case Sema::TDK_InvalidExplicitArguments:
+ case Sema::TDK_InstantiationDepth:
return 5;
+ case Sema::TDK_InvalidExplicitArguments:
+ return 6;
+
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
- return 6;
+ return 7;
}
llvm_unreachable("Unhandled deduction result");
}
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1453,56 +1453,6 @@
ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C)
: Expr(ConceptSpecializationExprClass, EmptyShell()) { }
-bool ConceptSpecializationExpr::evaluate(Sema &S)
-{
- llvm::SmallVector<TemplateArgument, 4> Converted;
- if (S.CheckTemplateArgumentList(SpecializedConcept,
- SpecializedConcept->getLocStart(),
- TemplateArgInfo,
- /*PartialTemplateArgs=*/false, Converted,
- /*UpdateArgsWithConversion=*/false)) {
- // We converted these arguments back in CheckConceptTemplateId, this should
- // work.
- return true;
- }
- EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
-
- MultiLevelTemplateArgumentList MLTAL;
- MLTAL.addOuterTemplateArguments(Converted);
-
- ExprResult E;
- {
- // We do not want error diagnostics escaping here.
- Sema::SFINAETrap Trap(S);
- E = S.SubstExpr(SpecializedConcept->getConstraintExpr(), MLTAL);
- if (E.isInvalid() || Trap.hasErrorOccurred()) {
- // C++2a [temp.constr.atomic]p1
- // ...If substitution results in an invalid type or expression, the
- // constraint is not satisfied.
- IsSatisfied = false;
- return true;
- }
- }
-
- if (!S.CheckConstraintExpression(E.get())) {
- S.Diag(getLocStart(), diag::note_in_concept_specialization) << this;
- return true;
- }
-
- bool Satisfied = false;
- if (!E.get()->EvaluateAsBooleanCondition(Satisfied, S.Context)) {
- // C++2a [temp.constr.atomic]p1
- // ...E shall be a constant expression of type bool.
- S.Diag(getLocStart(), diag::err_concept_non_constant_constraint_expression)
- << this << E.get();
- return true;
- }
- IsSatisfied = Satisfied;
- return false;
-}
-
bool ConceptSpecializationExpr::setTemplateArguments(Sema *S,
const TemplateArgumentListInfo *TALI){
bool IsDependent = false;
@@ -1526,7 +1476,25 @@
setInstantiationDependent(IsDependent);
setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
if (!IsDependent && S) {
- return evaluate(*S);
+ llvm::SmallVector<TemplateArgument, 4> Converted;
+ if (S->CheckTemplateArgumentList(SpecializedConcept,
+ SpecializedConcept->getLocStart(),
+ TemplateArgInfo,
+ /*PartialTemplateArgs=*/false, Converted,
+ /*UpdateArgsWithConversion=*/false)) {
+ // We converted these arguments back in CheckConceptTemplateId, this should
+ // work.
+ return true;
+ }
+ ExprResult SubstitutedExpression;
+ if (S->CheckConstraintSatisfaction(SpecializedConcept,
+ SpecializedConcept->getConstraintExpr(),
+ Converted, ConceptNameLoc, IsSatisfied,
+ SubstitutedExpression, SubstDiagnostic)){
+ S->Diag(getLocStart(), diag::note_in_concept_specialization) << this;
+ return true;
+ }
+ SubstitutedConstraintExpr = SubstitutedExpression.get();
}
return false;
}
\ No newline at end of file
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h
+++ include/clang/Sema/TemplateDeduction.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
#define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
+#include "clang/Sema/Ownership.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "llvm/ADT/SmallVector.h"
@@ -192,6 +193,10 @@
///
/// FIXME: This should be kept internal to SemaTemplateDeduction.
SmallVector<DeducedPack *, 8> PendingDeducedPacks;
+
+ /// \brief The substituted constraint expression used to determine whether the
+ /// associated constraints were satisfied.
+ ExprResult SubstitutedConstraintExpr;
};
} // end namespace sema
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5576,15 +5576,73 @@
ConceptDecl *CTD,
const TemplateArgumentListInfo *TALI);
- /// Check whether the given expression is a valid constraint expression.
- /// A diagnostic is emmited if it is not, and false is returned.
+ /// \brief Check whether the given expression is a valid constraint
+ /// expression. A diagnostic is emmited if it is not, and false is returned.
bool CheckConstraintExpression(Expr *CE);
+ /// \brief Check whether the given constraint expression is satisfied given
+ /// template arguments. Returns false and updates IsSatisfied with the
+ /// satisfaction verdict if successful, emits a diagnostic and returns true if
+ /// an error occured and satisfaction could not be determined.
+ ///
+ /// \param SubstitutedExpr if not null, will be used to return the substituted
+ /// constraint expression (to be used for diagnostics, for example).
+ ///
+ /// \param SubstDiag if the constraint was not satisfied because of a
+ /// substitution failure, this will contain the emitted diagnostics, if any.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ bool CheckConstraintSatisfaction(TemplateDecl *Template,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
+ bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
+ bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
+ const Expr *ConstraintExpr,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc,
+ bool &IsSatisfied,
+ ExprResult &SubstitutedExpression,
+ llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
/// \brief Check that the associated constraints of a template declaration
/// match the associated constraints of an older declaration of which it is a
/// redeclaration
bool CheckRedeclarationConstraintMatch(const Expr *OldAC, const Expr *NewAC);
+ /// \brief Ensure that the given template arguments satisfy the constraints
+ /// associated with the given template, emitting a diagnostic if they do not.
+ ///
+ /// \param Template The template to which the template arguments are being
+ /// provided.
+ ///
+ /// \param TemplateArgs The converted, canonicalized template arguments.
+ ///
+ /// \param TemplateNameLoc Where was the template name that evoked this
+ /// constraints check.
+ ///
+ /// \returns true if the constrains are not satisfied or could not be checked
+ /// for satisfaction, false if the constraints are satisfied.
+ bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc);
+
+ /// \brief Emit diagnostics explaining why a constraint expression was deemed
+ /// unsatisfied.
+ void DiagnoseUnsatisfiedConstraint(ExprResult SubstitutedConstraintExpr,
+ PartialDiagnosticAt *PDiag);
+
// ParseObjCStringLiteral - Parse Objective-C string literals.
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
ArrayRef<Expr *> Strings);
@@ -6899,6 +6957,9 @@
TDK_InvalidExplicitArguments,
/// \brief Checking non-dependent argument conversions failed.
TDK_NonDependentConversionFailure,
+ /// \brief The deduced arguments did not satisfy the constraints associated
+ /// with the template.
+ TDK_ConstraintsNotSatisfied,
/// \brief Deduction failed; that's all we know.
TDK_MiscellaneousDeductionFailure,
/// \brief CUDA Target attributes do not match.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2402,11 +2402,28 @@
def err_concept_no_associated_constraints : Error<
"concept may not have associated constraints">;
def err_concept_non_constant_constraint_expression : Error<
- "concept specialization '%0' resulted in a non-constant expression '%1'.">;
+ "substitution into constraint expression resulted in a non-constant expression '%0'">;
def err_non_bool_atomic_constraint : Error<
"atomic constraint '%0' must be of type 'bool' (found %1)">;
def note_in_concept_specialization : Note<
"in concept specialization '%0'">;
+def err_template_arg_list_constraints_not_satisfied : Error<
+ "constraints not satisfied for %select{class template|function template|variable template|alias template|"
+ "template template parameter|template}0 %1%2, because:">;
+def note_constraints_not_satisfied : Note<
+ "constraints not satisfied">;
+def note_substituted_constraint_expr_is_ill_formed : Note<
+ "substituted constraint expression '%0' is ill-formed%1">;
+def note_substituted_constraint_expr_is_ill_formed_no_expr : Note<
+ "substituted constraint expression is ill-formed%0">;
+def note_atomic_constraint_evaluated_to_false : Note<
+ "%select{and |}0'%1' evaluated to false">;
+def note_concept_specialization_constraint_evaluated_to_false : Note<
+ "%select{and |}0'%1' evaluated to false, because:">;
+def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
+ "%select{and |}0%1 does not satisfy %2, because:">;
+def note_atomic_constraint_evaluated_to_false_elaborated : Note<
+ "%select{and |}0'%1' (%2 %3 %4) evaluated to false">;
def err_template_different_associated_constraints : Error<
"associated constraints differ in template redeclaration">;
@@ -3540,6 +3557,8 @@
def note_ovl_candidate_explicit_arg_mismatch_named : Note<
"candidate template ignored: invalid explicitly-specified argument "
"for template parameter %0">;
+def note_ovl_candidate_unsatisfied_constraints : Note<
+ "candidate template ignored: constraints not satisfied%0">;
def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<
"candidate template ignored: invalid explicitly-specified argument "
"for %ordinal0 template parameter">;
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -4430,12 +4430,19 @@
/// when the expression was created. If any of the template arguments are
/// dependent (this expr would then be isValueDependent()), this is to be
/// ignored.
- bool IsSatisfied : 1;
+ bool IsSatisfied;
- /// \brief Evaluates this concept specialization to determine whether or not
- /// the concept is satisfied. Returns true if an error occured and the concept
- /// could not be checked for satisfaction.
- bool evaluate(Sema &S);
+ /// \brief The concept's constraint expression with the template arguments
+ /// substituted in. To be used in diagnostics. If any of the template
+ /// arguments are dependent (this expr would then be isValueDependent()), this
+ /// is to be ignored.
+ Expr *SubstitutedConstraintExpr;
+
+ /// \brief If the concept's constraint expression was not satisfied because of
+ /// an ill-formed substitution, this contains diagnostics emitted when
+ /// substituting the template arguments into the constraint expression, to be
+ /// used in elaborated diagnostics.
+ llvm::Optional<PartialDiagnosticAt> SubstDiagnostic;
public:
ConceptSpecializationExpr(ASTContext &C, Sema &S,
@@ -4479,6 +4486,14 @@
IsSatisfied = Satisfied;
}
+ Expr *getSubstitutedConstraintExpr() const {
+ return SubstitutedConstraintExpr;
+ }
+
+ void setSubstitutedConstraintExpr(Expr *SCE) {
+ SubstitutedConstraintExpr = SCE;
+ }
+
SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
void setConceptNameLoc(SourceLocation Loc) {
ConceptNameLoc = Loc;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits