Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -4415,6 +4415,9 @@ /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the /// specialization of a concepts results in a prvalue of type bool. class ConceptSpecializationExpr final : public Expr { +public: + using SubstitutionDiagnostic = std::pair; + protected: /// \brief The concept specialization this represents. ConceptDecl *SpecializedConcept; @@ -4430,12 +4433,17 @@ /// 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 for use in diagnostics and normalization, or, if the + /// substituted expression was ill-formed, a diagnostic explaining why it was + /// so. + /// + /// This field will be present even if the resulting expression is still + /// dependent after substitution. + llvm::PointerUnion + SubstitutedConstraintExpr; public: ConceptSpecializationExpr(ASTContext &C, Sema &S, @@ -4479,8 +4487,24 @@ IsSatisfied = Satisfied; } + Expr *getSubstitutedConstraintExpr() const { + return SubstitutedConstraintExpr.dyn_cast(); + } + + void setSubstitutedConstraintExpr(Expr *SCE) { + SubstitutedConstraintExpr = SCE; + } + + const SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + return SubstitutedConstraintExpr.dyn_cast(); + } + + void setSubstitutionDiagnostic(SubstitutionDiagnostic *Diag) { + SubstitutedConstraintExpr = Diag; + } + SourceLocation getConceptNameLoc() const { return ConceptNameLoc; } - void setConceptNameLoc(SourceLocation Loc) { + void setConceptNameLoc(SourceLocation Loc) { ConceptNameLoc = Loc; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2402,11 +2402,31 @@ 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">; +def note_constraints_not_satisfied : Note< + "constraints not satisfied">; +def note_substituted_constraint_expr_is_ill_formed : Note< + "because substituted constraint expression is ill-formed%0">; +def note_atomic_constraint_evaluated_to_false : Note< + "%select{and |because }0'%1' evaluated to false">; +def note_concept_specialization_constraint_evaluated_to_false : Note< + "%select{and |because }0'%1' evaluated to false">; +def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note< + "%select{and |because }0%1 does not satisfy %2">; +def note_atomic_constraint_evaluated_to_false_elaborated : Note< + "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">; +def err_could_not_normalize_ill_formed_constraint : Error< + "required expansion of concept specialization %0 failed, substituted " + "expression would be illegal">; +def note_could_not_normalize_ill_formed_constraint_reason : Note< + "because: %0">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; @@ -3540,6 +3560,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/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5576,15 +5576,78 @@ 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 TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& SubstDiag); + + bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD, + const Expr *ConstraintExpr, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& SubstDiag); + + bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD, + const Expr *ConstraintExpr, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& 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 TemplateArgs, + SourceLocation TemplateNameLoc); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied. + void DiagnoseUnsatisfiedConstraint(Expr *SubstitutedConstraintExpr); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied because it was ill-formed. + void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation, + StringRef Diagnostic); + void DiagnoseUnsatisfiedIllFormedConstraint(PartialDiagnosticAt *Diagnostic); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef Strings); @@ -6899,6 +6962,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/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 PendingDeducedPacks; + + /// \brief The substituted constraint expression used to determine whether the + /// associated constraints were satisfied. + ExprResult SubstitutedConstraintExpr; }; } // end namespace sema 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 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; @@ -1525,8 +1475,39 @@ setValueDependent(IsDependent); setInstantiationDependent(IsDependent); setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); - if (!IsDependent && S) { - return evaluate(*S); + if (S) { + llvm::SmallVector 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 SubstConstraint; + llvm::Optional SubstDiagnostic; + if (S->CheckConstraintSatisfaction(SpecializedConcept, + SpecializedConcept->getConstraintExpr(), + Converted, ConceptNameLoc, IsSatisfied, + SubstConstraint, + SubstDiagnostic)){ + S->Diag(getLocStart(), diag::note_in_concept_specialization) << this; + return true; + } + if (!SubstConstraint.isInvalid() && SubstConstraint.isUsable()) { + SubstitutedConstraintExpr = SubstConstraint.get(); + } else if (SubstDiagnostic.hasValue()) { + SmallString<128> DiagString; + DiagString = ": "; + SubstDiagnostic.getValue().second.EmitToString(S->getDiagnostics(), + DiagString); + SubstitutedConstraintExpr = + new (S->Context) SubstitutionDiagnostic{ + SubstDiagnostic.getValue().first, std::string(DiagString.begin(), + DiagString.end()) }; + } } return false; } \ No newline at end of file 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(Data); + case Sema::TDK_ConstraintsNotSatisfied: + return static_cast(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: @@ -1178,6 +1212,8 @@ return NewTarget != OldTarget; } + // TODO: Concepts: Check function trailing requires clauses here. + // The signatures match; this is not an overload. return false; } @@ -9865,6 +9901,27 @@ 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; + + if (DeductionFailure.HasDiagnostic) { + S.DiagnoseUnsatisfiedIllFormedConstraint( + DeductionFailure.getSFINAEDiagnostic()); + } else { + S.DiagnoseUnsatisfiedConstraint( + static_cast(DeductionFailure.Data) + ->SubstitutedConstraintExpr.get()); + } + return; + } case Sema::TDK_TooManyArguments: case Sema::TDK_TooFewArguments: DiagnoseArityMismatch(S, Found, Templated, NumArgs); @@ -10271,15 +10328,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/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -2906,8 +2906,7 @@ TemplateDecl *Template = Name.getAsTemplateDecl(); if (!Template || isa(Template) || - isa(Template) || - isa(Template)) { + isa(Template) || isa(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(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; @@ -3910,7 +3921,7 @@ TemplateKWLoc, TemplateArgs); } - if (R.getAsSingle() && !DependentArguments) { + if (R.getAsSingle()) { return CheckConceptTemplateId(SS, R.getLookupNameInfo(), R.getAsSingle(), TemplateKWLoc, TemplateArgs); @@ -4796,6 +4807,254 @@ return diagnoseArityMismatch(S, TD, Loc, Args); } +template +bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, + const Expr *ConstraintExpr, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& 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(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 TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& SubstDiag) { + return ::CheckConstraintSatisfaction(*this, Template, ConstraintExpr, + TemplateArgs, TemplateNameLoc, + IsSatisfied, SubstitutedExpression, + SubstDiag); +} + +bool +Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, + const Expr *ConstraintExpr, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& SubstDiag) { + return ::CheckConstraintSatisfaction(*this, Part, ConstraintExpr, + TemplateArgs, TemplateNameLoc, + IsSatisfied, SubstitutedExpression, + SubstDiag); +} + +bool +Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, + const Expr *ConstraintExpr, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, + bool &IsSatisfied, + ExprResult &SubstitutedExpression, + llvm::Optional& SubstDiag) { + return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExpr, + TemplateArgs, TemplateNameLoc, + IsSatisfied, SubstitutedExpression, + SubstDiag); +} + +bool Sema::EnsureTemplateArgumentListConstraints(TemplateDecl *TD, + ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc) { + bool IsSatisfied; + ExprResult SubstitutedConstraintExpr; + llvm::Optional 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; + if (SubstitutedConstraintExpr.isUsable()) { + DiagnoseUnsatisfiedConstraint(SubstitutedConstraintExpr.get()); + } else { + DiagnoseUnsatisfiedIllFormedConstraint(SubstDiag.getPointer()); + } + return true; + } + return false; +} + +static void diagnoseUnsatisfiedConstraintExpr(Sema &S, Expr *E, + bool First = true) { + if (BinaryOperator *BO = dyn_cast(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); + BO->getRHS()->EvaluateAsBooleanCondition(Result, S.Context); + if (!Result) { + // RHS evaluated to false as well - diagnose it too. + diagnoseUnsatisfiedConstraintExpr(S, BO->getRHS(), /*First=*/false); + } + 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(E)) { + diagnoseUnsatisfiedConstraintExpr(S, PE->getSubExpr(), First); + return; + } else if (auto *CSE = dyn_cast(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; + } + if (Expr *Subst = CSE->getSubstitutedConstraintExpr()) { + S.DiagnoseUnsatisfiedConstraint(Subst); + } else { + auto *Diag = CSE->getSubstitutionDiagnostic(); + S.DiagnoseUnsatisfiedIllFormedConstraint(Diag->first, Diag->second); + } + return; + } + S.Diag(E->getSourceRange().getBegin(), + diag::note_atomic_constraint_evaluated_to_false) << (int)First << E; +} + +void Sema::DiagnoseUnsatisfiedConstraint(Expr *UnsatisfiedConstraintExpr) { + diagnoseUnsatisfiedConstraintExpr(*this, UnsatisfiedConstraintExpr); +} + +void Sema::DiagnoseUnsatisfiedIllFormedConstraint(PartialDiagnosticAt *PDiag) { + if (PDiag) { + SmallString<128> SFINAEArgString; + SFINAEArgString = ": "; + PDiag->second.EmitToString(getDiagnostics(), SFINAEArgString); + DiagnoseUnsatisfiedIllFormedConstraint(PDiag->first, SFINAEArgString); + } else { + DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation(), ""); + } +} +void Sema::DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLoc, + StringRef Diagnostic) { + Diag(DiagnosticLoc, + diag::note_substituted_constraint_expr_is_ill_formed) + << Diagnostic; +} + +void +Sema::DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, + const TemplateParameterList *New){ + Diag(New->getTemplateLoc(), + diag::err_template_different_associated_constraints); + Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) + << /*declaration*/0; +} + /// \brief Check that the given template argument list is well-formed /// for specializing the given template. bool Sema::CheckTemplateArgumentList( @@ -4803,6 +5062,7 @@ TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, SmallVectorImpl &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 +7676,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 +8856,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/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -2658,6 +2658,27 @@ return Sema::TDK_Success; } +template +static Sema::TemplateDeductionResult +CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template, + ArrayRef DeducedArgs, + TemplateDeductionInfo& Info) { + bool IsSatisfied = false; + ExprResult SubstitutedExpr; + llvm::Optional 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/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/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -624,6 +624,16 @@ ArgInfo.addArgument(Record.readTemplateArgumentLoc()); E->setTemplateArguments(/*Sema=*/nullptr, &ArgInfo); E->setSatisfied(Record.readInt() == 1); + if (Record.readInt()) { + E->setSubstitutedConstraintExpr(Record.readExpr()); + } else { + SourceLocation DiagLocation = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + E->setSubstitutionDiagnostic( + new (Record.getContext()) + ConceptSpecializationExpr::SubstitutionDiagnostic{DiagLocation, + DiagMessage}); + } } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -357,6 +357,15 @@ Record.AddTemplateArgumentLoc(TALI->getArgumentArray()[i]); } Record.push_back(E->isSatisfied()); + Expr *SubstitutedExpr = E->getSubstitutedConstraintExpr(); + Record.push_back(SubstitutedExpr != nullptr); + if (SubstitutedExpr) { + Record.AddStmt(SubstitutedExpr); + } else { + auto *SubstitutionDiagnostic = E->getSubstitutionDiagnostic(); + Record.AddSourceLocation(SubstitutionDiagnostic->first); + Record.AddString(SubstitutionDiagnostic->second); + } Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } 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 -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); -static_assert(!C4); // expected-error {{concept specialization 'C4' resulted in a non-constant expression 'B::add(1, 2) == 3'}} +static bool X = C4; // expected-note {{in concept specialization 'C4'}} template constexpr bool is_same_v = false; 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 +constexpr bool is_ptr_v = false; + +template +constexpr bool is_ptr_v = true; + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +template requires is_ptr_v // expected-note {{because 'is_ptr_v' evaluated to false}} expected-note {{because 'is_ptr_v' 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(nullptr)), int>); +static_assert(is_same_v); // expected-error {{no matching function for call to 'dereference'}} +static_assert(is_same_v('a')), char>); // expected-error {{no matching function for call to 'dereference'}} + + +template 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/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,79 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template requires sizeof(T) >= 2 // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}} +struct A { + static constexpr int value = sizeof(T); +}; + +static_assert(A::value == 4); +static_assert(A::value == 1); // expected-error{{constraints not satisfied for class template 'A' [with T = char]}} + +template + requires sizeof(T) != sizeof(U) // expected-note{{because 'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}} + && sizeof(T) >= 4 // expected-note{{because '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 == 3); +static_assert(SizeDiff == 0); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = int, U = char [4]]}} +static_assert(SizeDiff == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int]}} + +template + requires ((sizeof(Ts) == 4) || ...) // expected-note{{because '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 == 13); +static_assert(SumSizes == 89); // expected-error{{constraints not satisfied for variable template 'SumSizes' [with Ts = ]}} + +template +concept IsBig = sizeof(T) > 100; // expected-note{{because 'sizeof(int) > 100' (4 > 100) evaluated to false}} + +template + requires IsBig // expected-note{{'int' does not satisfy 'IsBig'}} +using BigPtr = T*; + +static_assert(sizeof(BigPtr)); // expected-error{{constraints not satisfied for alias template 'BigPtr' [with T = int]}}}} + +template requires T::value // expected-note{{because 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::value); // expected-error{{constraints not satisfied for class template 'S' [with T = int]}} +static_assert(S::value); + +template +struct AA +{ + template requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}} + struct B + { + static constexpr int a = 0; + }; + + template requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}} + static constexpr int b = 1; + + template requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}} + static constexpr int getB() { // expected-note{{candidate template ignored: constraints not satisfied [with U = int [2]]}} + return 2; + } + + static auto foo() + { + return B::a; // expected-error{{constraints not satisfied for class template 'B' [with U = int [2]]}} + } + + static auto foo1() + { + return b; // expected-error{{constraints not satisfied for variable template 'b' [with U = int [2]]}} + } + + static auto foo2() + { + return AA::getB(); // expected-error{{no matching function for call to 'getB'}} + } +}; + +constexpr auto x = AA::foo(); // expected-note{{in instantiation of member function 'AA::foo' requested here}} +constexpr auto x1 = AA::foo1(); // expected-note{{in instantiation of member function 'AA::foo1' requested here}} +constexpr auto x2 = AA::foo2(); // expected-note{{in instantiation of member function 'AA::foo2' requested here}} \ No newline at end of file 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 requires sizeof(T) >= 4 // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}} + struct is_same { static constexpr bool value = false; }; + + template requires sizeof(T*) >= 4 && sizeof(T) >= 4 + struct is_same { static constexpr bool value = true; }; + + static_assert(!is_same::value); + static_assert(!is_same::value); + static_assert(is_same::value); + static_assert(is_same::value); // expected-error {{constraints not satisfied for class template 'is_same' [with T = char, U = char]}} +} + +namespace variable_templates +{ + template requires sizeof(T) >= 4 + constexpr bool is_same_v = false; + + template requires sizeof(T*) >= 4 && sizeof(T) >= 4 + constexpr bool is_same_v = true; + + static_assert(!is_same_v); + static_assert(!is_same_v); + static_assert(is_same_v); +} \ No newline at end of file