Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -736,9 +736,10 @@ void loadLazySpecializationsImpl() const; - template typename SpecEntryTraits::DeclType* + template + typename SpecEntryTraits::DeclType* findSpecializationImpl(llvm::FoldingSetVector &Specs, - ArrayRef Args, void *&InsertPos); + void *&InsertPos, ProfileArguments... ProfileArgs); template void addSpecializationImpl(llvm::FoldingSetVector &Specs, @@ -1985,7 +1986,22 @@ ->getInjectedSpecializationType(); } - // FIXME: Add Profile support! + void Profile(llvm::FoldingSetNodeID &ID) const { + llvm::SmallVector AC; + getAssociatedConstraints(AC); + Profile(ID, getTemplateArgs().asArray(), AC, getASTContext()); + } + + static void + Profile(llvm::FoldingSetNodeID &ID, ArrayRef TemplateArgs, + ArrayRef AssociatedConstraints, ASTContext &Context) { + ID.AddInteger(TemplateArgs.size()); + for (const TemplateArgument &TemplateArg : TemplateArgs) + TemplateArg.Profile(ID, Context); + ID.AddInteger(AssociatedConstraints.size()); + for (const Expr *Constraint : AssociatedConstraints) + Constraint->Profile(ID, Context, /*Canonical=*/true); + } static bool classof(const Decl *D) { return classofKind(D->getKind()); } @@ -2109,7 +2125,9 @@ /// Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. ClassTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef Args, void *&InsertPos); + findPartialSpecialization(ArrayRef Args, + ArrayRef AssociatedConstraints, + void *&InsertPos); /// Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization. @@ -2806,6 +2824,23 @@ return First->InstantiatedFromMember.setInt(true); } + void Profile(llvm::FoldingSetNodeID &ID) const { + llvm::SmallVector AC; + getAssociatedConstraints(AC); + Profile(ID, getTemplateArgs().asArray(), AC, getASTContext()); + } + + static void + Profile(llvm::FoldingSetNodeID &ID, ArrayRef TemplateArgs, + ArrayRef AssociatedConstraints, ASTContext &Context) { + ID.AddInteger(TemplateArgs.size()); + for (const TemplateArgument &TemplateArg : TemplateArgs) + TemplateArg.Profile(ID, Context); + ID.AddInteger(AssociatedConstraints.size()); + for (const Expr *Constraint : AssociatedConstraints) + Constraint->Profile(ID, Context, /*Canonical=*/true); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { @@ -2924,7 +2959,9 @@ /// Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. VarTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef Args, void *&InsertPos); + findPartialSpecialization(ArrayRef Args, + ArrayRef AssociatedConstraints, + void *&InsertPos); /// Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization. Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_EXPRCXX_H #define LLVM_CLANG_AST_EXPRCXX_H +#include "clang/Sema/SemaConcept.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -4533,6 +4534,10 @@ TemplateArgument> { friend class ASTStmtReader; friend TrailingObjects; +public: + using SubstitutionDiagnostic = std::pair; + +protected: // \brief The optional nested name specifier used when naming the concept. NestedNameSpecifierLoc NestedNameSpec; @@ -4550,11 +4555,8 @@ /// through a UsingShadowDecl. NamedDecl *FoundDecl; - /// \brief The concept named, and whether or not the concept with the given - /// arguments was satisfied when the expression was created. - /// If any of the template arguments are dependent (this expr would then be - /// isValueDependent()), this bit is to be ignored. - llvm::PointerIntPair NamedConcept; + /// \brief The concept named. + ConceptDecl *NamedConcept; /// \brief The template argument list source info used to specialize the /// concept. @@ -4564,13 +4566,18 @@ /// converted template arguments. unsigned NumTemplateArgs; + /// \brief Information about the satisfaction of the named concept with the + /// given arguments. If this expression is value dependent, this is to be + /// ignored. + ConstraintSatisfaction Satisfaction; + ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef ConvertedArgs, - bool IsSatisfied); + ConstraintSatisfaction Satisfaction); ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs); @@ -4581,7 +4588,8 @@ SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, bool IsSatisfied); + ArrayRef ConvertedArgs, + ConstraintSatisfaction Satisfaction); static ConceptSpecializationExpr * Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs); @@ -4595,7 +4603,7 @@ } ConceptDecl *getNamedConcept() const { - return NamedConcept.getPointer(); + return NamedConcept; } ArrayRef getTemplateArguments() const { @@ -4612,12 +4620,25 @@ ArrayRef Converted); /// \brief Whether or not the concept with the given arguments was satisfied - /// when the expression was created. This method assumes that the expression - /// is not dependent! + /// when the expression was created. + /// The expression must not be dependent. bool isSatisfied() const { assert(!isValueDependent() && "isSatisfied called on a dependent ConceptSpecializationExpr"); - return NamedConcept.getInt(); + return Satisfaction.IsSatisfied; + } + + /// \brief Get elaborated satisfaction info about the template arguments' + /// satisfaction of the named concept. + /// The expression must not be dependent. + const ConstraintSatisfaction &getSatisfaction() const { + assert(!isValueDependent() + && "getSatisfaction called on dependent ConceptSpecializationExpr"); + return Satisfaction; + } + + void setSatisfaction(ConstraintSatisfaction Satisfaction) { + this->Satisfaction = std::move(Satisfaction); } SourceLocation getConceptNameLoc() const { return ConceptNameLoc; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2450,6 +2450,24 @@ "expression">; def err_non_bool_atomic_constraint : Error< "atomic constraint must be of type 'bool' (found %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 note_could_not_normalize_argument_substitution_failed : Note< + "when substituting into %0 %1. Make sure concept arguments are " + "valid for any substitution">; def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; @@ -3653,6 +3671,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">; @@ -4327,6 +4347,14 @@ "while checking a default template argument used here">; def note_concept_specialization_here : Note< "while checking the satisfaction of concept '%0' requested here">; +def note_checking_constraints_for_template_id_here : Note< + "while checking constraint satisfaction for template '%0' required here">; +def note_checking_constraints_for_var_spec_id_here : Note< + "while checking constraint satisfaction for variable template " + "partial specialization '%0' required here">; +def note_checking_constraints_for_class_spec_id_here : Note< + "while checking constraint satisfaction for class template partial " + "specialization '%0' required here">; def note_constraint_substitution_here : Note< "while substituting template arguments into constraint expression here">; def note_instantiation_contexts_suppressed : Note< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -37,6 +37,7 @@ #include "clang/Basic/Specifiers.h" #include "clang/Basic/TemplateKinds.h" #include "clang/Basic/TypeTraits.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/AnalysisBasedWarnings.h" #include "clang/Sema/CleanupInfo.h" #include "clang/Sema/DeclSpec.h" @@ -5764,10 +5765,58 @@ /// A diagnostic is emitted if it is not, and false is returned. bool CheckConstraintExpression(Expr *CE); - bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept, - MultiLevelTemplateArgumentList &MLTAL, - Expr *ConstraintExpr, - bool &IsSatisfied); +private: + /// \brief Caches pairs of template-like decls whose associated constraints + /// were checked for subsumption and whether or not the first's constraints + /// did in fact subsume the second's. + llvm::DenseMap, bool> SubsumptionCache; + +public: + /// \brief Returns whether the given declaration's associated constraints are + /// more constrained than another declaration's according to the partial + /// ordering of constraints. + bool IsMoreConstrained(NamedDecl *D1, ArrayRef AC1, + NamedDecl *D2, ArrayRef AC2); + + /// \brief Check whether the given list of constraint expressions are + /// satisfied (as if in a 'conjunction') given template arguments. + /// \param ConstraintExprs a list of constraint expressions, treated as if + /// they were 'AND'ed together. + /// \param TemplateArgs the list of template arguments to substitute into the + /// constraint expression. + /// \param TemplateIDRange The source range of the template id that + /// caused the constraints check. + /// \param Satisfaction if true is returned, will contain details of the + /// satisfaction, with enough information to diagnose an unsatisfied + /// expression. + /// \returns true if an error occurred and satisfaction could not be checked, + /// false otherwise. + bool CheckConstraintSatisfaction(TemplateDecl *Template, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction); + + bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction); + + bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction); + + /// \brief Check whether the given non-dependent constraint expression is + /// satisfied. Returns false and updates Satisfaction with the satisfaction + /// verdict if successful, emits a diagnostic and returns true if an error + /// occured and satisfaction could not be determined. + /// + /// \returns true if an error occurred, false otherwise. + bool CheckConstraintSatisfaction(const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction); /// Check that the associated constraints of a template declaration match the /// associated constraints of an older declaration of which it is a @@ -5775,6 +5824,36 @@ bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old, TemplateParameterList *New); + /// \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 TemplateIDRange The source range of the template id that + /// caused the 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, + SourceRange TemplateIDRange); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied. + void + DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied because it was ill-formed. + void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation, + StringRef Diagnostic); + + void DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, + const TemplateParameterList *New); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef Strings); @@ -6553,13 +6632,19 @@ /// contain the converted forms of the template arguments as written. /// Otherwise, \p TemplateArgs will not be modified. /// + /// \param InstantiationDependent If provided, and no error occured, will + /// receive true if the arguments' match to the given template is + /// instantiation dependant - e.g. the arguments contain a pack expansion + /// into a non pack parameter. + /// /// \returns true if an error occurred, false otherwise. bool CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, SmallVectorImpl &Converted, - bool UpdateArgsWithConversions = true); + bool UpdateArgsWithConversions = true, + bool *InstantiationDependent = nullptr); bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, @@ -7101,6 +7186,9 @@ TDK_InvalidExplicitArguments, /// Checking non-dependent argument conversions failed. TDK_NonDependentConversionFailure, + /// The deduced arguments did not satisfy the constraints associated + /// with the template. + TDK_ConstraintsNotSatisfied, /// Deduction failed; that's all we know. TDK_MiscellaneousDeductionFailure, /// CUDA Target attributes do not match. @@ -7606,7 +7694,7 @@ /// constrained entity (a concept declaration or a template with associated /// constraints). InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintsCheck, TemplateDecl *Template, + ConstraintsCheck, NamedDecl *Template, ArrayRef TemplateArgs, SourceRange InstantiationRange); @@ -7615,7 +7703,7 @@ /// with a template declaration or as part of the satisfaction check of a /// concept. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintSubstitution, TemplateDecl *Template, + ConstraintSubstitution, NamedDecl *Template, sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange); Index: include/clang/Sema/SemaConcept.h =================================================================== --- /dev/null +++ include/clang/Sema/SemaConcept.h @@ -0,0 +1,43 @@ +//===--- SemaConcept.h - Concept Utilities ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides some common utilities for processing constraints +/// and concepts. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_SEMACONCEPTS_H +#define LLVM_CLANG_SEMA_SEMACONCEPTS_H +#include "clang/AST/Expr.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" +#include +#include +namespace clang { +class Sema; +/// \brief The result of a constraint satisfaction check, containing the +/// necessary information to diagnose an unsatisfied constraint. +struct ConstraintSatisfaction { + using SubstitutionDiagnostic = std::pair; + using Detail = llvm::PointerUnion; + + bool IsSatisfied = false; + + /// \brief Pairs of unsatisfied atomic constraint expressions along with the + /// substituted constraint expr, if the template arguments could be + /// substituted into them, or a diagnostic if substitution resulted in an + /// invalid expression. + llvm::SmallVector, 4> Details; +}; + +} // clang + +#endif \ No newline at end of file Index: include/clang/Sema/TemplateDeduction.h =================================================================== --- include/clang/Sema/TemplateDeduction.h +++ include/clang/Sema/TemplateDeduction.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H #define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H +#include "clang/Sema/SemaConcept.h" +#include "clang/Sema/Ownership.h" #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/TemplateBase.h" @@ -218,6 +220,10 @@ /// /// FIXME: This should be kept internal to SemaTemplateDeduction. SmallVector PendingDeducedPacks; + + /// \brief The constraint satisfaction details resulting from the associated + /// constraints satisfaction tests. + ConstraintSatisfaction AssociatedConstraintsSatisfaction; }; } // namespace sema Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5014,15 +5014,26 @@ D->getTemplateArgs().data(), D->getTemplateArgs().size(), TemplateArgs)) return std::move(Err); - // Try to find an existing specialization with these template arguments. + // Try to find an existing specialization with these template arguments and + // constraints. void *InsertPos = nullptr; ClassTemplateSpecializationDecl *PrevDecl = nullptr; ClassTemplatePartialSpecializationDecl *PartialSpec = dyn_cast(D); - if (PartialSpec) + llvm::SmallVector PartialSpecAC; + if (PartialSpec) { + PartialSpec->getAssociatedConstraints(PartialSpecAC); + // Import associated constraints into the "To" context. + for (const Expr *&Constraint : PartialSpecAC) { + ExpectedExpr ConstraintOrErr = import(Constraint); + if (!ConstraintOrErr) + return ConstraintOrErr.takeError(); + Constraint = *ConstraintOrErr; + } PrevDecl = - ClassTemplate->findPartialSpecialization(TemplateArgs, InsertPos); - else + ClassTemplate->findPartialSpecialization(TemplateArgs, PartialSpecAC, + InsertPos); + } else PrevDecl = ClassTemplate->findSpecialization(TemplateArgs, InsertPos); if (PrevDecl) { @@ -5089,10 +5100,11 @@ // Update InsertPos, because preceding import calls may have invalidated // it by adding new specializations. - if (!ClassTemplate->findPartialSpecialization(TemplateArgs, InsertPos)) + auto *PartSpec = cast(D2); + if (!ClassTemplate->findPartialSpecialization(TemplateArgs, PartialSpecAC, + InsertPos)) // Add this partial specialization to the class template. - ClassTemplate->AddPartialSpecialization( - cast(D2), InsertPos); + ClassTemplate->AddPartialSpecialization(PartSpec, InsertPos); } else { // Not a partial specialization. if (GetImportedOrCreateDecl( Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -231,15 +231,15 @@ } } -template +template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * RedeclarableTemplateDecl::findSpecializationImpl( - llvm::FoldingSetVector &Specs, ArrayRef Args, - void *&InsertPos) { + llvm::FoldingSetVector &Specs, void *&InsertPos, + ProfileArguments... ProfileArgs) { using SETraits = SpecEntryTraits; llvm::FoldingSetNodeID ID; - EntryType::Profile(ID, Args, getASTContext()); + EntryType::Profile(ID, ProfileArgs..., getASTContext()); EntryType *Entry = Specs.FindNodeOrInsertPos(ID, InsertPos); return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; } @@ -254,8 +254,8 @@ #ifndef NDEBUG void *CorrectInsertPos; assert(!findSpecializationImpl(Specializations, - SETraits::getTemplateArgs(Entry), - CorrectInsertPos) && + CorrectInsertPos, + SETraits::getTemplateArgs(Entry)) && InsertPos == CorrectInsertPos && "given incorrect InsertPos for specialization"); #endif @@ -312,7 +312,7 @@ FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void FunctionTemplateDecl::addSpecialization( @@ -418,7 +418,7 @@ ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, @@ -428,8 +428,9 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization(ArrayRef Args, - void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + ArrayRef AssociatedConstraints, void *&InsertPos) { + return findSpecializationImpl(getPartialSpecializations(), InsertPos, + Args, AssociatedConstraints); } void ClassTemplateDecl::AddPartialSpecialization( @@ -1032,7 +1033,7 @@ VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, @@ -1042,8 +1043,9 @@ VarTemplatePartialSpecializationDecl * VarTemplateDecl::findPartialSpecialization(ArrayRef Args, - void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + ArrayRef AssociatedConstraints, void *&InsertPos) { + return findSpecializationImpl(getPartialSpecializations(), InsertPos, Args, + AssociatedConstraints); } void VarTemplateDecl::AddPartialSpecialization( Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -1665,7 +1665,8 @@ NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, bool IsSatisfied) + ArrayRef ConvertedArgs, + ConstraintSatisfaction Satisfaction) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, /*TypeDependent=*/false, // All the flags below are set in setTemplateArguments. @@ -1673,8 +1674,8 @@ /*ContainsUnexpandedParameterPacks=*/false), NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl), - NamedConcept(NamedConcept, IsSatisfied), - NumTemplateArgs(ConvertedArgs.size()) { + NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()), + Satisfaction(std::move(Satisfaction)) { setTemplateArguments(ArgsAsWritten, ConvertedArgs); } @@ -1724,13 +1725,14 @@ ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef ConvertedArgs, - bool IsSatisfied) { + ConstraintSatisfaction Satisfaction) { void *Buffer = C.Allocate(totalSizeToAlloc( ConvertedArgs.size())); return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept, ArgsAsWritten, - ConvertedArgs, IsSatisfied); + ConvertedArgs, + std::move(Satisfaction)); } ConceptSpecializationExpr * Index: lib/Sema/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -12,10 +12,13 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/Sema.h" +#include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" #include "clang/AST/ExprCXX.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" using namespace clang; using namespace sema; @@ -43,80 +46,796 @@ return true; } -bool -Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept, - MultiLevelTemplateArgumentList &MLTAL, - Expr *ConstraintExpr, - bool &IsSatisfied) { +template +static bool +calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction, + AtomicEvaluator &&Evaluator) { if (auto *BO = dyn_cast(ConstraintExpr)) { - if (BO->getOpcode() == BO_LAnd) { - if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(), - IsSatisfied)) + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction, + Evaluator)) return true; - if (!IsSatisfied) + + bool IsLHSSatisfied = Satisfaction.IsSatisfied; + + if (BO->getOpcode() == BO_LOr && IsLHSSatisfied) + // [temp.constr.op] p3 + // A disjunction is a constraint taking two operands. To determine if + // a disjunction is satisfied, the satisfaction of the first operand + // is checked. If that is satisfied, the disjunction is satisfied. + // Otherwise, the disjunction is satisfied if and only if the second + // operand is satisfied. return false; - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(), - IsSatisfied); - } else if (BO->getOpcode() == BO_LOr) { - if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(), - IsSatisfied)) - return true; - if (IsSatisfied) + + if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied) + // [temp.constr.op] p2 + // A conjunction is a constraint taking two operands. To determine if + // a conjunction is satisfied, the satisfaction of the first operand + // is checked. If that is not satisfied, the conjunction is not + // satisfied. Otherwise, the conjunction is satisfied if and only if + // the second operand is satisfied. return false; - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(), - IsSatisfied); + + return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction, + std::forward(Evaluator)); } } else if (auto *PO = dyn_cast(ConstraintExpr)) - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, - PO->getSubExpr(), IsSatisfied); + return calculateConstraintSatisfaction(S, PO->getSubExpr(), Satisfaction, + std::forward(Evaluator)); else if (auto *C = dyn_cast(ConstraintExpr)) - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(), - IsSatisfied); - - EnterExpressionEvaluationContext ConstantEvaluated( - *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); - - // Atomic constraint - substitute arguments and check satisfaction. - ExprResult E; - { - TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc()); - InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(), - InstantiatingTemplate::ConstraintSubstitution{}, - NamedConcept, Info, - ConstraintExpr->getSourceRange()); - if (Inst.isInvalid()) - return true; - // We do not want error diagnostics escaping here. - Sema::SFINAETrap Trap(*this); + return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction, + std::forward(Evaluator)); - E = SubstExpr(ConstraintExpr, 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 false; - } - } + // An atomic constraint expression + ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); - if (!CheckConstraintExpression(E.get())) + if (SubstitutedAtomicExpr.isInvalid()) return true; + if (!SubstitutedAtomicExpr.isUsable()) + // Evaluator has decided satisfaction without yielding an expression. + return false; + + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); SmallVector EvaluationDiags; Expr::EvalResult EvalResult; EvalResult.Diag = &EvaluationDiags; - if (!E.get()->EvaluateAsRValue(EvalResult, Context)) { - // C++2a [temp.constr.atomic]p1 - // ...E shall be a constant expression of type bool. - Diag(E.get()->getBeginLoc(), - diag::err_non_constant_constraint_expression) - << E.get()->getSourceRange(); + if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) { + // C++2a [temp.constr.atomic]p1 + // ...E shall be a constant expression of type bool. + S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(), + diag::err_non_constant_constraint_expression) + << SubstitutedAtomicExpr.get()->getSourceRange(); for (const PartialDiagnosticAt &PDiag : EvaluationDiags) - Diag(PDiag.first, PDiag.second); + S.Diag(PDiag.first, PDiag.second); + return true; + } + + Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue(); + if (!Satisfaction.IsSatisfied) + Satisfaction.Details.emplace_back(ConstraintExpr, + SubstitutedAtomicExpr.get()); + + return false; +} + +template +static bool calculateConstraintSatisfaction( + Sema &S, TemplateDeclT *Template, ArrayRef TemplateArgs, + SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, + const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { + return calculateConstraintSatisfaction( + S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + // Atomic constraint - substitute arguments and check satisfaction. + ExprResult SubstitutedExpression; + { + TemplateDeductionInfo Info(TemplateNameLoc); + Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template, + Info, AtomicExpr->getSourceRange()); + if (Inst.isInvalid()) + return ExprError(); + // We do not want error diagnostics escaping here. + Sema::SFINAETrap Trap(S); + SubstitutedExpression = S.SubstExpr(const_cast(AtomicExpr), + MLTAL); + if (SubstitutedExpression.isInvalid() || 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()) + // A non-SFINAE error has occured as a result of this + // substitution. + return ExprError(); + + PartialDiagnosticAt SubstDiag{SourceLocation(), + PartialDiagnostic::NullDiagnostic()}; + Info.takeSFINAEDiagnostic(SubstDiag); + SmallString<128> DiagString; + DiagString = ": "; + SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); + Satisfaction.Details.emplace_back( + AtomicExpr, + new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + SubstDiag.first, + std::string(DiagString.begin(), DiagString.end())}); + Satisfaction.IsSatisfied = false; + return ExprEmpty(); + } + } + + if (!S.CheckConstraintExpression(SubstitutedExpression.get())) + return ExprError(); + + return SubstitutedExpression; + }); +} + +template +static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + if (ConstraintExprs.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + for (auto& Arg : TemplateArgs) + if (Arg.isInstantiationDependent()) { + // No need to check satisfaction for dependent constraint expressions. + Satisfaction.IsSatisfied = true; + return false; + } + + Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs, + TemplateIDRange); + if (Inst.isInvalid()) return true; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TemplateArgs); + + for (const Expr *ConstraintExpr : ConstraintExprs) { + if (calculateConstraintSatisfaction(S, Template, TemplateArgs, + TemplateIDRange.getBegin(), MLTAL, + ConstraintExpr, Satisfaction)) + return true; + if (!Satisfaction.IsSatisfied) + // [temp.constr.op] p2 + // [...] To determine if a conjunction is satisfied, the satisfaction + // of the first operand is checked. If that is not satisfied, the + // conjunction is not satisfied. [...] + return false; } + return false; +} + +bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} + +bool +Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} + +bool +Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} + +bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { + return calculateConstraintSatisfaction( + *this, ConstraintExpr, Satisfaction, + [](const Expr *AtomicExpr) -> ExprResult { + return ExprResult(const_cast(AtomicExpr)); + }); +} + +bool Sema::EnsureTemplateArgumentListConstraints( + TemplateDecl *TD, ArrayRef TemplateArgs, + SourceRange TemplateIDRange) { + ConstraintSatisfaction Satisfaction; + llvm::SmallVector AssociatedConstraints; + TD->getAssociatedConstraints(AssociatedConstraints); + if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs, + TemplateIDRange, Satisfaction)) + return true; - IsSatisfied = EvalResult.Val.getInt().getBoolValue(); + if (!Satisfaction.IsSatisfied) { + SmallString<128> TemplateArgString; + TemplateArgString = " "; + TemplateArgString += getTemplateArgumentBindingsText( + TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); + Diag(TemplateIDRange.getBegin(), + diag::err_template_arg_list_constraints_not_satisfied) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD + << TemplateArgString << TemplateIDRange; + DiagnoseUnsatisfiedConstraint(Satisfaction); + return true; + } return false; +} + +static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, + Expr *SubstExpr, + bool First = true) { + if (BinaryOperator *BO = dyn_cast(SubstExpr)) { + switch (BO->getOpcode()) { + // These two cases will in practice only be reached when using fold + // expressions with || and &&, since otherwise the || and && will have been + // broken down into atomic constraints during satisfaction checking. + case BO_LOr: + // Or evaluated to false - meaning both RHS and LHS evaluated to false. + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First); + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), + /*First=*/false); + return; + case BO_LAnd: + bool LHSSatisfied; + BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context); + if (LHSSatisfied) { + // LHS is true, so RHS must be false. + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First); + return; + } + // LHS is false + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First); + + // RHS might also be false + bool RHSSatisfied; + BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context); + if (!RHSSatisfied) + diagnoseWellFormedUnsatisfiedConstraintExpr(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()) { + Expr::EvalResult SimplifiedLHS; + Expr::EvalResult SimplifiedRHS; + BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context); + BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context); + if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) { + S.Diag(SubstExpr->getBeginLoc(), + diag::note_atomic_constraint_evaluated_to_false_elaborated) + << (int)First << SubstExpr + << SimplifiedLHS.Val.getInt().toString(10) + << BinaryOperator::getOpcodeStr(BO->getOpcode()) + << SimplifiedRHS.Val.getInt().toString(10); + return; + } + } + break; + + default: + break; + } + } else if (ParenExpr *PE = dyn_cast(SubstExpr)) { + diagnoseWellFormedUnsatisfiedConstraintExpr(S, PE->getSubExpr(), First); + return; + } else if (auto *CSE = dyn_cast(SubstExpr)) { + if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { + S.Diag( + CSE->getSourceRange().getBegin(), + diag:: + note_single_arg_concept_specialization_constraint_evaluated_to_false) + << (int)First + << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument() + << CSE->getNamedConcept(); + } else { + S.Diag(SubstExpr->getSourceRange().getBegin(), + diag::note_concept_specialization_constraint_evaluated_to_false) + << (int)First << CSE; + } + S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); + return; + } + + S.Diag(SubstExpr->getSourceRange().getBegin(), + diag::note_atomic_constraint_evaluated_to_false) + << (int)First << SubstExpr; +} + +static void diagnoseUnsatisfiedConstraintExpr( + Sema &S, const Expr *E, ConstraintSatisfaction::Detail Detail, + bool First = true) { + if (auto *Diag = + Detail.dyn_cast()) { + S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) + << Diag->second; + return; + } + + diagnoseWellFormedUnsatisfiedConstraintExpr(S, Detail.get(), First); +} + +void Sema::DiagnoseUnsatisfiedConstraint( + const ConstraintSatisfaction& Satisfaction) { + assert(!Satisfaction.IsSatisfied && + "Attempted to diagnose a satisfied constraint"); + bool First = true; + for (auto &Pair : Satisfaction.Details) { + diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); + First = false; + } +} +namespace { +struct AtomicConstraint { + AtomicConstraint(const Expr *ConstraintExpr, + const ASTTemplateArgumentListInfo *ParameterMapping = nullptr) : + ConstraintExpr{ConstraintExpr}, ParameterMapping{ParameterMapping} {} + + bool subsumes(ASTContext &C, const AtomicConstraint &Other) const { + // C++ [temp.constr.order] p2 + // - an atomic constraint A subsumes another atomic constraint B + // if and only if the A and B are identical [...] + // + // C++ [temp.constr.atomic] p2 + // Two atomic constraints are identical if they are formed from the + // same expression and the targets of the parameter mappings are + // equivalent according to the rules for expressions [...] + + // We do not actually substitute the parameter mappings, therefore the + // constraint expressions are the originals, and comparing them will + // suffice. + if (ConstraintExpr != Other.ConstraintExpr) + return false; + + // Check that the parameter lists are identical + if ((!ParameterMapping) != (!Other.ParameterMapping)) + return false; + if (!ParameterMapping) + return true; + if (ParameterMapping->NumTemplateArgs != + Other.ParameterMapping->NumTemplateArgs) + return false; + + for (unsigned I = 0, S = ParameterMapping->NumTemplateArgs; I < S; ++I) + if (!C.getCanonicalTemplateArgument( + ParameterMapping->arguments()[I].getArgument()) + .structurallyEquals(C.getCanonicalTemplateArgument( + Other.ParameterMapping->arguments()[I].getArgument()))) + return false; + + + return true; + } + + const Expr *ConstraintExpr; + const ASTTemplateArgumentListInfo *ParameterMapping; +}; + +/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is +/// either an atomic constraint, a conjunction of normalized constraints or a +/// disjunction of normalized constraints. +struct NormalizedConstraint { + enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; + + using CompoundConstraint = llvm::PointerIntPair< + std::pair *, 1, + CompoundConstraintKind>; + + llvm::PointerUnion Constraint; + + NormalizedConstraint(AtomicConstraint *C) : Constraint{C} {}; + NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, + NormalizedConstraint RHS, CompoundConstraintKind Kind) + : Constraint{CompoundConstraint{ + new (C) std::pair{LHS, + RHS}, + Kind}} {}; + + CompoundConstraintKind getCompoundKind() const { + assert(!isAtomic() && "getCompoundKind called on atomic constraint."); + return Constraint.get().getInt(); + } + + bool isAtomic() const { return Constraint.is(); } + + NormalizedConstraint &getLHS() const { + assert(!isAtomic() && "getLHS called on atomic constraint."); + return Constraint.get().getPointer()->first; + } + + NormalizedConstraint &getRHS() const { + assert(!isAtomic() && "getRHS called on atomic constraint."); + return Constraint.get().getPointer()->second; + } + + AtomicConstraint *getAtomicConstraint() const { + assert(isAtomic() && + "getAtomicConstraint called on non-atomic constraint."); + return Constraint.get(); + } + static llvm::Optional fromConstraintExpr( + Sema &S, const Expr *E, TemplateDecl *TD = nullptr, + const ASTTemplateArgumentListInfo *ParameterMapping = nullptr) { + assert(E != nullptr); + + // C++ [temp.constr.normal]p1.1 + // [...] + // - The normal form of an expression (E) is the normal form of E. + // [...] + if (auto *P = dyn_cast(E)) + return fromConstraintExpr(S, P->getSubExpr(), TD, ParameterMapping); + if (auto *BO = dyn_cast(E)) { + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + auto LHS = fromConstraintExpr(S, BO->getLHS(), TD, ParameterMapping); + if (!LHS) + return llvm::Optional{}; + auto RHS = fromConstraintExpr(S, BO->getRHS(), TD, ParameterMapping); + if (!RHS) + return llvm::Optional{}; + + return NormalizedConstraint( + S.Context, std::move(*LHS), std::move(*RHS), + BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction); + } + } else if (auto *CSE = dyn_cast(E)) { + // C++ [temp.constr.normal]p1.1 + // [...] + // The normal form of an id-expression of the form C, + // where C names a concept, is the normal form of the + // constraint-expression of C, after substituting A1, A2, ..., AN for C’s + // respective template parameters in the parameter mappings in each atomic + // constraint. If any such substitution results in an invalid type or + // expression, the program is ill-formed; no diagnostic is required. + // [...] + const ASTTemplateArgumentListInfo *Mapping = + CSE->getTemplateArgsAsWritten(); + if (!ParameterMapping) { + // This is a top level CSE. + // + // template + // concept C = true; + // + // template + // void foo() requires C {} -> Mapping is + // + llvm::SmallVector TempList; + bool InstantiationDependent = false; + TemplateArgumentListInfo TALI(Mapping->LAngleLoc, Mapping->RAngleLoc); + for (auto &Arg : Mapping->arguments()) + TALI.addArgument(Arg); + bool Failed = S.CheckTemplateArgumentList(CSE->getNamedConcept(), + E->getBeginLoc(), TALI, /*PartialTemplateArgs=*/false, TempList, + /*UpdateArgsWithConversions=*/false, &InstantiationDependent); + // The potential failure case here is this: + // + // template + // concept C = true; + // + // template + // void foo() requires C // The immediate constraint expr + // // contains a CSE with incorrect no. + // // of arguments. + // {} + // This case should have been handled when C was parsed. + assert( + !Failed && + "Unmatched arguments in top level concept specialization " + "expression should've been caught while it was being constructed"); + + if (InstantiationDependent) + // The case is this: + // + // template + // concept C = true; + // + // template + // void foo() requires C // The immediate constraint expr + // // contains a CSE whose parameters + // // are not mappable to arguments + // // without concrete values. + // {} + // + // Just treat C as an atomic constraint. + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, Mapping)}; + + return fromConstraintExpr(S, + CSE->getNamedConcept()->getConstraintExpr(), + CSE->getNamedConcept(), Mapping); + } + + // This is not a top level CSE. + // + // template + // concept C1 = true; + // + // template + // concept C2 = C1; -> We are here. + // Mapping is {T1=U, T2=T} + // ParameterMapping is {T=X, U=Y}, TD is C2 + // + // template + // void foo() requires C2 {} + // + // We would like to substitute ParameterMapping into Mapping, to get + // ParameterMapping={T1=Y, T2=X} for the next level down. + // Instead of doing the direct substitution of ParameterMapping into + // Mapping, we instead substitute ParameterMapping into C1 and take + // the substituted argument list as the ParameterMapping for the next + // level down. + assert(TD && "ParameterMapping provided without TemplateDecl"); + + TemplateArgumentListInfo TALI(ParameterMapping->LAngleLoc, + ParameterMapping->RAngleLoc); + for (auto &Arg : ParameterMapping->arguments()) + TALI.addArgument(Arg); + llvm::SmallVector TempList; + bool InstantiationDependent = false; + bool Success = + !S.CheckTemplateArgumentList(TD, ParameterMapping->LAngleLoc, + TALI, /*PartialTemplateArgs=*/false, + TempList, + /*UpdateArgsWithConversions=*/false, + &InstantiationDependent) && + !InstantiationDependent; + assert(Success && "ParameterMapping should have already been cheked " + "against template argument list earlier."); + + auto DiagnoseSubstitutionError = [&](unsigned int Diag) { + std::string TemplateArgString = S.getTemplateArgumentBindingsText( + TD->getTemplateParameters(), TempList.data(), TempList.size()); + S.Diag(CSE->getBeginLoc(), Diag) + << const_cast(CSE) + << TemplateArgString; + }; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TempList); + + ExprResult Result = S.SubstExpr( + const_cast(CSE), MLTAL); + if (!Result.isUsable() || Result.isInvalid()) { + // C++ [temp.constr.normal] + // If any such substitution results in an invalid type or + // expression, the program is ill-formed; no diagnostic is required. + + // A diagnostic was already emitted from the substitution , but + // we'll let the user know why it's not SFINAEd from them. + DiagnoseSubstitutionError( + diag::note_could_not_normalize_argument_substitution_failed); + return llvm::Optional{}; + } + ParameterMapping = cast(Result.get()) + ->getTemplateArgsAsWritten(); + + TemplateArgumentListInfo SubstTALI(ParameterMapping->LAngleLoc, + ParameterMapping->RAngleLoc); + for (auto &Arg : ParameterMapping->arguments()) + SubstTALI.addArgument(Arg); + llvm::SmallVector Converted; + bool Failure = S.CheckTemplateArgumentList( + CSE->getNamedConcept(), CSE->getBeginLoc(), SubstTALI, + /*PartialTemplateArgs=*/false, Converted, + /*UpdateArgsWithConversions=*/true, &InstantiationDependent); + // The case is this: + // + // template + // concept C1 = true; + // + // template + // concept C2 = C1; // After substituting Ts = {T}, the + // // resulting argument list does not match + // // the parameter list. + // + // template + // void foo() requires C2 {} + // + // This case should be checked when substituting into C1, and will + // be caught by the if above. + assert(!Failure && + "Template argument list match should have been checked during " + "substitution."); + if (InstantiationDependent) + // The case is this: + // + // template + // concept C1 = true; + // + // template + // concept C2 = C1; // After substituting Us = {Ts}, we cannot + // // match arguments to parameters. + // + // template + // void foo() requires C2 {} + // + // Treat the CSE as an atomic expression. + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, ParameterMapping)}; + + return fromConstraintExpr(S, CSE->getNamedConcept()->getConstraintExpr(), + CSE->getNamedConcept(), ParameterMapping); + } + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, ParameterMapping)}; + } + + static llvm::Optional fromConstraintExprs(Sema &S, + ArrayRef E) { + assert(E.size() != 0); + auto First = fromConstraintExpr(S, E[0]); + if (E.size() == 1) + return First; + auto Second = fromConstraintExpr(S, E[1]); + if (!Second) + return llvm::Optional{}; + llvm::Optional Conjunction; + Conjunction.emplace(S.Context, std::move(*First), std::move(*Second), + CCK_Conjunction); + for (unsigned I = 2; I < E.size(); ++I) { + auto Next = fromConstraintExpr(S, E[I]); + if (!Next) + return llvm::Optional{}; + NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction), + std::move(*Next), CCK_Conjunction); + *Conjunction = std::move(NewConjunction); + } + return Conjunction; + } +}; +} // namespace + +using NormalForm = + llvm::SmallVector, 4>; + +static NormalForm makeCNF(const NormalizedConstraint &Normalized) { + if (Normalized.isAtomic()) + return {{Normalized.getAtomicConstraint()}}; + + NormalForm LCNF = makeCNF(Normalized.getLHS()); + NormalForm RCNF = makeCNF(Normalized.getRHS()); + if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) { + LCNF.reserve(LCNF.size() + RCNF.size()); + while (!RCNF.empty()) + LCNF.push_back(std::move(RCNF.pop_back_val())); + return LCNF; + } + + // Disjunction + NormalForm Res; + Res.reserve(LCNF.size() * RCNF.size()); + for (auto &LDisjunction : LCNF) + for (auto &RDisjunction : RCNF) { + NormalForm::value_type Combined; + Combined.reserve(LDisjunction.size() + RDisjunction.size()); + std::copy(LDisjunction.begin(), LDisjunction.end(), + std::back_inserter(Combined)); + std::copy(RDisjunction.begin(), RDisjunction.end(), + std::back_inserter(Combined)); + Res.emplace_back(Combined); + } + return Res; +} + +static NormalForm makeDNF(const NormalizedConstraint &Normalized) { + if (Normalized.isAtomic()) + return {{Normalized.getAtomicConstraint()}}; + + NormalForm LDNF = makeDNF(Normalized.getLHS()); + NormalForm RDNF = makeDNF(Normalized.getRHS()); + if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) { + LDNF.reserve(LDNF.size() + RDNF.size()); + while (!RDNF.empty()) + LDNF.push_back(std::move(RDNF.pop_back_val())); + return LDNF; + } + + // Conjunction + NormalForm Res; + Res.reserve(LDNF.size() * RDNF.size()); + for (auto &LConjunction : LDNF) { + for (auto &RConjunction : RDNF) { + NormalForm::value_type Combined; + Combined.reserve(LConjunction.size() + RConjunction.size()); + std::copy(LConjunction.begin(), LConjunction.end(), + std::back_inserter(Combined)); + std::copy(RConjunction.begin(), RConjunction.end(), + std::back_inserter(Combined)); + Res.emplace_back(Combined); + } + } + return Res; +} + +static bool subsumes(Sema &S, ArrayRef P, + ArrayRef Q) { + // C++ [temp.constr.order] p2 + // In order to determine if a constraint P subsumes a constraint Q, P is + // transformed into disjunctive normal form, and Q is transformed into + // conjunctive normal form. [...] + auto PNormalized = NormalizedConstraint::fromConstraintExprs(S, P); + if (!PNormalized) + // Program is ill formed at this point. + return false; + const NormalForm PDNF = makeDNF(*PNormalized); + + auto QNormalized = NormalizedConstraint::fromConstraintExprs(S, Q); + if (!QNormalized) + // Program is ill formed at this point. + return false; + const NormalForm QCNF = makeCNF(*QNormalized); + + // C++ [temp.constr.order] p2 + // Then, P subsumes Q if and only if, for every disjunctive clause Pi in the + // disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in + // the conjuctive normal form of Q, where [...] + for (const auto &Pi : PDNF) { + for (const auto &Qj : QCNF) { + // C++ [temp.constr.order] p2 + // - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if + // and only if there exists an atomic constraint Pia in Pi for which + // there exists an atomic constraint, Qjb, in Qj such that Pia + // subsumes Qjb. + bool Found = false; + for (const AtomicConstraint *Pia : Pi) { + for (const AtomicConstraint *Qjb : Qj) { + if (Pia->subsumes(S.Context, *Qjb)) { + Found = true; + break; + } + } + if (Found) + break; + } + if (!Found) { + return false; + } + } + } + return true; +} + +bool Sema::IsMoreConstrained(NamedDecl *D1, ArrayRef AC1, + NamedDecl *D2, ArrayRef AC2) { + if (AC1.empty()) + return AC2.empty(); + if (AC2.empty()) + // TD1 has associated constraints and TD2 does not. + return true; + + std::pair Key{D1, D2}; + auto CacheEntry = SubsumptionCache.find(Key); + if (CacheEntry != SubsumptionCache.end()) { + return CacheEntry->second; + } + bool Subsumes = subsumes(*this, AC1, AC2); + SubsumptionCache.try_emplace(Key, Subsumes); + return Subsumes; } \ No newline at end of file Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -13977,8 +13977,16 @@ std::string InnerCondDescription; std::tie(InnerCond, InnerCondDescription) = findFailedBooleanCondition(Converted.get()); - if (InnerCond && !isa(InnerCond) - && !isa(InnerCond)) { + if (InnerCond && isa(InnerCond)) { + // Drill down into concept specialization expressions to see why they + // weren't satisfied. + Diag(StaticAssertLoc, diag::err_static_assert_failed) + << !AssertMessage << Msg.str() << AssertExpr->getSourceRange(); + ConstraintSatisfaction Satisfaction; + if (!CheckConstraintSatisfaction(InnerCond, Satisfaction)) + DiagnoseUnsatisfiedConstraint(Satisfaction); + } else if (InnerCond && !isa(InnerCond) + && !isa(InnerCond)) { Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed) << InnerCondDescription << !AssertMessage << Msg.str() << InnerCond->getSourceRange(); Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -581,6 +581,10 @@ TemplateArgumentList *TemplateArgs; unsigned CallArgIndex; }; + struct CNSInfo { + TemplateArgumentList *TemplateArgs; + ConstraintSatisfaction Satisfaction; + }; } /// Convert from Sema's representation of template deduction information @@ -651,6 +655,14 @@ } break; + case Sema::TDK_ConstraintsNotSatisfied: { + CNSInfo *Saved = new (Context) CNSInfo; + Saved->TemplateArgs = Info.take(); + Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction; + Result.Data = Saved; + break; + } + case Sema::TDK_Success: case Sema::TDK_NonDependentConversionFailure: llvm_unreachable("not a deduction failure"); @@ -691,6 +703,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; @@ -716,6 +737,7 @@ case Sema::TDK_NonDeducedMismatch: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return TemplateParameter(); case Sema::TDK_Incomplete: @@ -759,6 +781,9 @@ case Sema::TDK_SubstitutionFailure: return static_cast(Data); + case Sema::TDK_ConstraintsNotSatisfied: + return static_cast(Data)->TemplateArgs; + // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: break; @@ -779,6 +804,7 @@ case Sema::TDK_SubstitutionFailure: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return nullptr; case Sema::TDK_IncompletePack: @@ -810,6 +836,7 @@ case Sema::TDK_SubstitutionFailure: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return nullptr; case Sema::TDK_Inconsistent: @@ -1225,6 +1252,8 @@ return NewTarget != OldTarget; } + // TODO: Concepts: Check function trailing requires clauses here. + // The signatures match; this is not an overload. return false; } @@ -10109,6 +10138,21 @@ 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(DeductionFailure.Data)->Satisfaction); + return; + } case Sema::TDK_TooManyArguments: case Sema::TDK_TooFewArguments: DiagnoseArityMismatch(S, Found, Templated, NumArgs); @@ -10522,15 +10566,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 @@ -1049,11 +1049,11 @@ // Check that we have valid decl-specifiers specified. auto CheckValidDeclSpecifiers = [this, &D] { // C++ [temp.param] - // p1 + // p1 // template-parameter: // ... // parameter-declaration - // p2 + // p2 // ... A storage class shall not be specified in a template-parameter // declaration. // [dcl.typedef]p1: @@ -3120,8 +3120,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()) @@ -3135,9 +3134,17 @@ // Check that the template argument list is well-formed for this // template. + bool InstantiationDependentMatch; SmallVector Converted; if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, - false, Converted)) + false, Converted, + /*UpdateArgsWithConversion=*/true, + &InstantiationDependentMatch)) + return QualType(); + + if (!InstantiationDependentMatch + && EnsureTemplateArgumentListConstraints(Template, Converted, + SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) return QualType(); QualType CanonType; @@ -3145,6 +3152,7 @@ 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()) @@ -3726,9 +3734,17 @@ // Check that the template argument list is well-formed for this // template. + bool InstantiationDependent; SmallVector Converted; if (CheckTemplateArgumentList(VarTemplate, TemplateNameLoc, TemplateArgs, - false, Converted)) + false, Converted, + /*UpdateArgsWithConversion=*/true, + &InstantiationDependent)) + return true; + + if (!InstantiationDependent + && EnsureTemplateArgumentListConstraints(VarTemplate, Converted, + SourceRange(TemplateNameLoc, RAngleLoc))) return true; // Find the variable template (partial) specialization declaration that @@ -3751,7 +3767,9 @@ } if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(), - Converted)) { + Converted) + && (!Context.getLangOpts().ConceptsTS + || !TemplateParams->hasAssociatedConstraints())) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical @@ -3771,7 +3789,8 @@ if (IsPartialSpecialization) // FIXME: Template parameter list matters too - PrevDecl = VarTemplate->findPartialSpecialization(Converted, InsertPos); + PrevDecl = VarTemplate->findPartialSpecialization(Converted, + TemplateParams->getAssociatedConstraints(), InsertPos); else PrevDecl = VarTemplate->findSpecialization(Converted, InsertPos); @@ -3902,11 +3921,18 @@ assert(Template && "A variable template id without template?"); // Check that the template argument list is well-formed for this template. + bool InstantiationDependentMatch; SmallVector Converted; if (CheckTemplateArgumentList( Template, TemplateNameLoc, const_cast(TemplateArgs), false, - Converted)) + Converted, /*UpdateArgsWithConversion=*/true, + &InstantiationDependentMatch)) + return true; + + if (!InstantiationDependentMatch + && EnsureTemplateArgumentListConstraints(Template, Converted, + SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()))) return true; // Find the variable template specialization declaration that @@ -4097,7 +4123,7 @@ /*UpdateArgsWithConversion=*/false)) return ExprError(); - bool IsSatisfied = true; + ConstraintSatisfaction Satisfaction; bool IsInstantiationDependent = false; for (TemplateArgument &Arg : Converted) { if (Arg.isInstantiationDependent()) { @@ -4105,23 +4131,21 @@ break; } } - if (!IsInstantiationDependent) { - InstantiatingTemplate Inst(*this, ConceptNameLoc, - InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted, - SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc, - TemplateArgs->getRAngleLoc())); - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(Converted); - if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, - NamedConcept->getConstraintExpr(), - IsSatisfied)) + if (!IsInstantiationDependent) + if (CheckConstraintSatisfaction(NamedConcept, + {NamedConcept->getConstraintExpr()}, + Converted, + SourceRange(SS.isSet() ? SS.getBeginLoc() : + ConceptNameLoc, + TemplateArgs->getRAngleLoc()), + Satisfaction)) return ExprError(); - } + return ConceptSpecializationExpr::Create(Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept, ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted, - IsSatisfied); + std::move(Satisfaction)); } ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, @@ -4165,7 +4189,7 @@ TemplateKWLoc, TemplateArgs); } - if (R.getAsSingle() && !AnyDependentArguments()) { + if (R.getAsSingle()) { return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo().getBeginLoc(), R.getFoundDecl(), @@ -5057,7 +5081,8 @@ TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, SmallVectorImpl &Converted, - bool UpdateArgsWithConversions) { + bool UpdateArgsWithConversions, bool *InstantiationDependent) { + // 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. @@ -5161,6 +5186,10 @@ ++ArgIdx; } + // We cannot determine yet whether the arguments are well-formed. + if (InstantiationDependent) + *InstantiationDependent = true; + return false; } @@ -5173,6 +5202,8 @@ Converted.push_back( TemplateArgument::CreatePackCopy(Context, ArgumentPack)); + if (InstantiationDependent) + *InstantiationDependent = false; return false; } @@ -5311,6 +5342,9 @@ if (UpdateArgsWithConversions) TemplateArgs = std::move(NewArgs); + if (InstantiationDependent) + *InstantiationDependent = false; + return false; } @@ -6948,6 +6982,7 @@ bool Complain, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { + // TODO: Concepts: Check constrained-parameter constraints here. // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -7620,9 +7655,17 @@ // Check that the template argument list is well-formed for this // template. + bool InstantiationDependent; SmallVector Converted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, - TemplateArgs, false, Converted)) + TemplateArgs, false, Converted, + /*UpdateArgsWithConversion=*/true, + &InstantiationDependent)) + return true; + + if (!InstantiationDependent + && EnsureTemplateArgumentListConstraints(ClassTemplate, Converted, + SourceRange(TemplateNameLoc, RAngleLoc))) return true; // Find the class template (partial) specialization declaration that @@ -7649,7 +7692,8 @@ if (isPartialSpecialization) // FIXME: Template parameter list matters, too - PrevDecl = ClassTemplate->findPartialSpecialization(Converted, InsertPos); + PrevDecl = ClassTemplate->findPartialSpecialization(Converted, + TemplateParams->getAssociatedConstraints(), InsertPos); else PrevDecl = ClassTemplate->findSpecialization(Converted, InsertPos); @@ -7673,7 +7717,9 @@ Converted); if (Context.hasSameType(CanonType, - ClassTemplate->getInjectedClassNameSpecialization())) { + ClassTemplate->getInjectedClassNameSpecialization()) + && (!Context.getLangOpts().ConceptsTS + || !TemplateParams->hasAssociatedConstraints())) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical @@ -8841,11 +8887,20 @@ // Check that the template argument list is well-formed for this // template. + bool InstantiationDependent; SmallVector Converted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, - TemplateArgs, false, Converted)) + TemplateArgs, false, Converted, + /*UpdateArgsWithConversion=*/true, + &InstantiationDependent)) return true; + if (!InstantiationDependent + && EnsureTemplateArgumentListConstraints(ClassTemplate, Converted, + SourceRange(TemplateNameLoc, RAngleLoc))) { + return true; + } + // Find the class template specialization declaration that // corresponds to these arguments. void *InsertPos = nullptr; @@ -10275,3 +10330,9 @@ MissingImportKind::PartialSpecialization, /*Recover*/true); } + + // p1 + // template-parameter: + // ... + // parameter-declaration + // p2 \ No newline at end of file Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -2709,6 +2709,23 @@ static constexpr bool value = true; }; +template +static Sema::TemplateDeductionResult +CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template, + ArrayRef DeducedArgs, + TemplateDeductionInfo& Info) { + llvm::SmallVector AssociatedConstraints; + Template->getAssociatedConstraints(AssociatedConstraints); + if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, + DeducedArgs, Info.getLocation(), + Info.AssociatedConstraintsSatisfaction) + || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); + return Sema::TDK_ConstraintsNotSatisfied; + } + return Sema::TDK_Success; +} + /// Complete template argument deduction for a partial specialization. template static typename std::enable_if::value, @@ -2786,6 +2803,9 @@ if (Trap.hasErrorOccurred()) return Sema::TDK_SubstitutionFailure; + if (auto Result = CheckDeducedArgumentConstraints(S, Partial, Builder, Info)) + return Result; + return Sema::TDK_Success; } @@ -2828,10 +2848,13 @@ if (Trap.hasErrorOccurred()) return Sema::TDK_SubstitutionFailure; + if (auto Result = CheckDeducedArgumentConstraints(S, Template, Builder, + Info)) + return Result; + return Sema::TDK_Success; } - /// Perform template argument deduction to determine whether /// the given template arguments match the given class template /// partial specialization per C++ [temp.class.spec.match]. @@ -3333,6 +3356,11 @@ PartialOverloading)) return Result; + if (TemplateDeductionResult Result + = CheckDeducedArgumentConstraints(*this, FunctionTemplate, Builder, + 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, @@ -4865,6 +4893,18 @@ TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, unsigned NumCallArguments2) { + + auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * { + llvm::SmallVector AC1, AC2; + FT1->getAssociatedConstraints(AC1); + FT2->getAssociatedConstraints(AC2); + bool MoreConstrained1 = IsMoreConstrained(FT1, AC1, FT2, AC2); + bool MoreConstrained2 = IsMoreConstrained(FT2, AC2, FT1, AC1); + if (MoreConstrained1 == MoreConstrained2) + return nullptr; + return MoreConstrained1 ? FT1 : FT2; + }; + bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, NumCallArguments1); bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, @@ -4874,7 +4914,7 @@ return Better1 ? FT1 : FT2; if (!Better1 && !Better2) // Neither is better than the other - return nullptr; + return JudgeByConstraints(); // FIXME: This mimics what GCC implements, but doesn't match up with the // proposed resolution for core issue 692. This area needs to be sorted out, @@ -4884,7 +4924,7 @@ if (Variadic1 != Variadic2) return Variadic1? FT2 : FT1; - return nullptr; + return JudgeByConstraints(); } /// Determine if the two templates are equivalent. @@ -5079,8 +5119,16 @@ bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info); bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info); - if (Better1 == Better2) - return nullptr; + if (Better1 == Better2) { + llvm::SmallVector AC1, AC2; + PS1->getAssociatedConstraints(AC1); + PS2->getAssociatedConstraints(AC2); + bool MoreConstrained1 = IsMoreConstrained(PS1, AC1, PS2, AC2); + bool MoreConstrained2 = IsMoreConstrained(PS2, AC2, PS1, AC1); + if (MoreConstrained1 == MoreConstrained2) + return nullptr; + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5090,11 +5138,23 @@ ClassTemplateDecl *Primary = Spec->getSpecializedTemplate(); QualType PrimaryT = Primary->getInjectedClassNameSpecialization(); QualType PartialT = Spec->getInjectedSpecializationType(); + auto JudgeByConstraints = [&] { + llvm::SmallVector PrimaryAC, SpecAC; + Primary->getAssociatedConstraints(PrimaryAC); + Spec->getAssociatedConstraints(SpecAC); + bool MoreConstrainedPrimary = IsMoreConstrained(Primary, PrimaryAC, Spec, + SpecAC); + bool MoreConstrainedSpec = IsMoreConstrained(Spec, SpecAC, Primary, + PrimaryAC); + if (MoreConstrainedPrimary == MoreConstrainedSpec) + return false; + return MoreConstrainedSpec; + }; if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info)) - return false; - if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)) { + return JudgeByConstraints(); + if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)){ Info.clearSFINAEDiagnostic(); - return false; + return JudgeByConstraints(); } return true; } @@ -5119,8 +5179,17 @@ bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info); bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info); - if (Better1 == Better2) - return nullptr; + if (Better1 == Better2) { + llvm::SmallVector AC1, AC2; + PS1->getAssociatedConstraints(AC1); + PS2->getAssociatedConstraints(AC2); + bool MoreConstrained1 = IsMoreConstrained(PS1, AC1, PS2, AC2); + bool MoreConstrained2 = IsMoreConstrained(PS2, AC2, PS1, AC1); + if (MoreConstrained1 == MoreConstrained2) { + return nullptr; + } + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5140,11 +5209,25 @@ CanonTemplate, PrimaryArgs); QualType PartialT = Context.getTemplateSpecializationType( CanonTemplate, Spec->getTemplateArgs().asArray()); + + auto JudgeByConstraints = [&] { + llvm::SmallVector PrimaryAC, SpecAC; + Primary->getAssociatedConstraints(PrimaryAC); + Spec->getAssociatedConstraints(SpecAC); + bool MoreConstrainedPrimary = IsMoreConstrained(Primary, PrimaryAC, Spec, + SpecAC); + bool MoreConstrainedSpec = IsMoreConstrained(Spec, SpecAC, Primary, + PrimaryAC); + if (MoreConstrainedPrimary == MoreConstrainedSpec) + return false; + return MoreConstrainedSpec; + }; + if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info)) - return false; - if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)) { + return JudgeByConstraints(); + if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)){ Info.clearSFINAEDiagnostic(); - return false; + return JudgeByConstraints(); } return true; } Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -358,7 +358,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintsCheck, TemplateDecl *Template, + ConstraintsCheck, NamedDecl *Template, ArrayRef TemplateArgs, SourceRange InstantiationRange) : InstantiatingTemplate( SemaRef, CodeSynthesisContext::ConstraintsCheck, @@ -367,7 +367,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintSubstitution, TemplateDecl *Template, + ConstraintSubstitution, NamedDecl *Template, sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange) : InstantiatingTemplate( SemaRef, CodeSynthesisContext::ConstraintSubstitution, @@ -677,24 +677,27 @@ case CodeSynthesisContext::Memoization: break; - case CodeSynthesisContext::ConstraintsCheck: - if (auto *CD = dyn_cast(Active->Entity)) { - SmallVector TemplateArgsStr; - llvm::raw_svector_ostream OS(TemplateArgsStr); - CD->printName(OS); - printTemplateArgumentList(OS, Active->template_arguments(), - getPrintingPolicy()); - Diags.Report(Active->PointOfInstantiation, - diag::note_concept_specialization_here) - << OS.str() - << Active->InstantiationRange; - break; + case CodeSynthesisContext::ConstraintsCheck: { + unsigned DiagID = 0; + if (isa(Active->Entity)) + DiagID = diag::note_concept_specialization_here; + else if (isa(Active->Entity)) + DiagID = diag::note_checking_constraints_for_template_id_here; + else if (isa(Active->Entity)) + DiagID = diag::note_checking_constraints_for_var_spec_id_here; + else { + assert(isa(Active->Entity)); + DiagID = diag::note_checking_constraints_for_class_spec_id_here; } - // TODO: Concepts - implement this for constrained templates and partial - // specializations. - llvm_unreachable("only concept constraints are supported right now"); + SmallVector TemplateArgsStr; + llvm::raw_svector_ostream OS(TemplateArgsStr); + cast(Active->Entity)->printName(OS); + printTemplateArgumentList(OS, Active->template_arguments(), + getPrintingPolicy()); + Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str() + << Active->InstantiationRange; break; - + } case CodeSynthesisContext::ConstraintSubstitution: Diags.Report(Active->PointOfInstantiation, diag::note_constraint_substitution_here) Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3173,12 +3173,21 @@ // Check that the template argument list is well-formed for this // class template. + bool InstantiationDependent; SmallVector Converted; if (SemaRef.CheckTemplateArgumentList(InstClassTemplate, D->getLocation(), InstTemplateArgs, false, - Converted)) + Converted, + /*UpdateArgsWithConversion=*/true, + &InstantiationDependent)) + return nullptr; + + if (!InstantiationDependent + && SemaRef.EnsureTemplateArgumentListConstraints(InstClassTemplate, + Converted, + D->getLocation())) return nullptr; // Figure out where to insert this class template explicit specialization @@ -3290,11 +3299,19 @@ return nullptr; // Check that the template argument list is well-formed for this template. + bool InstantiationDependent; SmallVector Converted; if (SemaRef.CheckTemplateArgumentList( VarTemplate, VarTemplate->getBeginLoc(), const_cast(VarTemplateArgsInfo), false, - Converted)) + Converted, /*UpdateArgsWithConversion=*/true, + &InstantiationDependent)) + return nullptr; + + if (!InstantiationDependent + && SemaRef.EnsureTemplateArgumentListConstraints(VarTemplate, Converted, + SourceRange(VarTemplate->getLocation(), + VarTemplateArgsInfo.getRAngleLoc()))) return nullptr; // Find the variable template specialization declaration that @@ -3485,7 +3502,8 @@ // in the member template's set of class template partial specializations. void *InsertPos = nullptr; ClassTemplateSpecializationDecl *PrevDecl - = ClassTemplate->findPartialSpecialization(Converted, InsertPos); + = ClassTemplate->findPartialSpecialization(Converted, + InstParams->getAssociatedConstraints(), InsertPos); // Build the canonical type that describes the converted template // arguments of the class template partial specialization. @@ -3609,7 +3627,8 @@ // in the member template's set of variable template partial specializations. void *InsertPos = nullptr; VarTemplateSpecializationDecl *PrevDecl = - VarTemplate->findPartialSpecialization(Converted, InsertPos); + VarTemplate->findPartialSpecialization(Converted, + InstParams->getAssociatedConstraints(), InsertPos); // Build the canonical type that describes the converted template // arguments of the variable template partial specialization. Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -2233,12 +2233,14 @@ void ASTDeclReader::VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { - RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D); - + // We need to read the template params first because redeclarable is going to + // need them for profiling TemplateParameterList *Params = Record.readTemplateParameterList(); D->TemplateParams = Params; D->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D); + // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( @@ -2334,12 +2336,12 @@ /// using Template(Partial)SpecializationDecl as input type. void ASTDeclReader::VisitVarTemplatePartialSpecializationDecl( VarTemplatePartialSpecializationDecl *D) { - RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D); - TemplateParameterList *Params = Record.readTemplateParameterList(); D->TemplateParams = Params; D->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D); + // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -721,14 +721,31 @@ E->TemplateKWLoc = Record.readSourceLocation(); E->ConceptNameLoc = Record.readSourceLocation(); E->FoundDecl = ReadDeclAs(); - E->NamedConcept.setPointer(ReadDeclAs()); + E->NamedConcept = ReadDeclAs(); const ASTTemplateArgumentListInfo *ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); llvm::SmallVector Args; for (unsigned I = 0; I < NumTemplateArgs; ++I) Args.push_back(Record.readTemplateArgument()); E->setTemplateArguments(ArgsAsWritten, Args); - E->NamedConcept.setInt(Record.readInt() == 1); + ConstraintSatisfaction Satisfaction; + Satisfaction.IsSatisfied = Record.readInt(); + if (!Satisfaction.IsSatisfied) { + unsigned NumDetailRecords = Record.readInt(); + for (unsigned i = 0; i != NumDetailRecords; ++i) { + Expr *ConstraintExpr = Record.readExpr(); + bool IsDiagnostic = Record.readInt(); + if (IsDiagnostic) { + SourceLocation DiagLocation = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + Satisfaction.Details.emplace_back( + ConstraintExpr, new (Record.getContext()) + ConstraintSatisfaction::SubstitutionDiagnostic{ + DiagLocation, DiagMessage}); + } else + Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); + } + } } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1484,11 +1484,11 @@ void ASTDeclWriter::VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { - VisitClassTemplateSpecializationDecl(D); - Record.AddTemplateParameterList(D->getTemplateParameters()); Record.AddASTTemplateArgumentListInfo(D->getTemplateArgsAsWritten()); + VisitClassTemplateSpecializationDecl(D); + // These are read/set from/to the first declaration. if (D->getPreviousDecl() == nullptr) { Record.AddDeclRef(D->getInstantiatedFromMember()); @@ -1544,11 +1544,11 @@ void ASTDeclWriter::VisitVarTemplatePartialSpecializationDecl( VarTemplatePartialSpecializationDecl *D) { - VisitVarTemplateSpecializationDecl(D); - Record.AddTemplateParameterList(D->getTemplateParameters()); Record.AddASTTemplateArgumentListInfo(D->getTemplateArgsAsWritten()); + VisitVarTemplateSpecializationDecl(D); + // These are read/set from/to the first declaration. if (D->getPreviousDecl() == nullptr) { Record.AddDeclRef(D->getInstantiatedFromMember()); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -397,7 +397,24 @@ Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); for (const TemplateArgument &Arg : TemplateArgs) Record.AddTemplateArgument(Arg); - Record.push_back(E->isSatisfied()); + const ConstraintSatisfaction &Satisfaction = E->getSatisfaction(); + Record.push_back(Satisfaction.IsSatisfied); + if (!Satisfaction.IsSatisfied) { + Record.push_back(Satisfaction.Details.size()); + for (const auto &DetailRecord : Satisfaction.Details) { + Record.AddStmt(const_cast(DetailRecord.first)); + auto *Diag = + DetailRecord.second + .dyn_cast(); + Record.push_back(Diag != nullptr); + if (Diag) { + Record.AddSourceLocation(Diag->first); + Record.AddString(Diag->second); + } else + Record.AddStmt(DetailRecord.second.get()); + } + } + 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 @@ -72,6 +72,15 @@ static_assert(IsTypePredicate); static_assert(!IsTypePredicate); +template +concept OneOf = (Same || ...); + +template +constexpr bool S = OneOf; + +static_assert(S); +static_assert(!S); + namespace piecewise_substitution { template concept True = true; @@ -151,3 +160,25 @@ struct X { static constexpr bool a = SameSize; }; static_assert(X::a); + +// static_assert concept diagnostics +template +concept Large = sizeof(T) > 100; +// expected-note@-1 2{{because 'sizeof(small) > 100' (1 > 100) evaluated to false}} + +struct small { }; +static_assert(Large); +// expected-error@-1 {{static_assert failed}} +// expected-note@-2 {{because 'small' does not satisfy 'Large'}} +static_assert(Large, "small isn't large"); +// expected-error@-1 {{static_assert failed "small isn't large"}} +// expected-note@-2 {{because 'small' does not satisfy 'Large'}} + +// Make sure access-checking can fail a concept specialization + +class T4 { static constexpr bool f = true; }; +template concept AccessPrivate = T{}.f; +// expected-note@-1{{because substituted constraint expression is ill-formed: 'f' is a private member of 'T4'}} +static_assert(AccessPrivate); +// expected-error@-1{{static_assert failed}} +// expected-note@-2{{because 'T4' does not satisfy 'AccessPrivate'}} 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,43 @@ +// 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@-1{{because 'is_ptr_v' evaluated to false}} +auto dereference(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}} + // expected-note@-1{{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 {{because 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; +} + + +template requires !((T{} - T{}) && (T{} + T{})) || false +// expected-note@-1{{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}} +// expected-note@-2{{and 'false' evaluated to false}} +auto bar(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'}} +static_assert(bar(A{})); // expected-error {{no matching function for call to 'bar'}} \ 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,92 @@ +// 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}} + +template +struct B { using type = typename T::type; }; // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} + +template requires B::type // expected-note{{in instantiation of template class 'B' requested here}} + // expected-note@-1{{while substituting template arguments into constraint expression here}} +struct C { }; + +template requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}} +struct D { }; + +static_assert(C{}); // expected-note{{while checking constraint satisfaction for template 'C' required here}} +static_assert(D{}); // expected-note{{while checking constraint satisfaction for template 'D' required 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,67 @@ +// 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]}} + + template + struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}} + + template + struct B {}; + + template requires A::type // expected-note{{in instantiation of template class 'class_templates::A' requested here}} + // expected-note@-1{{while substituting template arguments into constraint expression here}} + struct B {}; + + template requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}} + struct B {}; + + static_assert((B{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B' required here}} + // expected-note@-1{{while checking constraint satisfaction for class template partial specialization 'B' required here}} + // expected-note@-2{{during template argument deduction for class template partial specialization 'B' [with T = int *]}} + // expected-note@-3{{during template argument deduction for class template partial specialization 'B' [with T = int]}} + // expected-note@-4 2{{in instantiation of template class 'class_templates::B' requested here}} +} + +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); + + template + struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}} + + template + constexpr bool v1 = false; + + template requires A::type // expected-note{{in instantiation of template class 'variable_templates::A' requested here}} + // expected-note@-1{{while substituting template arguments into constraint expression here}} + constexpr bool v1 = true; + + template requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}} + constexpr bool v1 = true; + + static_assert(v1); // expected-note{{while checking constraint satisfaction for variable template partial specialization 'v1' required here}} + // expected-note@-1{{while checking constraint satisfaction for variable template partial specialization 'v1' required here}} + // expected-note@-2{{during template argument deduction for variable template partial specialization 'v1' [with T = int *]}} + // expected-note@-3{{during template argument deduction for variable template partial specialization 'v1' [with T = int]}} + // expected-error@-4{{static_assert failed due to requirement 'v1'}} + +} \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +namespace maybe_incorrect_args { + template + concept C = true; + + // diagnostic expected here - the Cs are treated as atomic, and since + // they originate from different source-level constructs, they do not subsume + // each other. + template requires C + struct A {}; // expected-note{{template is declared here}} + + template requires C && true + struct A {}; // expected-error{{class template partial specialization is not more specialized than the primary template}} +} + +namespace ill_formed_subst { + + template + struct B; // expected-note 2{{template is declared here}} + + template + concept C1 = true; + + template + concept C2 = C1::foo>; + // expected-error@-1 2{{implicit instantiation of undefined template 'ill_formed_subst::B'}} + // expected-note@-2 2{{when substituting into C1::foo> [with T = type-parameter-0-0, U = int]. Make sure concept arguments are valid for any substitution}} + + template requires C2 + struct A {}; // expected-note {{template is declared here}} + + template requires C2 && true + struct A {}; // expected-error {{class template partial specialization is not more specialized than the primary template}} +} + +namespace incorrect_args_after_subst { + template + concept C1 = true; // expected-note 2{{template is declared here}} + + template + concept C2 = C1; + // expected-error@-1 2{{too many template arguments for concept 'C1'}} + // expected-note@-2 2{{when substituting into C1 [with Ts = ]. Make sure concept arguments are valid for any substitution}} + + template requires C2 + struct A {}; // expected-note{{template is declared here}} + + template requires C2 && true + struct A {}; // expected-error{{class template partial specialization is not more specialized than the primary template}} +} + +namespace maybe_incorrect_args_after_subst { + template + concept C1 = true; + + template + concept C2 = C1; + + // no diagnostic expected here - C1 is treated as atomic, and since it + // originates at the same source level construct, the specialized subsumes the + // primary. + template requires C2 + struct A {}; + + template requires C2 && true + struct A {}; +} Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template requires sizeof(T) >= 4 +class A{}; // expected-note{{template is declared here}} + +template requires sizeof(T) >= 4 && sizeof(T) <= 10 +class A{}; // expected-error{{class template partial specialization is not more specialized than the primary template}} + +template +concept C1 = sizeof(T) >= 4; + +template requires C1 +class B{}; + +template requires C1 && sizeof(T) <= 10 +class B{}; + +template +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template +class C{}; + +template requires C1 +class C{}; + +template +class D{}; // expected-note{{previous definition is here}} + +template +class D{}; // expected-error{{class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} expected-error{{redefinition of 'D'}} + +template requires C1 // expected-note{{previous template declaration is here}} +class E{}; + +template // expected-error{{associated constraints differ in template redeclaration}} +class E{}; // expected-error{{class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template +struct F{ enum{ value = 1 }; }; + +template requires C1 && C2 +struct F{ enum{ value = 2 }; }; + +template requires C1 || C2 +struct F{ enum{ value = 3 }; }; + +static_assert(F::value == 2); +static_assert(F::value == 3); +static_assert(F::value == 1); + +template +concept C3 = true; + +template +concept C4 = C3; + +// C4 is normalized to C3@ because there is a pack +// expansion into a non-pack parameter. therefore the two C4 subsume each +// other and the following is non-ambiguous. + +template requires C4 +struct G { }; + +template requires C4 && C4 +struct G { }; + +// Here the two C3s cannot be normalized further, and do not subsume each other +// because they originate in two different locations in code. + +template requires C3 +struct H { }; // expected-note {{template is declared here}} + +template requires C3 && C4 +struct H { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +// Make sure atomic constraints subsume each other only if their parameter +// mappings are identical. + +template requires C2 +struct I { }; // expected-note {{template is declared here}} + +template requires C2 +struct I { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template requires sizeof(T) >= 4 +bool a() { return false; } // expected-note {{candidate function [with T = unsigned int]}} + +template requires sizeof(T) >= 4 && sizeof(T) <= 10 +bool a() { return true; } // expected-note {{candidate function [with T = unsigned int]}} + +bool av = a(); // expected-error {{call to 'a' is ambiguous}} + +template +concept C1 = sizeof(T) >= 4; + +template requires C1 +constexpr bool b() { return false; } + +template requires C1 && sizeof(T) <= 10 +constexpr bool b() { return true; } + +static_assert(b()); +static_assert(!b()); + +template +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template +bool c() { return false; } + +template requires C1 +bool c() { return true; } + +template requires C1 +constexpr bool d() { return false; } + +template +constexpr bool d() { return true; } + +static_assert(!d()); + +template +constexpr int e() { return 1; } + +template requires C1 && C2 +constexpr int e() { return 2; } + +template requires C1 || C2 +constexpr int e() { return 3; } + +static_assert(e() == 2); +static_assert(e() == 3); +static_assert(e() == 1); + +template +concept BiggerThan = sizeof(T) > sizeof(U); + +template +concept BiggerThanInt = BiggerThan; + +template requires BiggerThan +void f() { } +// expected-note@-1 {{candidate function [with T = long long, U = int]}} + +template requires BiggerThanInt +void f() { } +// expected-note@-1 {{candidate function [with T = long long, U = int]}} + +static_assert(sizeof(f())); +// expected-error@-1 {{call to 'f' is ambiguous}} + +template +concept C3 = true; + +template +concept C4 = true && C3; + +template requires C3 +int g() { } + +template requires C4 +int g() { } + +static_assert(sizeof(g())); \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template requires sizeof(T) >= 4 +bool a = false; // expected-note{{template is declared here}} + +template requires sizeof(T) >= 4 && sizeof(T) <= 10 +bool a = true; // expected-error{{variable template partial specialization is not more specialized than the primary template}} + +template +concept C1 = sizeof(T) >= 4; + +template requires C1 +bool b = false; + +template requires C1 && sizeof(T) <= 10 +bool b = true; + +template +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template +bool c = false; + +template requires C1 +bool c = true; + +template +bool d = false; + +template +bool d = true; // expected-error{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template requires C1 +bool e = false; + +template +bool e = true; // expected-error{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template +constexpr int f = 1; + +template requires C1 && C2 +constexpr int f = 2; + +template requires C1 || C2 +constexpr int f = 3; + +static_assert(f == 2); +static_assert(f == 3); +static_assert(f == 1); + + +