Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1790,6 +1790,10 @@ /// the DeclaratorDecl base class. DeclarationNameLoc DNLoc; + /// \brief The constraint-expression introduced by the trailing + /// requires-clause provided in the function declaration, if any. + Expr *TrailingRequiresClause; + /// \brief Specify that this function declaration is actually a function /// template specialization. /// @@ -1830,7 +1834,8 @@ FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, bool isInlineSpecified, - bool isConstexprSpecified) + bool isConstexprSpecified, + Expr *TrailingRequiresClause = nullptr) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), redeclarable_base(C), SClass(S), @@ -1842,7 +1847,8 @@ IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified), InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false), WillHaveBody(false), IsCopyDeductionCandidate(false), - EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {} + EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()), + TrailingRequiresClause(TrailingRequiresClause) {} using redeclarable_base = Redeclarable; @@ -1879,12 +1885,13 @@ StorageClass SC, bool isInlineSpecified = false, bool hasWrittenPrototype = true, - bool isConstexprSpecified = false) { + bool isConstexprSpecified = false, + Expr *TrailingRequiresClause = nullptr) { DeclarationNameInfo NameInfo(N, NLoc); return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC, isInlineSpecified, hasWrittenPrototype, - isConstexprSpecified); + isConstexprSpecified, TrailingRequiresClause); } static FunctionDecl *Create(ASTContext &C, DeclContext *DC, @@ -1894,7 +1901,8 @@ StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, - bool isConstexprSpecified = false); + bool isConstexprSpecified = false, + Expr *TrailingRequiresClause = nullptr); static FunctionDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2148,6 +2156,21 @@ bool willHaveBody() const { return WillHaveBody; } void setWillHaveBody(bool V = true) { WillHaveBody = V; } + /// \brief Get the constraint-expression introduced by the trailing + /// requires-clause in the function/member declaration, or null if no + /// requires-clause was provided. + Expr *getTrailingRequiresClause() { + return TrailingRequiresClause; + } + + const Expr *getTrailingRequiresClause() const { + return TrailingRequiresClause; + } + + void setTrailingRequiresClause(Expr *E) { + TrailingRequiresClause = E; + } + void setPreviousDeclaration(FunctionDecl * PrevDecl); FunctionDecl *getCanonicalDecl() override; Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -1969,9 +1969,10 @@ SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInline, - bool isConstexpr, SourceLocation EndLocation) + bool isConstexpr, SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr) : FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, - SC, isInline, isConstexpr) { + SC, isInline, isConstexpr, TrailingRequiresClause) { if (EndLocation.isValid()) setRangeEnd(EndLocation); } @@ -1984,7 +1985,8 @@ StorageClass SC, bool isInline, bool isConstexpr, - SourceLocation EndLocation); + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr); static CXXMethodDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2413,9 +2415,11 @@ QualType T, TypeSourceInfo *TInfo, bool isExplicitSpecified, bool isInline, bool isImplicitlyDeclared, bool isConstexpr, - InheritedConstructor Inherited) + InheritedConstructor Inherited, + Expr *TrailingRequiresClause = nullptr) : CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, isConstexpr, SourceLocation()), + SC_None, isInline, isConstexpr, SourceLocation(), + TrailingRequiresClause), NumCtorInitializers(0), IsInheritingConstructor((bool)Inherited) { setImplicit(isImplicitlyDeclared); if (Inherited) @@ -2437,7 +2441,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isExplicit, bool isInline, bool isImplicitlyDeclared, bool isConstexpr, - InheritedConstructor Inherited = InheritedConstructor()); + InheritedConstructor Inherited = InheritedConstructor(), + Expr *TrailingRequiresClause = nullptr); /// \brief Iterates through the member/base initializer list. using init_iterator = CXXCtorInitializer **; @@ -2629,9 +2634,11 @@ CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared) + bool isInline, bool isImplicitlyDeclared, + Expr *TrailingRequiresClause = nullptr) : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, /*isConstexpr=*/false, SourceLocation()) + SC_None, isInline, /*isConstexpr=*/false, SourceLocation(), + TrailingRequiresClause) { setImplicit(isImplicitlyDeclared); } @@ -2644,7 +2651,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo* TInfo, bool isInline, - bool isImplicitlyDeclared); + bool isImplicitlyDeclared, + Expr *TrailingRequiresClause = nullptr); static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID); void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg); @@ -2684,9 +2692,11 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicitSpecified, bool isConstexpr, - SourceLocation EndLocation) + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr) : CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, isConstexpr, EndLocation) { + SC_None, isInline, isConstexpr, EndLocation, + TrailingRequiresClause) { IsExplicitSpecified = isExplicitSpecified; } @@ -2702,7 +2712,8 @@ QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicit, bool isConstexpr, - SourceLocation EndLocation); + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr); static CXXConversionDecl *CreateDeserialized(ASTContext &C, unsigned ID); /// Whether this function is marked as explicit explicitly. Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1982,6 +1982,11 @@ } } + // Visit the trailing requires clause, if any. + if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) { + TRY_TO(TraverseStmt(TrailingRequiresClause)); + } + if (CXXConstructorDecl *Ctor = dyn_cast(D)) { // Constructor initializers. for (auto *I : Ctor->inits()) { Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -278,6 +278,10 @@ def warn_cxx98_compat_trailing_return_type : Warning< "trailing return types are incompatible with C++98">, InGroup, DefaultIgnore; +def err_requires_clause_must_come_after_trailing_return : Error< + "trailing return type must come before trailing requires clause">; +def err_requires_clause_on_declarator_not_declaring_a_function : Error< + "trailing requires clause can only be used when declaring a function">; def ext_auto_storage_class : ExtWarn< "'auto' storage class specifier is not permitted in C++11, and will not " "be supported in future releases">, InGroup>; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2429,6 +2429,10 @@ def note_could_not_normalize_unmatched_argument_list_after_subst : Note< "could not substitute template arguments into %0 %1 - the resulting template " "argument list does not match the concept's parameter list">; +def err_constrained_virtual_method : Error< + "a virtual function must not have a requires clause">; +def err_reference_to_function_with_unsatisfied_constraints : Error< + "invalid reference to function %0 - constraints not satisfied">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; @@ -3592,6 +3596,9 @@ def err_addrof_function_disabled_by_enable_if_attr : Error< "cannot take address of function %0 because it has one or more " "non-tautological enable_if conditions">; +def err_addrof_function_constraints_not_satisfied : Error< + "cannot take address of function %0 because its constraints are not " + "satisfied">; def note_addrof_ovl_candidate_disabled_by_enable_if_attr : Note< "candidate function made ineligible by enable_if">; def note_ovl_candidate_deduced_mismatch : Note< @@ -3829,6 +3836,16 @@ "call to " "%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from" " %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">; +def note_ovl_candidate_constraints_not_satisfied : Note< + "candidate %select{function|function|constructor|" + "function|function|constructor|" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "constructor (the implicit move constructor)|" + "function (the implicit copy assignment operator)|" + "function (the implicit move assignment operator)|" + "inherited constructor|" + "inherited constructor}0 not viable: constraints not satisfied">; def note_implicit_member_target_infer_collision : Note< "implicit %select{" "default constructor|" Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -1329,6 +1329,11 @@ /// type specified. UnionParsedType TrailingReturnType; + /// \brief The constraint-expression specified by the trailing + /// requires-clause, or null if no such clause was specified. + Expr *TrailingRequiresClause; + + /// \brief Reset the parameter list to having zero parameters. /// /// This is used in various places for error recovery. @@ -1447,6 +1452,15 @@ /// \brief Get the trailing-return-type for this function declarator. ParsedType getTrailingReturnType() const { return TrailingReturnType; } + + /// \brief Determine whether this function declarator had a + /// trailing requires-clause. + bool hasTrailingRequiresClause() const { return TrailingRequiresClause; } + + /// \brief Get the trailing requires-clause for this function declarator. + Expr *getTrailingRequiresClause() const { + return TrailingRequiresClause; + } }; struct BlockPointerTypeInfo : TypeInfoCommon { @@ -1591,7 +1605,8 @@ SourceLocation LocalRangeEnd, Declarator &TheDeclarator, TypeResult TrailingReturnType = - TypeResult()); + TypeResult(), + Expr *TrailingRequiresClause = nullptr); /// \brief Return a DeclaratorChunk for a block. static DeclaratorChunk getBlockPointer(unsigned TypeQuals, @@ -2347,6 +2362,16 @@ return false; } + /// \brief Determine whether a trailing requires clause was written (at any + /// level) within this declarator. + bool hasTrailingRequiresClause() const { + for (const auto &Chunk : type_objects()) + if (Chunk.Kind == DeclaratorChunk::Function && + Chunk.Fun.hasTrailingRequiresClause()) + return true; + return false; + } + /// takeAttributes - Takes attributes from the given parsed-attributes /// set and add them to this declarator. /// Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -613,6 +613,10 @@ /// This inherited constructor is not viable because it would slice the /// argument. ovl_fail_inhctor_slice, + + /// This candidate was not viable because its associated constraints were + /// not satisfied. + ovl_fail_constraints_not_satisfied }; /// A list of implicit conversion sequences for the arguments of an Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2857,10 +2857,9 @@ bool *pHadMultipleCandidates = nullptr); FunctionDecl * - resolveAddressOfOnlyViableOverloadCandidate(Expr *E, - DeclAccessPair &FoundResult); + resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &FoundResult); - bool resolveAndFixAddressOfOnlyViableOverloadCandidate( + bool resolveAndFixAddressOfSingleOverloadCandidate( ExprResult &SrcExpr, bool DoFunctionPointerConversion = false); FunctionDecl * @@ -5595,16 +5594,10 @@ bool IsMoreConstrained(NamedDecl *D1, Expr *AC1, NamedDecl *D2, Expr *AC2); /// \brief Check whether the given constraint expression is satisfied given - /// template arguments. Returns false and updates IsSatisfied with the + /// template arguments. 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. /// - /// \param SubstitutedExpr if not null, will be used to return the substituted - /// constraint expression (to be used for diagnostics, for example). - /// - /// \param SubstDiag if the constraint was not satisfied because of a - /// substitution failure, this will contain the emitted diagnostics, if any. - /// /// \returns true if an error occurred, false otherwise. bool CheckConstraintSatisfaction(TemplateDecl *Template, const Expr *ConstraintExpr, @@ -5624,6 +5617,15 @@ SourceLocation TemplateNameLoc, 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); + /// \brief Check that the associated constraints of a template declaration /// match the associated constraints of an older declaration of which it is a /// redeclaration Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -1217,6 +1217,11 @@ } } + if (const Expr *RequiresClause = D->getTrailingRequiresClause()) { + OS << " requires "; + dumpStmt(RequiresClause); + } + if (D->doesThisDeclarationHaveABody()) dumpStmt(D->getBody()); } Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -2134,7 +2134,9 @@ FromConstructor->isExplicit(), D->isInlineSpecified(), D->isImplicit(), - D->isConstexpr()); + D->isConstexpr(), + InheritedConstructor(), + D->getTrailingRequiresClause()); if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { SmallVector CtorInitializers; for (CXXCtorInitializer *I : FromConstructor->inits()) { @@ -2157,7 +2159,8 @@ InnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - D->isImplicit()); + D->isImplicit(), + D->getTrailingRequiresClause()); } else if (CXXConversionDecl *FromConversion = dyn_cast(D)) { ToFunction = CXXConversionDecl::Create(Importer.getToContext(), @@ -2167,7 +2170,8 @@ D->isInlineSpecified(), FromConversion->isExplicit(), D->isConstexpr(), - Importer.Import(D->getLocEnd())); + Importer.Import(D->getLocEnd()), + D->getTrailingRequiresClause()); } else if (CXXMethodDecl *Method = dyn_cast(D)) { ToFunction = CXXMethodDecl::Create(Importer.getToContext(), cast(DC), @@ -2176,14 +2180,16 @@ Method->getStorageClass(), Method->isInlineSpecified(), D->isConstexpr(), - Importer.Import(D->getLocEnd())); + Importer.Import(D->getLocEnd()), + D->getTrailingRequiresClause()); } else { ToFunction = FunctionDecl::Create(Importer.getToContext(), DC, InnerLocStart, NameInfo, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), D->hasWrittenPrototype(), - D->isConstexpr()); + D->isConstexpr(), + D->getTrailingRequiresClause()); } // Import the qualifier, if any. Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -4211,10 +4211,12 @@ StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, - bool isConstexprSpecified) { + bool isConstexprSpecified, + Expr *TrailingRequiresClause) { FunctionDecl *New = new (C, DC) FunctionDecl(Function, C, DC, StartLoc, NameInfo, T, TInfo, - SC, isInlineSpecified, isConstexprSpecified); + SC, isInlineSpecified, isConstexprSpecified, + TrailingRequiresClause); New->HasWrittenPrototype = hasWrittenPrototype; return New; } @@ -4222,7 +4224,7 @@ FunctionDecl *FunctionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) FunctionDecl(Function, C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - SC_None, false, false); + SC_None, false, false, nullptr); } BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -1698,16 +1698,18 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInline, - bool isConstexpr, SourceLocation EndLocation) { + bool isConstexpr, SourceLocation EndLocation, + Expr *TrailingRequiresClause) { return new (C, RD) CXXMethodDecl(CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, isInline, isConstexpr, - EndLocation); + EndLocation, TrailingRequiresClause); } CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXMethodDecl(CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - SC_None, false, false, SourceLocation()); + SC_None, false, false, SourceLocation(), + nullptr); } CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, @@ -2048,7 +2050,7 @@ unsigned Extra = additionalSizeToAlloc(Inherited); auto *Result = new (C, ID, Extra) CXXConstructorDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - false, false, false, false, InheritedConstructor()); + false, false, false, false, InheritedConstructor(), nullptr); Result->IsInheritingConstructor = Inherited; return Result; } @@ -2060,7 +2062,8 @@ QualType T, TypeSourceInfo *TInfo, bool isExplicit, bool isInline, bool isImplicitlyDeclared, bool isConstexpr, - InheritedConstructor Inherited) { + InheritedConstructor Inherited, + Expr *TrailingRequiresClause) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName && "Name must refer to a constructor"); @@ -2068,7 +2071,7 @@ additionalSizeToAlloc(Inherited ? 1 : 0); return new (C, RD, Extra) CXXConstructorDecl( C, RD, StartLoc, NameInfo, T, TInfo, isExplicit, isInline, - isImplicitlyDeclared, isConstexpr, Inherited); + isImplicitlyDeclared, isConstexpr, Inherited, TrailingRequiresClause); } CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const { @@ -2189,7 +2192,7 @@ CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, false, false); + QualType(), nullptr, false, false, nullptr); } CXXDestructorDecl * @@ -2197,12 +2200,14 @@ SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared) { + bool isInline, bool isImplicitlyDeclared, + Expr *TrailingRequiresClause) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXDestructorName && "Name must refer to a destructor"); return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, - isInline, isImplicitlyDeclared); + isInline, isImplicitlyDeclared, + TrailingRequiresClause); } void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) { @@ -2222,7 +2227,7 @@ return new (C, ID) CXXConversionDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, false, false, false, - SourceLocation()); + SourceLocation(), nullptr); } CXXConversionDecl * @@ -2231,13 +2236,14 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isExplicit, - bool isConstexpr, SourceLocation EndLocation) { + bool isConstexpr, SourceLocation EndLocation, + Expr *TrailingRequiresClause) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConversionFunctionName && "Name must refer to a conversion function"); return new (C, RD) CXXConversionDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline, isExplicit, isConstexpr, - EndLocation); + EndLocation, TrailingRequiresClause); } bool CXXConversionDecl::isLambdaToBlockPointerConversion() const { Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -694,6 +694,11 @@ Proto.clear(); } Out << Proto; + + if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) { + Out << " requires "; + TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation); + } } else { Ty.print(Out, Policy, Proto); } Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -146,16 +146,31 @@ } // namespace clang +// Create a constraint expression as the conjunction (the "and") of two other +// constraint expressions. +static Expr *CreateConstraintConjunction(ASTContext &C, Expr *A, Expr *B) { + if (!A) { + return B; + } + if (B) { + return new (C) BinaryOperator(A, B, BO_LAnd, C.BoolTy, VK_RValue, + OK_Ordinary, /*opLoc=*/SourceLocation(), + FPOptions()); + } + return A; +} + static ConstrainedTemplateDeclInfo * -collectAssociatedConstraints(ASTContext& C, TemplateParameterList *Params) { +collectAssociatedConstraints(ASTContext &C, TemplateParameterList *Params, + Expr *TrailingRequiresClause = nullptr) { // TODO: Instead of calling getRequiresClause - write and call a // TemplateParameterList member function calculateAssociatedConstraints, which // will also fetch constraint-expressions from constrained-parameters. - Expr *AssociatedConstraints = Params->getRequiresClause(); - // TODO: Collect function requires clause, if any. - if (AssociatedConstraints) { + Expr *TotalAC = CreateConstraintConjunction(C, Params->getRequiresClause(), + TrailingRequiresClause); + if (TotalAC) { ConstrainedTemplateDeclInfo *CTDI = new (C) ConstrainedTemplateDeclInfo; - CTDI->setAssociatedConstraints(AssociatedConstraints); + CTDI->setAssociatedConstraints(TotalAC); CTDI->setTemplateParameters(Params); return CTDI; } @@ -170,12 +185,14 @@ llvm::PointerIntPair< llvm::PointerUnion, - 1, bool>& TemplateParamsMember) { + 1, bool>& TemplateParamsMember, + Expr *TrailingRequiresClause = nullptr) { if (!TemplateParamsMember.getInt()) { TemplateParamsMember.setInt(true); ConstrainedTemplateDeclInfo *CTDI = collectAssociatedConstraints(C, TemplateParamsMember.getPointer() - .get()); + .get(), + TrailingRequiresClause); if (CTDI) { TemplateParamsMember.setPointer(CTDI); return CTDI->getAssociatedConstraints(); @@ -199,8 +216,11 @@ Expr *TemplateDecl::getAssociatedConstraints() { - return getOrCollectAssociatedConstraints(getASTContext(), - cast(getCanonicalDecl())->TemplateParams); + FunctionDecl *Func = dyn_cast_or_null(TemplatedDecl); + return getOrCollectAssociatedConstraints( + getASTContext(), + cast(getCanonicalDecl())->TemplateParams, + Func ? Func->getTrailingRequiresClause() : nullptr); } void TemplateDecl::anchor() {} Index: lib/AST/ODRHash.cpp =================================================================== --- lib/AST/ODRHash.cpp +++ lib/AST/ODRHash.cpp @@ -320,6 +320,7 @@ } AddQualType(D->getReturnType()); + AddStmt(D->getTrailingRequiresClause()); Inherited::VisitFunctionDecl(D); } Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -5925,7 +5925,8 @@ /// /// For C++, after the parameter-list, it also parses the cv-qualifier-seq[opt], /// (C++11) ref-qualifier[opt], exception-specification[opt], -/// (C++11) attribute-specifier-seq[opt], and (C++11) trailing-return-type[opt]. +/// (C++11) attribute-specifier-seq[opt], (C++11) trailing-return-type[opt] and +/// (C++2a) the trailing requires-clause. /// /// [C++11] exception-specification: /// dynamic-exception-specification @@ -5963,6 +5964,7 @@ CachedTokens *ExceptionSpecTokens = nullptr; ParsedAttributesWithRange FnAttrs(AttrFactory); TypeResult TrailingReturnType; + ExprResult TrailingRequiresClause; /* LocalEndLoc is the end location for the local FunctionTypeLoc. EndLoc is the end location for the function declarator. @@ -6084,7 +6086,7 @@ // Parse trailing-return-type[opt]. LocalEndLoc = EndLoc; - if (getLangOpts().CPlusPlus11 && Tok.is(tok::arrow)) { + auto ParseTrailingReturn = [&] { Diag(Tok, diag::warn_cxx98_compat_trailing_return_type); if (D.getDeclSpec().getTypeSpecType() == TST_auto) StartLoc = D.getDeclSpec().getTypeSpecTypeLoc(); @@ -6092,6 +6094,73 @@ SourceRange Range; TrailingReturnType = ParseTrailingReturnType(Range); EndLoc = Range.getEnd(); + }; + if (getLangOpts().CPlusPlus11 && Tok.is(tok::arrow)) { + ParseTrailingReturn(); + } + // Parse trailing requires-clause[opt]. + if (getLangOpts().ConceptsTS && Tok.is(tok::kw_requires)) { + LocalEndLoc = Tok.getLocation(); + ConsumeToken(); + + TentativeParsingAction TPA(*this); + Diags.setSuppressAllDiagnostics(true); + TrailingRequiresClause = ParseConstraintExpression(); + Diags.setSuppressAllDiagnostics(false); + + if (TrailingRequiresClause.isUsable() + && !TrailingRequiresClause.isInvalid()) { + TPA.Commit(); + EndLoc = TrailingRequiresClause.get()->getLocEnd(); + + // Did the user swap the trailing return type and requires clause? + if (getLangOpts().CPlusPlus11 && Tok.is(tok::arrow)) { + Diag(Tok, diag::err_requires_clause_must_come_after_trailing_return); + // Parse it anyway + ParseTrailingReturn(); + } + if (!D.isFunctionDeclaratorAFunctionDeclaration()) { + Diag(LocalEndLoc, + diag::err_requires_clause_on_declarator_not_declaring_a_function); + } + + } else { + // Did the user swap the trailing return type and requires clause? + SourceLocation FailureLocation = Tok.getLocation(); + TPA.Revert(); + TentativeParsingAction SwapTPA(*this); + bool Found = false; + while (true) { + if (Tok.is(tok::arrow)) { + SourceLocation ArrowLoc = Tok.getLocation(); + TentativeParsingAction TPA(*this); + Diags.setSuppressAllDiagnostics(true); + ParseTrailingReturn(); + Diags.setSuppressAllDiagnostics(false); + + if (TrailingReturnType.isUsable() + && !TrailingReturnType.isInvalid()) { + SwapTPA.Commit(); + TPA.Commit(); + Diag(ArrowLoc, + diag::err_requires_clause_must_come_after_trailing_return); + EndLoc = Tok.getLocation(); + Found = true; + break; + } + TPA.Revert(); + } + if (Tok.getLocation() == FailureLocation) + break; + ConsumeAnyToken(); + } + if (!Found) { + SwapTPA.Revert(); + // User did not swap a trailing return and a trailing requires clause. + // Re-parse the thing and display the original error message. + ParseConstraintExpression(); + } + } } } else if (standardAttributesAllowed()) { MaybeParseCXX11Attributes(FnAttrs); @@ -6134,7 +6203,9 @@ ExceptionSpecTokens, DeclsInPrototype, StartLoc, LocalEndLoc, D, - TrailingReturnType), + TrailingReturnType, + TrailingRequiresClause.isUsable() ? + TrailingRequiresClause.get() : nullptr), FnAttrs, EndLoc); } Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -949,6 +949,14 @@ // direct-declarator '[' constant-expression[opt] ']' // direct-abstract-declarator[opt] '[' constant-expression[opt] ']' TPR = TryParseBracketDeclarator(); + } else if (Tok.is(tok::kw_requires)) { + // declarator requires-clause + // A requires clause indicates a function declaration. + if (ParenCount) { + SkipUntil(tok::l_paren); + } else { + TPR = TPResult::True; + } } else { break; } @@ -1852,7 +1860,6 @@ /// 'throw' '(' type-id-list[opt] ')' /// Parser::TPResult Parser::TryParseFunctionDeclarator() { - // The '(' is already parsed. TPResult TPR = TryParseParameterDeclarationClause(); Index: lib/Sema/DeclSpec.cpp =================================================================== --- lib/Sema/DeclSpec.cpp +++ lib/Sema/DeclSpec.cpp @@ -178,7 +178,8 @@ SourceLocation LocalRangeBegin, SourceLocation LocalRangeEnd, Declarator &TheDeclarator, - TypeResult TrailingReturnType) { + TypeResult TrailingReturnType, + Expr *TrailingRequiresClause) { assert(!(TypeQuals & DeclSpec::TQ_atomic) && "function cannot have _Atomic qualifier"); @@ -212,6 +213,7 @@ I.Fun.HasTrailingReturnType = TrailingReturnType.isUsable() || TrailingReturnType.isInvalid(); I.Fun.TrailingReturnType = TrailingReturnType.get(); + I.Fun.TrailingRequiresClause = TrailingRequiresClause; assert(I.Fun.TypeQuals == TypeQuals && "bitfield overflow"); assert(I.Fun.ExceptionSpecType == ESpecType && "bitfield overflow"); Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -1907,7 +1907,7 @@ // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization // preserves Result. Result = E; - if (!Self.resolveAndFixAddressOfOnlyViableOverloadCandidate( + if (!Self.resolveAndFixAddressOfSingleOverloadCandidate( Result, /*DoFunctionPointerConversion=*/true)) return false; return Result.isUsable(); Index: lib/Sema/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -72,19 +72,15 @@ << /*declaration*/0; } -template +template static bool -calculateConstraintSatisfaction(Sema& S, TemplateDeclT *Template, - ArrayRef TemplateArgs, - SourceLocation TemplateNameLoc, - MultiLevelTemplateArgumentList &MLTAL, - const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction) { +calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction, + AtomicEvaluator &&Evaluator) { if (auto *BO = dyn_cast(ConstraintExpr)) { if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateNameLoc, MLTAL, BO->getLHS(), - Satisfaction)) + if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction, + Evaluator)) return true; bool IsLHSSatisfied = Satisfaction.IsSatisfied; @@ -94,9 +90,9 @@ // needed for RHS, no need to generate it. return false; - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateNameLoc, MLTAL, BO->getRHS(), - Satisfaction)) + if (calculateConstraintSatisfaction( + S, BO->getRHS(), Satisfaction, + std::forward(Evaluator))) return true; if (BO->getOpcode() == BO_LAnd) @@ -107,69 +103,93 @@ return false; } } else if (auto *PO = dyn_cast(ConstraintExpr)) - return calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateNameLoc, MLTAL, - PO->getSubExpr(), Satisfaction); + return calculateConstraintSatisfaction( + S, PO->getSubExpr(), Satisfaction, + std::forward(Evaluator)); - EnterExpressionEvaluationContext ConstantEvaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated); - - // Atomic constraint - substitute arguments and check satisfaction. - ExprResult SubstitutedExpression; - { - TemplateDeductionInfo Info(TemplateNameLoc); - Sema::InstantiatingTemplate Inst(S, TemplateNameLoc, Template, TemplateArgs, - Info); - // We do not want error diagnostics escaping here. - Sema::SFINAETrap Trap(S); - SubstitutedExpression = S.SubstExpr(const_cast(ConstraintExpr), - MLTAL); - if (!SubstitutedExpression.isUsable() || - 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()) { - PartialDiagnosticAt SubstDiag{ SourceLocation(), - PartialDiagnostic::NullDiagnostic() }; - Info.takeSFINAEDiagnostic(SubstDiag); - SmallString<128> DiagString; - DiagString = ": "; - SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); - Satisfaction.Details.emplace_back( - ConstraintExpr, - new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstDiag.first, - std::string(DiagString.begin(), DiagString.end())}); - } - Satisfaction.IsSatisfied = false; - return false; - } - } + // An atomic constraint expression + ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); - if (!S.CheckConstraintExpression(SubstitutedExpression.get())) + if (SubstitutedAtomicExpr.isInvalid()) return true; - assert(!SubstitutedExpression.get()->isInstantiationDependent() && + if (!SubstitutedAtomicExpr.isUsable()) + // The evaluator decided satisfaction without yielding an expression. + return false; + + assert(!SubstitutedAtomicExpr.get()->isInstantiationDependent() && "Instantiation dependent constraint expressions should not get here!"); - if (!SubstitutedExpression.get()->EvaluateAsBooleanCondition(Satisfaction.IsSatisfied, - S.Context)) { - // C++2a [temp.constr.atomic]p1 - // ...E shall be a constant expression of type bool. - S.Diag(SubstitutedExpression.get()->getLocStart(), + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + if (!SubstitutedAtomicExpr.get()->EvaluateAsBooleanCondition( + Satisfaction.IsSatisfied, S.Context)) { + // C++2a [temp.constr.atomic]p1 + // ...E shall be a constant expression of type bool. + S.Diag(SubstitutedAtomicExpr.get()->getLocStart(), diag::err_non_constant_constraint_expression) - << SubstitutedExpression.get(); + << SubstitutedAtomicExpr.get(); return true; } if (!Satisfaction.IsSatisfied) Satisfaction.Details.emplace_back(ConstraintExpr, - SubstitutedExpression.get()); + 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, TemplateNameLoc, Template, + TemplateArgs, Info); + // We do not want error diagnostics escaping here. + Sema::SFINAETrap Trap(S); + SubstitutedExpression = + S.SubstExpr(const_cast(AtomicExpr), MLTAL); + if (!SubstitutedExpression.isUsable() || + 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()) { + 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, const Expr *ConstraintExpr, @@ -228,6 +248,15 @@ 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, SourceLocation TemplateNameLoc) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -7845,6 +7845,10 @@ bool isExplicit = D.getDeclSpec().isExplicitSpecified(); bool isConstexpr = D.getDeclSpec().isConstexprSpecified(); + Expr *TrailingRequiresClause = D.isFunctionDeclarator() + && D.hasTrailingRequiresClause() ? + D.getFunctionTypeInfo().getTrailingRequiresClause() + : nullptr; // Check that the return type is not an abstract class type. // For record types, this is done by the AbstractClassUsageDiagnoser once @@ -7865,7 +7869,8 @@ D.getLocStart(), NameInfo, R, TInfo, isExplicit, isInline, /*isImplicitlyDeclared=*/false, - isConstexpr); + isConstexpr, InheritedConstructor(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. @@ -7876,7 +7881,8 @@ SemaRef.Context, Record, D.getLocStart(), NameInfo, R, TInfo, isInline, - /*isImplicitlyDeclared=*/false); + /*isImplicitlyDeclared=*/false, + TrailingRequiresClause); // If the class is complete, then we now create the implicit exception // specification. If the class is incomplete or dependent, we can't do @@ -7900,7 +7906,8 @@ D.getLocStart(), D.getIdentifierLoc(), Name, R, TInfo, SC, isInline, - /*hasPrototype=*/true, isConstexpr); + /*hasPrototype=*/true, isConstexpr, + TrailingRequiresClause); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -7915,7 +7922,8 @@ return CXXConversionDecl::Create(SemaRef.Context, cast(DC), D.getLocStart(), NameInfo, R, TInfo, isInline, isExplicit, - isConstexpr, SourceLocation()); + isConstexpr, SourceLocation(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) { SemaRef.CheckDeductionGuideDeclarator(D, R, SC); @@ -7941,7 +7949,8 @@ cast(DC), D.getLocStart(), NameInfo, R, TInfo, SC, isInline, - isConstexpr, SourceLocation()); + isConstexpr, SourceLocation(), + TrailingRequiresClause); IsVirtualOkay = !Ret->isStatic(); return Ret; } else { @@ -7956,7 +7965,8 @@ return FunctionDecl::Create(SemaRef.Context, DC, D.getLocStart(), NameInfo, R, TInfo, SC, isInline, - true/*HasPrototype*/, isConstexpr); + true/*HasPrototype*/, isConstexpr, + TrailingRequiresClause); } } @@ -8364,6 +8374,11 @@ Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_virtual_member_function_template) << FixItHint::CreateRemoval(D.getDeclSpec().getVirtualSpecLoc()); + } else if (D.hasTrailingRequiresClause()) { + // C++2a [class.virtual]p6 + // A virtual method shall not have a requires-clause. + Diag(NewFD->getTrailingRequiresClause()->getLocStart(), + diag::err_constrained_virtual_method); } else { // Okay: Add virtual to the method. NewFD->setVirtualAsWritten(true); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -6709,7 +6709,8 @@ FD->getType(), FD->getTypeSourceInfo(), SC_None, false/*isInlineSpecified*/, FD->hasPrototype(), - false/*isConstexprSpecified*/); + false/*isConstexprSpecified*/, + FD->getTrailingRequiresClause()); NewD = NewFD; if (FD->getQualifier()) Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -10552,7 +10552,8 @@ Context, Derived, UsingLoc, NameInfo, TInfo->getType(), TInfo, BaseCtor->isExplicit(), /*Inline=*/true, /*ImplicitlyDeclared=*/true, Constexpr, - InheritedConstructor(Shadow, BaseCtor)); + InheritedConstructor(Shadow, BaseCtor), + BaseCtor->getTrailingRequiresClause()); if (Shadow->isInvalidDecl()) DerivedCtor->setInvalidDecl(); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -2713,6 +2713,24 @@ return true; } + if (FunctionDecl *FD = dyn_cast(D)) { + if (Expr *RC = FD->getTrailingRequiresClause()) { + ConstraintSatisfaction Satisfaction; + bool Failed = S.CheckConstraintSatisfaction(RC, Satisfaction); + if (Failed) + // A diagnostic will have already been generated (non-constant + // constraint expression, for example) + return true; + if (!Satisfaction.IsSatisfied) { + S.Diag(Loc, + diag::err_reference_to_function_with_unsatisfied_constraints) + << D; + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + return true; + } + } + } + return false; } @@ -15965,7 +15983,7 @@ // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization // leaves Result unchanged on failure. Result = E; - if (resolveAndFixAddressOfOnlyViableOverloadCandidate(Result)) + if (resolveAndFixAddressOfSingleOverloadCandidate(Result)) return Result; // If that failed, try to recover with a call. Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -1205,7 +1205,20 @@ return NewTarget != OldTarget; } - // TODO: Concepts: Check function trailing requires clauses here. + Expr *NewRC = New->getTrailingRequiresClause(), + *OldRC = Old->getTrailingRequiresClause(); + if ((NewRC != nullptr) != (OldRC != nullptr)) + // RC are most certainly different - these are overloads. + return true; + + if (NewRC) { + llvm::FoldingSetNodeID NewID, OldID; + NewRC->Profile(NewID, Context, /*Canonical=*/true); + OldRC->Profile(OldID, Context, /*Canonical=*/true); + if (NewID != OldID) + // RCs are not equivalent - these are overloads. + return true; + } // The signatures match; this is not an overload. return false; @@ -6088,6 +6101,16 @@ Candidate.FailureKind = ovl_fail_ext_disabled; return; } + + Expr *RequiresClause = Function->getTrailingRequiresClause(); + if (LangOpts.ConceptsTS && RequiresClause) { + ConstraintSatisfaction Satisfaction; + if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + !Satisfaction.IsSatisfied) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_constraints_not_satisfied; + } + } } ObjCMethodDecl * @@ -6593,6 +6616,17 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + Expr *RequiresClause = Method->getTrailingRequiresClause(); + if (LangOpts.ConceptsTS && RequiresClause) { + ConstraintSatisfaction Satisfaction; + if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + !Satisfaction.IsSatisfied) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_constraints_not_satisfied; + return; + } + } } /// \brief Add a C++ member function template as a candidate to the candidate @@ -9086,6 +9120,20 @@ return Cmp == Comparison::Better; } + if (Cand1.Function && Cand2.Function) { + Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); + Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); + if (RC1 && RC2) { + bool MoreConstrained1 = S.IsMoreConstrained(Cand1.Function, RC1, + Cand2.Function, RC2); + bool MoreConstrained2 = S.IsMoreConstrained(Cand2.Function, RC2, + Cand1.Function, RC1); + if (MoreConstrained1 != MoreConstrained2) + return MoreConstrained1; + } else if (RC1 || RC2) + return RC1 != nullptr; + } + if (S.getLangOpts().CUDA && Cand1.Function && Cand2.Function) { FunctionDecl *Caller = dyn_cast(S.CurContext); return S.IdentifyCUDAPreference(Caller, Cand1.Function) > @@ -9364,6 +9412,24 @@ return false; } + if (const Expr *RC = FD->getTrailingRequiresClause()) { + ConstraintSatisfaction Satisfaction; + if (S.CheckConstraintSatisfaction(RC, Satisfaction)) + return false; + if (!Satisfaction.IsSatisfied) { + if (Complain) { + if (InOverloadResolution) + S.Diag(FD->getLocStart(), + diag::note_ovl_candidate_unsatisfied_constraints); + else + S.Diag(Loc, diag::err_addrof_function_constraints_not_satisfied) + << FD; + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + } + return false; + } + } + auto I = llvm::find_if(FD->parameters(), [](const ParmVarDecl *P) { return P->hasAttr(); }); @@ -10220,6 +10286,20 @@ assert(!Available); break; } + + case ovl_fail_constraints_not_satisfied: { + std::string Description; + OverloadCandidateKind FnKind = + ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Description); + S.Diag(Fn->getLocation(), + diag::note_ovl_candidate_constraints_not_satisfied) + << (unsigned) FnKind; + ConstraintSatisfaction Satisfaction; + if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(), + Satisfaction)) + break; + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + } } } @@ -11208,13 +11288,14 @@ /// resolve that function to a single function that can have its address taken. /// This will modify `Pair` iff it returns non-null. /// -/// This routine can only realistically succeed if all but one candidates in the -/// overload set for SrcExpr cannot have their addresses taken. +/// This routine can only succeed if from all of the candidates in the overload +/// set for SrcExpr that can have their addresses taken, there is one candidate +/// that is more constrained than the rest. FunctionDecl * -Sema::resolveAddressOfOnlyViableOverloadCandidate(Expr *E, - DeclAccessPair &Pair) { +Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { OverloadExpr::FindResult R = OverloadExpr::find(E); OverloadExpr *Ovl = R.Expression; + bool IsResultAmbiguous = false; FunctionDecl *Result = nullptr; DeclAccessPair DAP; // Don't use the AddressOfResolver because we're specifically looking for @@ -11228,32 +11309,50 @@ if (!checkAddressOfFunctionIsAvailable(FD)) continue; - // We have more than one result; quit. - if (Result) - return nullptr; + // We have more than one result - see if it is more constrained than the + // previous one. + if (Result) { + bool ResultMoreConstrained = IsMoreConstrained(Result, + Result->getTrailingRequiresClause(), + FD, FD->getTrailingRequiresClause()); + bool FDMoreConstrained = IsMoreConstrained(FD, + FD->getTrailingRequiresClause(), + Result, + Result->getTrailingRequiresClause()); + if (ResultMoreConstrained == FDMoreConstrained) { + IsResultAmbiguous = true; + continue; + } else if (ResultMoreConstrained) + continue; + // FD is more constrained replace Result with it. + } + IsResultAmbiguous = false; DAP = I.getPair(); Result = FD; } + if (IsResultAmbiguous) + return nullptr; + if (Result) Pair = DAP; return Result; } /// \brief Given an overloaded function, tries to turn it into a non-overloaded -/// function reference using resolveAddressOfOnlyViableOverloadCandidate. This -/// will perform access checks, diagnose the use of the resultant decl, and, if +/// function reference using resolveAddressOfSingleOverloadCandidate. This will +/// perform access checks, diagnose the use of the resultant decl, and, if /// requested, potentially perform a function-to-pointer decay. /// -/// Returns false if resolveAddressOfOnlyViableOverloadCandidate fails. +/// Returns false if resolveAddressOfSingleOverloadCandidate fails. /// Otherwise, returns true. This may emit diagnostics and return true. -bool Sema::resolveAndFixAddressOfOnlyViableOverloadCandidate( +bool Sema::resolveAndFixAddressOfSingleOverloadCandidate( ExprResult &SrcExpr, bool DoFunctionPointerConverion) { Expr *E = SrcExpr.get(); assert(E->getType() == Context.OverloadTy && "SrcExpr must be an overload"); DeclAccessPair DAP; - FunctionDecl *Found = resolveAddressOfOnlyViableOverloadCandidate(E, DAP); + FunctionDecl *Found = resolveAddressOfSingleOverloadCandidate(E, DAP); if (!Found) return false; Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -3353,7 +3353,7 @@ DeclAccessPair DAP; if (FunctionDecl *Viable = - S.resolveAddressOfOnlyViableOverloadCandidate(Arg, DAP)) + S.resolveAddressOfSingleOverloadCandidate(Arg, DAP)) return GetTypeOfFunction(S, R, Viable); return QualType(); Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1634,6 +1634,17 @@ return nullptr; } + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + if (TrailingRequiresClause) { + ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, + TemplateArgs); + if (!SubstRC.isUsable() || SubstRC.isInvalid()) + return nullptr; + TrailingRequiresClause = SubstRC.get(); + if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) + return nullptr; + } + // If we're instantiating a local function declaration, put the result // in the enclosing namespace; otherwise we need to find the instantiated // context. @@ -1692,6 +1703,9 @@ Params[P]->setOwningFunction(Function); Function->setParams(Params); + if (TrailingRequiresClause) + Function->setTrailingRequiresClause(TrailingRequiresClause); + if (TemplateParams) { // Our resulting instantiation is actually a function template, since we // are substituting only the outer template parameters. For example, given @@ -1944,6 +1958,17 @@ return nullptr; } + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + if (TrailingRequiresClause) { + ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, + TemplateArgs); + if (!SubstRC.isUsable() || SubstRC.isInvalid()) + return nullptr; + TrailingRequiresClause = SubstRC.get(); + if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) + return nullptr; + } + DeclContext *DC = Owner; if (isFriend) { if (QualifierLoc) { @@ -1973,13 +1998,15 @@ StartLoc, NameInfo, T, TInfo, Constructor->isExplicit(), Constructor->isInlineSpecified(), - false, Constructor->isConstexpr()); + false, Constructor->isConstexpr(), + InheritedConstructor(), + TrailingRequiresClause); Method->setRangeEnd(Constructor->getLocEnd()); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, Destructor->isInlineSpecified(), - false); + false, TrailingRequiresClause); Method->setRangeEnd(Destructor->getLocEnd()); } else if (CXXConversionDecl *Conversion = dyn_cast(D)) { Method = CXXConversionDecl::Create(SemaRef.Context, Record, @@ -1987,13 +2014,15 @@ Conversion->isInlineSpecified(), Conversion->isExplicit(), Conversion->isConstexpr(), - Conversion->getLocEnd()); + Conversion->getLocEnd(), + TrailingRequiresClause); } else { StorageClass SC = D->isStatic() ? SC_Static : SC_None; Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, SC, D->isInlineSpecified(), - D->isConstexpr(), D->getLocEnd()); + D->isConstexpr(), D->getLocEnd(), + TrailingRequiresClause); } if (D->isInlined()) Index: lib/Sema/SemaTemplateVariadic.cpp =================================================================== --- lib/Sema/SemaTemplateVariadic.cpp +++ lib/Sema/SemaTemplateVariadic.cpp @@ -877,9 +877,13 @@ if (Chunk.Fun.hasTrailingReturnType()) { QualType T = Chunk.Fun.getTrailingReturnType().get(); - if (!T.isNull() && T->containsUnexpandedParameterPack()) - return true; + if (!T.isNull() && T->containsUnexpandedParameterPack()) + return true; } + + if (Chunk.Fun.hasTrailingRequiresClause() + &&Chunk.Fun.TrailingRequiresClause->containsUnexpandedParameterPack()) + return true; break; case DeclaratorChunk::MemberPointer: Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -796,6 +796,9 @@ FD->IsLateTemplateParsed = Record.readInt(); FD->setCachedLinkage(Linkage(Record.readInt())); FD->EndRangeLoc = ReadSourceLocation(); + bool HasTrailingRequiresClause = Record.readInt(); + if (HasTrailingRequiresClause) + FD->TrailingRequiresClause = Record.readExpr(); switch ((FunctionDecl::TemplatedKind)Record.readInt()) { case FunctionDecl::TK_NonTemplate: Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -538,6 +538,10 @@ Record.push_back(D->IsLateTemplateParsed); Record.push_back(D->getLinkageInternal()); Record.AddSourceLocation(D->getLocEnd()); + Expr *RC = D->getTrailingRequiresClause(); + Record.push_back(RC != nullptr); + if (RC) + Record.AddStmt(RC); Record.push_back(D->getTemplatedKind()); switch (D->getTemplatedKind()) { Index: test/CXX/concepts-ts/class.derived/class.virtual/p6.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/class.derived/class.virtual/p6.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template +class A { + virtual void f1() requires sizeof(T) == 0; // expected-error{{a virtual function must not have a requires clause}} + virtual void f2() requires sizeof(T) == 1; // expected-error{{a virtual function must not have a requires clause}} +}; + +template +class B : A { + virtual void f1() requires sizeof(T) == 0 override {} // expected-error{{a virtual function must not have a requires clause}} +}; \ No newline at end of file Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp @@ -1,60 +0,0 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -fcxx-exceptions -x c++ -verify %s - -namespace A { - template concept bool C1() { return true; } - - template concept bool C2 = true; -} - -template concept bool C3() { return (throw 0, true); } -static_assert(noexcept(C3()), "function concept should be treated as if noexcept(true) specified"); - -template concept bool D1(); // expected-error {{function concept declaration must be a definition}} - -struct B { - template concept bool D2() { return true; } // expected-error {{concept declarations may only appear in namespace scope}} -}; - -struct C { - template static concept bool D3 = true; // expected-error {{concept declarations may only appear in namespace scope}} -}; - -concept bool D4() { return true; } // expected-error {{'concept' can only appear on the definition of a function template or variable template}} - -concept bool D5 = true; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} - -template -concept bool D6; // expected-error {{variable concept declaration must be initialized}} - -template -concept bool D7() throw(int) { return true; } // expected-error {{function concept cannot have exception specification}} - -// Tag -concept class CC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -concept struct CS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -concept union CU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -concept enum CE1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -template concept class TCC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -template concept struct TCS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -template concept union TCU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -typedef concept int CI; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} -void fpc(concept int i) {} // expected-error {{'concept' can only appear on the definition of a function template or variable template}} - -concept bool; // expected-error {{'concept' can only appear on the definition of a function template or variable template}} - -template concept bool VCEI{ true }; -template concept bool VCEI; // expected-error {{'concept' cannot be applied on an explicit instantiation}} -extern template concept bool VCEI; // expected-error {{'concept' cannot be applied on an explicit instantiation}} - -template concept bool VCPS{ true }; -template concept bool VCPS{ true }; // expected-error {{'concept' cannot be applied on an partial specialization}} - -template concept bool VCES{ true }; -template <> concept bool VCES{ true }; // expected-error {{'concept' cannot be applied on an explicit specialization}} - -template concept bool FCEI() { return true; } -template concept bool FCEI(); // expected-error {{'concept' cannot be applied on an explicit instantiation}} -extern template concept bool FCEI(); // expected-error {{'concept' cannot be applied on an explicit instantiation}} - -template concept bool FCES() { return true; } -template <> concept bool FCES() { return true; } // expected-error {{'concept' cannot be applied on an explicit specialization}} Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp @@ -1,13 +0,0 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s - -template concept thread_local bool VCTL = true; // expected-error {{variable concept cannot be declared 'thread_local'}} - -template concept constexpr bool VCC = true; // expected-error {{variable concept cannot be declared 'constexpr'}} - -template concept inline bool FCI() { return true; } // expected-error {{function concept cannot be declared 'inline'}} - -struct X { - template concept friend bool FCF() { return true; } // expected-error {{function concept cannot be declared 'friend'}} -}; - -template concept constexpr bool FCC() { return true; } // expected-error {{function concept cannot be declared 'constexpr'}} Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p5.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p5.cpp @@ -1,25 +0,0 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s - -template -concept bool fcpv(void) { return true; } - -template -concept bool fcpi(int i = 0) { return true; } // expected-error {{function concept cannot have any parameters}} - -template -concept bool fcpp(Ts... ts) { return true; } // expected-error {{function concept cannot have any parameters}} - -template -concept bool fcpva(...) { return true; } // expected-error {{function concept cannot have any parameters}} - -template -concept const bool fcrtc() { return true; } // expected-error {{declared return type of function concept must be 'bool'}} - -template -concept int fcrti() { return 5; } // expected-error {{declared return type of function concept must be 'bool'}} - -template -concept float fcrtf() { return 5.5; } // expected-error {{declared return type of function concept must be 'bool'}} - -template -concept decltype(auto) fcrtd(void) { return true; } // expected-error {{declared return type of function concept must be 'bool'}} Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p6.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p6.cpp @@ -1,25 +0,0 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s - -template -concept bool vc { true }; - -template -struct B { typedef bool Boolean; }; - -template -B::Boolean concept vctb(!0); - -template -concept const bool vctc { true }; // expected-error {{declared type of variable concept must be 'bool'}} - -template -concept int vcti { 5 }; // expected-error {{declared type of variable concept must be 'bool'}} - -template -concept float vctf { 5.5 }; // expected-error {{declared type of variable concept must be 'bool'}} - -template -concept auto vcta { true }; // expected-error {{declared type of variable concept must be 'bool'}} - -template -concept decltype(auto) vctd { true }; // expected-error {{declared type of variable concept must be 'bool'}} Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p7.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p7.cpp @@ -1,18 +0,0 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s - -template concept bool FCEI() { return true; } // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}} -template bool FCEI(); // expected-error {{function concept cannot be explicitly instantiated}} -extern template bool FCEI(); // expected-error {{function concept cannot be explicitly instantiated}} - -template concept bool FCES() { return true; } // expected-note {{previous declaration is here}} -template <> bool FCES() { return true; } // expected-error {{function concept cannot be explicitly specialized}} - -template concept bool VC { true }; // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}} -template bool VC; // expected-error {{variable concept cannot be explicitly instantiated}} -extern template bool VC; // expected-error {{variable concept cannot be explicitly instantiated}} - -template concept bool VCES { true }; // expected-note {{previous declaration is here}} -template <> bool VCES { true }; // expected-error {{variable concept cannot be explicitly specialized}} - -template concept bool VCPS { true }; // expected-note {{previous declaration is here}} -template bool VCPS { true }; // expected-error {{variable concept cannot be partially specialized}} Index: test/CXX/concepts-ts/dcl.dcl/lit.cfg.py =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl.dcl/lit.cfg.py @@ -1,26 +0,0 @@ -# -*- Python -*- - -import os -import lit.formats - -from lit.llvm import llvm_config - -# Configuration file for the 'lit' test runner. - -# name: The name of this test suite. -config.name = 'Clang-Concepts-TS-Unsupported' - -# testFormat: The test format to use to interpret tests. -# -# For now we require '&&' between commands, until they get globally killed and -# the test runner updated. -config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) - -# suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu', - '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs'] - -config.unsupported = True - -# test_source_root: The root path where tests are located. -config.test_source_root = os.path.dirname(__file__) Index: test/CXX/concepts-ts/dcl/dcl.decl/p3.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl/dcl.decl/p3.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +void f1(int a) requires true; // OK +auto f2(int a) -> bool requires true; // OK +auto f3(int a) requires true -> bool; // expected-error{{trailing return type must come before trailing requires clause}} +int f4(int a) requires; // expected-error{{expected expression}} +int f5(int a) requires {} // expected-error{{expected expression}} +void (*pf)() requires true; // expected-error{{trailing requires clause can only be used when declaring a function}} +void g1(int (*dsdads)() requires false); // expected-error{{trailing requires clause can only be used when declaring a function}} +void g2(int (*(*dsdads)())() requires true); // expected-error{{trailing requires clause can only be used when declaring a function}} +void g3(int (*(*dsdads)(int) requires true)() ); // expected-error{{trailing requires clause can only be used when declaring a function}} + Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/mixed-constraints.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/mixed-constraints.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template requires sizeof(T) >= 4 && sizeof(T) <= 10 // expected-note{{because 'sizeof(char [20]) <= 10' (20 <= 10) evaluated to false}} expected-note{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}} +void foo() requires sizeof(T) <= 8 {} // expected-note{{candidate template ignored: constraints not satisfied [with T = char]}} expected-note{{candidate template ignored: constraints not satisfied [with T = char [9]]}} expected-note{{candidate template ignored: constraints not satisfied [with T = char [20]]}} expected-note{{and 'sizeof(char [20]) <= 8' (20 <= 8) evaluated to false}} expected-note{{because 'sizeof(char [9]) <= 8' (9 <= 8) evaluated to false}} + +void bar() { + foo(); // expected-error{{no matching function for call to 'foo'}} + foo(); + foo(); + foo(); // expected-error{{no matching function for call to 'foo'}} + foo(); // expected-error{{no matching function for call to 'foo'}} +} \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p4.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p4.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +namespace functions +{ + void foo(int) requires false {} // expected-note 3{{because 'false' evaluated to false}} + void bar(int) requires true {} + + void a(int); + void a(double); + + void baz() { + foo(1); // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + bar(1); + void (*p1)(int) = foo; // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + void (*p3)(int) = bar; + decltype(foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + decltype(bar)* a2 = nullptr; + } +} + +namespace methods +{ + template + struct A { + static void foo(int) requires sizeof(T) == 1 {} // expected-note 3{{because 'sizeof(char [2]) == 1' (2 == 1) evaluated to false}} + static void bar(int) requires sizeof(T) == 2 {} // expected-note 3{{because 'sizeof(char) == 2' (1 == 2) evaluated to false}} + }; + + void baz() { + A::foo(1); + A::bar(1); // expected-error{{invalid reference to function 'bar' - constraints not satisfied}} + A::foo(1); // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + A::bar(1); + void (*p1)(int) = A::foo; + void (*p2)(int) = A::bar; // expected-error{{invalid reference to function 'bar' - constraints not satisfied}} + void (*p3)(int) = A::foo; // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + void (*p4)(int) = A::bar; + decltype(A::foo)* a1 = nullptr; + decltype(A::bar)* a2 = nullptr; // expected-error{{invalid reference to function 'bar' - constraints not satisfied}} + decltype(A::foo)* a3 = nullptr; // expected-error{{invalid reference to function 'foo' - constraints not satisfied}} + decltype(A::bar)* a4 = nullptr; + } +} + +namespace operators +{ + template + struct A { + A operator-(A b) requires sizeof(T) == 1 { return b; } // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}} + }; + + void baz() { + auto* x = &A::operator-; // expected-error{{invalid reference to function 'operator-' - constraints not satisfied}} + auto y = &A::operator-; + } +} \ No newline at end of file Index: test/CXX/concepts-ts/over/over.match/over.match.best/p1.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/over/over.match/over.match.best/p1.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + + +template +constexpr static bool is_same_v = false; + +template +constexpr static bool is_same_v = true; + +namespace templates +{ + template + concept AtLeast1 = sizeof(T) >= 1; + + template + int foo(T t) requires sizeof(T) == 4 { // expected-note {{candidate function}} + return 0; + } + + template + char foo(T t) requires AtLeast1 { // expected-note {{candidate function}} + return 'a'; + } + + template + double foo(T t) requires AtLeast1 && sizeof(T) <= 2 { + return 'a'; + } + + void bar() { + static_assert(is_same_v); // expected-error {{call to 'foo' is ambiguous}} + static_assert(is_same_v); + } +} + +namespace non_template +{ + template + concept AtLeast2 = sizeof(T) >= 2; + + template + concept AtMost8 = sizeof(T) <= 8; + + int foo() requires AtLeast2 && AtMost8 { + return 0; + } + + double foo() requires AtLeast2 { + return 0.0; + } + + double baz() requires AtLeast2 && AtMost8 { // expected-note {{candidate function}} + return 0.0; + } + + int baz() requires AtMost8 && AtLeast2 { // expected-note {{candidate function}} + return 0.0; + } + + void bar() { + static_assert(is_same_v); + static_assert(is_same_v); // expected-error {{call to 'baz' is ambiguous}} + } +} + Index: test/CXX/concepts-ts/over/over.over/p4.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/over/over.over/p4.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + + +template +constexpr static bool is_same_v = false; + +template +constexpr static bool is_same_v = true; + +template +concept AtLeast2 = sizeof(T) >= 2; + +template +concept AtMost8 = sizeof(T) <= 8; + +int foo() requires AtLeast2 && AtMost8 { + return 0; +} + +double foo() requires AtLeast2 { + return 0.0; +} + +char bar() requires AtLeast2 { // expected-note {{possible target for call}} + return 1.0; +} + +short bar() requires AtLeast2 && AtMost8 { // expected-note {{possible target for call}} expected-note {{candidate function}} + return 0.0; +} + +int bar() requires AtMost8 && AtLeast2 { // expected-note {{possible target for call}} expected-note {{candidate function}} + return 0.0; +} + +char baz() requires AtLeast2 { + return 1.0; +} + +short baz() requires AtLeast2 && AtMost8 { + return 0.0; +} + +int baz() requires AtMost8 && AtLeast2 { + return 0.0; +} + +long baz() requires AtMost8 && AtLeast2 && AtLeast2 { + return 3.0; +} + +void a() { + static_assert(is_same_v); + static_assert(is_same_v); // expected-error {{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}} expected-error{{call to 'bar' is ambiguous}} + static_assert(is_same_v); +} \ No newline at end of file