Index: include/clang/AST/ASTLambda.h =================================================================== --- include/clang/AST/ASTLambda.h +++ include/clang/AST/ASTLambda.h @@ -64,6 +64,17 @@ dyn_cast(DC)); } +inline bool isGenericLambdaCallOperatorOrStaticInvokerSpecialization( + DeclContext *DC) { + CXXMethodDecl *MD = dyn_cast(DC); + if (!MD) return false; + const CXXRecordDecl *LambdaClass = MD->getParent(); + if (LambdaClass && LambdaClass->isGenericLambda()) + return (isLambdaCallOperator(MD) || MD->isLambdaStaticInvoker()) && + MD->isFunctionTemplateSpecialization(); + return false; +} + // This returns the parent DeclContext ensuring that the correct // parent DeclContext is returned for Lambdas Index: include/clang/AST/ASTNodeTraverser.h =================================================================== --- include/clang/AST/ASTNodeTraverser.h +++ include/clang/AST/ASTNodeTraverser.h @@ -343,6 +343,9 @@ for (const auto *I : C->inits()) Visit(I); + if (const Expr *TRC = D->getTrailingRequiresClause()) + Visit(TRC); + if (D->doesThisDeclarationHaveABody()) Visit(D->getBody()); } Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -686,10 +686,12 @@ /// Represents a ValueDecl that came out of a declarator. /// Contains type source information through TypeSourceInfo. class DeclaratorDecl : public ValueDecl { - // A struct representing both a TInfo and a syntactic qualifier, - // to be used for the (uncommon) case of out-of-line declarations. + // A struct representing a TInfo, a trailing requires-clause and a syntactic + // qualifier, to be used for the (uncommon) case of out-of-line declarations + // and constrained function decls. struct ExtInfo : public QualifierInfo { TypeSourceInfo *TInfo; + Expr *TrailingRequiresClause = nullptr; }; llvm::PointerUnion DeclInfo; @@ -756,6 +758,21 @@ void setQualifierInfo(NestedNameSpecifierLoc QualifierLoc); + /// \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 hasExtInfo() ? getExtInfo()->TrailingRequiresClause + : nullptr; + } + + const Expr *getTrailingRequiresClause() const { + return hasExtInfo() ? getExtInfo()->TrailingRequiresClause + : nullptr; + } + + void setTrailingRequiresClause(Expr *TrailingRequiresClause); + unsigned getNumTemplateParameterLists() const { return hasExtInfo() ? getExtInfo()->NumTemplParamLists : 0; } @@ -1841,7 +1858,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); using redeclarable_base = Redeclarable; @@ -1878,12 +1896,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, @@ -1893,7 +1912,8 @@ StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, - bool isConstexprSpecified = false); + bool isConstexprSpecified = false, + Expr *TrailingRequiresClause = nullptr); static FunctionDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2248,6 +2268,17 @@ /// the target functionality. bool isTargetMultiVersion() const; + /// \brief Get the associated-constraints of this function declaration. + /// Currently, this will either be a vector of size 1 containing the + /// trailing-requires-clause or an empty vector. + /// + /// Use this instead of getTrailingRequiresClause for concepts APIs that + /// accept an ArrayRef of constraint expressions. + void getAssociatedConstraints(SmallVectorImpl &AC) const { + if (auto *TRC = getTrailingRequiresClause()) + AC.push_back(TRC); + } + void setPreviousDeclaration(FunctionDecl * PrevDecl); FunctionDecl *getCanonicalDecl() override; Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -2053,9 +2053,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); } @@ -2068,7 +2069,8 @@ StorageClass SC, bool isInline, bool isConstexpr, - SourceLocation EndLocation); + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr); static CXXMethodDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2504,7 +2506,8 @@ QualType T, TypeSourceInfo *TInfo, bool isExplicitSpecified, bool isInline, bool isImplicitlyDeclared, bool isConstexpr, - InheritedConstructor Inherited); + InheritedConstructor Inherited, + Expr *TrailingRequiresClause = nullptr); void anchor() override; @@ -2520,7 +2523,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); /// Iterates through the member/base initializer list. using init_iterator = CXXCtorInitializer **; @@ -2722,9 +2726,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); } @@ -2737,7 +2743,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); @@ -2777,9 +2784,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) { setExplicitSpecified(isExplicitSpecified); } @@ -2795,7 +2804,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 explicit. Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2016,6 +2016,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 @@ -170,6 +170,17 @@ def err_at_defs_cxx : Error<"@defs is not supported in Objective-C++">; def err_at_in_class : Error<"unexpected '@' in member specification">; def err_unexpected_semi : Error<"unexpected ';' before %0">; +def err_expected_primary_got_unary : Error< + "expected primary expression before %0; did you forget parentheses?">; +def note_potential_bin_op_in_constraint_logical_or : Note< + "%0 is not considered part of the requires clause (use parentheses to " + "include it)">; +def note_potential_function_call_in_constraint_logical_or : Note< + "function call must be parenthesized to be considered part of the requires " + "clause">; +def err_potential_function_call_in_constraint_logical_or : Error< + "function call must be parenthesized to be considered part of the requires " + "clause">; def err_expected_fn_body : Error< "expected function body after function declarator">; @@ -297,6 +308,12 @@ 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 err_requires_clause_inside_parens : Error< + "trailing requires clause should be placed outside parentheses">; 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 @@ -2468,6 +2468,14 @@ 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_constrained_virtual_method : Error< + "virtual function cannot have a requires clause">; +def err_reference_to_function_with_unsatisfied_constraints : Error< + "invalid reference to function %0: constraints not satisfied">; +def note_ambiguous_atomic_constraints : Note< + "'%0' in the two declarations is not considered equivalent - move it to a " + "concept and reference it from here:">; +def note_ambiguous_atomic_constraints_second : Note<"and here">; def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; @@ -3701,6 +3709,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< @@ -3810,6 +3821,16 @@ "call to " "%select{__device__|__global__|__host__|__host__ __device__|invalid}3 function from" " %select{__device__|__global__|__host__|__host__ __device__|invalid}4 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 %sub{select_special_member_kind}0 inferred target collision: call to both " "%select{__device__|__global__|__host__|__host__ __device__}1 and " Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1620,6 +1620,9 @@ ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast); ExprResult ParseCaseExpression(SourceLocation CaseLoc); ExprResult ParseConstraintExpression(); + ExprResult + ParseConstraintLogicalAndExpression(Expr **RightMostExpr = nullptr); + ExprResult ParseConstraintLogicalOrExpression(); // Expr that doesn't include commas. ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast); @@ -1634,12 +1637,18 @@ ExprResult ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec); - ExprResult ParseCastExpression(bool isUnaryExpression, + /// CastParseOption - Control what ParseCastExpression will parse. + enum CastParseOption { + AnyCastExpr = 0, + UnaryExprOnly, + PrimaryExprOnly + }; + ExprResult ParseCastExpression(CastParseOption ExprType, bool isAddressOfOperand, bool &NotCastExpr, TypeCastState isTypeCast, bool isVectorLiteral = false); - ExprResult ParseCastExpression(bool isUnaryExpression, + ExprResult ParseCastExpression(CastParseOption ExprType, bool isAddressOfOperand = false, TypeCastState isTypeCast = NotTypeCast, bool isVectorLiteral = false); @@ -1834,6 +1843,11 @@ ExprResult ParseCoyieldExpression(); + //===--------------------------------------------------------------------===// + // C++ Concepts + + void ParseTrailingRequiresClause(Declarator &D); + //===--------------------------------------------------------------------===// // C99 6.7.8: Initialization. @@ -2649,6 +2663,8 @@ BalancedDelimiterTracker &Tracker, bool IsAmbiguous, bool RequiresArg = false); + void InitCXXThisScopeForDeclaratorIfRelevant(const Declarator &D, + const DeclSpec &DS, llvm::Optional &ThisScope); bool ParseRefQualifier(bool &RefQualifierIsLValueRef, SourceLocation &RefQualifierLoc); bool isFunctionDeclaratorIdentifierList(); @@ -2756,10 +2772,11 @@ Decl *TagDecl); ExprResult ParseCXXMemberInitializer(Decl *D, bool IsFunction, SourceLocation &EqualLoc); - bool ParseCXXMemberDeclaratorBeforeInitializer(Declarator &DeclaratorInfo, - VirtSpecifiers &VS, - ExprResult &BitfieldSize, - LateParsedAttrList &LateAttrs); + bool + ParseCXXMemberDeclaratorBeforeInitializer(Declarator &DeclaratorInfo, + VirtSpecifiers &VS, + ExprResult &BitfieldSize, + LateParsedAttrList &LateAttrs); void MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(Declarator &D, VirtSpecifiers &VS); DeclGroupPtrTy ParseCXXClassMemberDeclaration( Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -1814,6 +1814,10 @@ /// The asm label, if specified. Expr *AsmLabel; + /// \brief The constraint-expression specified by the trailing + /// requires-clause, or null if no such clause was specified. + Expr *TrailingRequiresClause; + #ifndef _MSC_VER union { #endif @@ -1843,7 +1847,8 @@ GroupingParens(false), FunctionDefinition(FDK_Declaration), Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), - Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr) {} + Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr), + TrailingRequiresClause(nullptr) {} ~Declarator() { clear(); @@ -2389,6 +2394,22 @@ return false; } + /// \brief Sets a trailing requires clause for this declarator. + void setTrailingRequiresClause(Expr *TRC) { + TrailingRequiresClause = TRC; + } + + /// \brief Sets a trailing requires clause for this declarator. + Expr *getTrailingRequiresClause() { + return TrailingRequiresClause; + } + + /// \brief Determine whether a trailing requires clause was written in this + /// declarator. + bool hasTrailingRequiresClause() const { + return TrailingRequiresClause != nullptr; + } + /// 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 @@ -718,6 +718,10 @@ /// This candidate was not viable because it is a non-default multiversioned /// function. ovl_non_default_multiversion_function, + + /// 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 @@ -2634,7 +2634,8 @@ NamedDecl *&OldDecl, bool IsForUsingDecl); bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl, - bool ConsiderCudaAttrs = true); + bool ConsiderCudaAttrs = true, + bool ConsiderRequiresClauses = true); ImplicitConversionSequence TryImplicitConversion(Expr *From, QualType ToType, @@ -2991,10 +2992,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 * @@ -5762,8 +5762,10 @@ Expr *Src); /// Check whether the given expression is a valid constraint expression. - /// A diagnostic is emitted if it is not, and false is returned. - bool CheckConstraintExpression(Expr *CE); + /// A diagnostic is emitted if it is not, false is returned and \p Culprit + /// (if non-null) will contain the first atomic constraint expression that + /// caused the problem. + bool CheckConstraintExpression(Expr *CE, Expr **Culprit = nullptr); private: /// \brief Caches pairs of template-like decls whose associated constraints @@ -5778,6 +5780,13 @@ bool IsMoreConstrained(NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, ArrayRef AC2); + /// \brief If D1 was not at least as constrained as D2, but would've been if + /// a pair of atomic constraints involved had been declared in a concept and + /// not repeated in two separate places in code. + /// \returns true if such a diagnostic was emitted, false otherwise. + bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(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 @@ -8127,6 +8136,10 @@ void InstantiateExceptionSpec(SourceLocation PointOfInstantiation, FunctionDecl *Function); + bool CheckInstantiatedFunctionTemplateConstraints( + SourceLocation PointOfInstantiation, FunctionDecl *Decl, + ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction); FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, const TemplateArgumentList *Args, SourceLocation Loc); Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -3026,10 +3026,12 @@ TypeSourceInfo *TInfo; SourceLocation ToInnerLocStart, ToEndLoc; NestedNameSpecifierLoc ToQualifierLoc; + Expr *TrailingRequiresClause; if (auto Imp = importSeq( FromTy, D->getTypeSourceInfo(), D->getInnerLocStart(), - D->getQualifierLoc(), D->getEndLoc())) - std::tie(T, TInfo, ToInnerLocStart, ToQualifierLoc, ToEndLoc) = *Imp; + D->getQualifierLoc(), D->getEndLoc(), D->getTrailingRequiresClause())) + std::tie(T, TInfo, ToInnerLocStart, ToQualifierLoc, ToEndLoc, + TrailingRequiresClause) = *Imp; else return Imp.takeError(); @@ -3049,7 +3051,8 @@ ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, FromConstructor->isExplicit(), - D->isInlineSpecified(), D->isImplicit(), D->isConstexpr())) + D->isInlineSpecified(), D->isImplicit(), D->isConstexpr(), + InheritedConstructor(), TrailingRequiresClause)) return ToFunction; } else if (CXXDestructorDecl *FromDtor = dyn_cast(D)) { @@ -3067,7 +3070,7 @@ if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - D->isImplicit())) + D->isImplicit(), TrailingRequiresClause)) return ToFunction; CXXDestructorDecl *ToDtor = cast(ToFunction); @@ -3078,19 +3081,22 @@ if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - FromConversion->isExplicit(), D->isConstexpr(), SourceLocation())) + FromConversion->isExplicit(), D->isConstexpr(), SourceLocation(), + TrailingRequiresClause)) return ToFunction; } else if (auto *Method = dyn_cast(D)) { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, Method->getStorageClass(), - Method->isInlineSpecified(), D->isConstexpr(), SourceLocation())) + Method->isInlineSpecified(), D->isConstexpr(), SourceLocation(), + TrailingRequiresClause)) return ToFunction; } else { if (GetImportedOrCreateDecl(ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, NameInfo, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), - D->hasWrittenPrototype(), D->isConstexpr())) + D->hasWrittenPrototype(), D->isConstexpr(), + TrailingRequiresClause)) return ToFunction; } Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1789,7 +1789,8 @@ } else { // Here Qualifier == 0, i.e., we are removing the qualifier (if any). if (hasExtInfo()) { - if (getExtInfo()->NumTemplParamLists == 0) { + if (getExtInfo()->NumTemplParamLists == 0 && + !getExtInfo()->TrailingRequiresClause) { // Save type source info pointer. TypeSourceInfo *savedTInfo = getExtInfo()->TInfo; // Deallocate the extended decl info. @@ -1803,6 +1804,21 @@ } } +void DeclaratorDecl::setTrailingRequiresClause(Expr *TrailingRequiresClause) { + assert(TrailingRequiresClause); + // Make sure the extended decl info is allocated. + if (!hasExtInfo()) { + // Save (non-extended) type source info pointer. + auto *savedTInfo = DeclInfo.get(); + // Allocate external info struct. + DeclInfo = new (getASTContext()) ExtInfo; + // Restore savedTInfo into (extended) decl info. + getExtInfo()->TInfo = savedTInfo; + } + // Set requires clause info. + getExtInfo()->TrailingRequiresClause = TrailingRequiresClause; +} + void DeclaratorDecl::setTemplateParameterListsInfo( ASTContext &Context, ArrayRef TPLists) { assert(!TPLists.empty()); @@ -2652,7 +2668,8 @@ SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, - bool isInlineSpecified, bool isConstexprSpecified) + bool isInlineSpecified, bool isConstexprSpecified, + Expr *TrailingRequiresClause) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), redeclarable_base(C), ODRHash(0), @@ -2681,6 +2698,8 @@ FunctionDeclBits.IsMultiVersion = false; FunctionDeclBits.IsCopyDeductionCandidate = false; FunctionDeclBits.HasODRHash = false; + if (TrailingRequiresClause) + setTrailingRequiresClause(TrailingRequiresClause); } void FunctionDecl::getNameForDiagnostic( @@ -4441,10 +4460,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->setHasWrittenPrototype(hasWrittenPrototype); return New; } @@ -4452,7 +4473,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 @@ -1947,16 +1947,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, @@ -2313,9 +2315,11 @@ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isExplicitSpecified, bool isInline, bool isImplicitlyDeclared, - bool isConstexpr, InheritedConstructor Inherited) + bool isConstexpr, InheritedConstructor Inherited, + Expr *TrailingRequiresClause) : CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, isConstexpr, SourceLocation()) { + SC_None, isInline, isConstexpr, SourceLocation(), + TrailingRequiresClause) { setNumCtorInitializers(0); setInheritingConstructor(static_cast(Inherited)); setImplicit(isImplicitlyDeclared); @@ -2344,7 +2348,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"); @@ -2352,7 +2357,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 { @@ -2473,7 +2478,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 * @@ -2481,12 +2486,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) { @@ -2506,7 +2513,7 @@ return new (C, ID) CXXConversionDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, false, false, false, - SourceLocation()); + SourceLocation(), nullptr); } CXXConversionDecl * @@ -2515,13 +2522,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 @@ -705,6 +705,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 @@ -171,13 +171,18 @@ void TemplateDecl:: getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { - // TODO: Concepts: Append function trailing requires clause. TemplateParams->getAssociatedConstraints(AC); + if (auto *FD = dyn_cast_or_null(getTemplatedDecl())) + if (const Expr *TRC = FD->getTrailingRequiresClause()) + AC.push_back(TRC); } bool TemplateDecl::hasAssociatedConstraints() const { - // TODO: Concepts: Regard function trailing requires clause. - return TemplateParams->hasAssociatedConstraints(); + if (TemplateParams->hasAssociatedConstraints()) + return true; + if (auto *FD = dyn_cast_or_null(getTemplatedDecl())) + return FD->getTrailingRequiresClause(); + return false; } //===----------------------------------------------------------------------===// Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -1978,6 +1978,9 @@ } } + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); + // Check to see if we have a function *definition* which must have a body. if (D.isFunctionDeclarator() && // Look at the next token to make sure that this isn't a function @@ -2208,6 +2211,13 @@ } }; + // C++2a [dcl.decl]p1 + // init-declarator: + // declarator initializer[opt] + // declarator requires-clause + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); + // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = nullptr; switch (TemplateInfo.Kind) { @@ -5878,6 +5888,28 @@ PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); + } else if (Tok.is(tok::kw_requires)) { + if (D.hasGroupingParens()) + // This declarator is declaring a function, but the requires clause is + // in the wrong place: + // void (f() requires true); + // instead of + // void f() requires true; + // or + // void (f()) requires true; + Diag(Tok, diag::err_requires_clause_inside_parens); + else + // This requires clause is in the right place, but will be parsed later + // as part of the init-declarator, member-declarator or + // function-definition. + break; + ConsumeToken(); + ExprResult TrailingRequiresClause = Actions.CorrectDelayedTyposInExpr( + ParseConstraintLogicalOrExpression()); + if (TrailingRequiresClause.isUsable() && D.isFunctionDeclarator() && + !D.hasTrailingRequiresClause()) + // We're already ill-formed if we got here but we'll accept it anyway. + D.setTrailingRequiresClause(TrailingRequiresClause.get()); } else { break; } @@ -6058,6 +6090,47 @@ PrototypeScope.Exit(); } +void Parser::InitCXXThisScopeForDeclaratorIfRelevant( + const Declarator &D, const DeclSpec &DS, + llvm::Optional &ThisScope) { + // C++11 [expr.prim.general]p3: + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type + // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq + // and the end of the function-definition, member-declarator, or + // declarator. + // FIXME: currently, "static" case isn't handled correctly. + bool IsCXX11MemberFunction = getLangOpts().CPlusPlus11 && + D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && + (D.getContext() == DeclaratorContext::MemberContext + ? !D.getDeclSpec().isFriendSpecified() + : D.getContext() == DeclaratorContext::FileContext && + D.getCXXScopeSpec().isValid() && + Actions.CurContext->isRecord()); + if (!IsCXX11MemberFunction) + return; + + Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers()); + if (D.getDeclSpec().isConstexprSpecified() && !getLangOpts().CPlusPlus14) + Q.addConst(); + // FIXME: Collect C++ address spaces. + // If there are multiple different address spaces, the source is invalid. + // Carry on using the first addr space for the qualifiers of 'this'. + // The diagnostic will be given later while creating the function + // prototype for the method. + if (getLangOpts().OpenCLCPlusPlus) { + for (ParsedAttr &attr : DS.getAttributes()) { + LangAS ASIdx = attr.asOpenCLLangAS(); + if (ASIdx != LangAS::Default) { + Q.addAddressSpace(ASIdx); + break; + } + } + } + ThisScope.emplace(Actions, dyn_cast(Actions.CurContext), Q, + IsCXX11MemberFunction); +} + /// ParseFunctionDeclarator - We are after the identifier and have parsed the /// declarator D up to a paren, which indicates that we are parsing function /// arguments. @@ -6071,7 +6144,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 @@ -6166,43 +6240,8 @@ if (ParseRefQualifier(RefQualifierIsLValueRef, RefQualifierLoc)) EndLoc = RefQualifierLoc; - // C++11 [expr.prim.general]p3: - // If a declaration declares a member function or member function - // template of a class X, the expression this is a prvalue of type - // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq - // and the end of the function-definition, member-declarator, or - // declarator. - // FIXME: currently, "static" case isn't handled correctly. - bool IsCXX11MemberFunction = - getLangOpts().CPlusPlus11 && - D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && - (D.getContext() == DeclaratorContext::MemberContext - ? !D.getDeclSpec().isFriendSpecified() - : D.getContext() == DeclaratorContext::FileContext && - D.getCXXScopeSpec().isValid() && - Actions.CurContext->isRecord()); - - Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers()); - if (D.getDeclSpec().isConstexprSpecified() && !getLangOpts().CPlusPlus14) - Q.addConst(); - // FIXME: Collect C++ address spaces. - // If there are multiple different address spaces, the source is invalid. - // Carry on using the first addr space for the qualifiers of 'this'. - // The diagnostic will be given later while creating the function - // prototype for the method. - if (getLangOpts().OpenCLCPlusPlus) { - for (ParsedAttr &attr : DS.getAttributes()) { - LangAS ASIdx = attr.asOpenCLLangAS(); - if (ASIdx != LangAS::Default) { - Q.addAddressSpace(ASIdx); - break; - } - } - } - - Sema::CXXThisScopeRAII ThisScope( - Actions, dyn_cast(Actions.CurContext), Q, - IsCXX11MemberFunction); + llvm::Optional ThisScope; + InitCXXThisScopeForDeclaratorIfRelevant(D, DS, ThisScope); // Parse exception-specification[opt]. bool Delayed = D.isFirstDeclarationOfMember() && @@ -6457,6 +6496,16 @@ // Parse GNU attributes, if present. MaybeParseGNUAttributes(ParmDeclarator); + if (Tok.is(tok::kw_requires)) { + // User tried to define a requires clause in a parameter declaration, + // which is surely not a function declaration. + // void f(int (*g)(int, int) requires true); + Diag(Tok, + diag::err_requires_clause_on_declarator_not_declaring_a_function); + ConsumeToken(); + Actions.CorrectDelayedTyposInExpr(ParseConstraintLogicalOrExpression()); + } + // Remember this parsed parameter in ParamInfo. IdentifierInfo *ParmII = ParmDeclarator.getIdentifier(); Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2265,6 +2265,7 @@ LateParsedAttrList &LateParsedAttrs) { // member-declarator: // declarator pure-specifier[opt] + // declarator requires-clause // declarator brace-or-equal-initializer[opt] // identifier[opt] ':' constant-expression if (Tok.isNot(tok::colon)) @@ -2278,7 +2279,9 @@ BitfieldSize = ParseConstantExpression(); if (BitfieldSize.isInvalid()) SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); - } else { + } else if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(DeclaratorInfo); + else { ParseOptionalCXX11VirtSpecifierSeq( VS, getCurrentClass().IsInterface, DeclaratorInfo.getDeclSpec().getFriendSpecLoc()); @@ -2400,6 +2403,7 @@ /// /// member-declarator: /// declarator virt-specifier-seq[opt] pure-specifier[opt] +/// [C++2a] declarator requires-clause /// declarator constant-initializer[opt] /// [C++11] declarator brace-or-equal-initializer[opt] /// identifier[opt] ':' constant-expression @@ -2626,6 +2630,7 @@ SmallVector DeclsInGroup; ExprResult BitfieldSize; + ExprResult TrailingRequiresClause; bool ExpectSemi = true; // Parse the first declarator. @@ -3737,6 +3742,70 @@ : DeclaratorContext::TrailingReturnContext); } +/// ParseTrailingRequiresClause - Parse a requires-clause as part of a function +/// declaration. +void Parser::ParseTrailingRequiresClause(Declarator &D) { + assert(Tok.is(tok::kw_requires) && "expected requires"); + + SourceLocation RequiresKWLoc = ConsumeToken(); + + ExprResult TrailingRequiresClause; + { + ParseScope ParamScope(this, + Scope::DeclScope | Scope::FunctionPrototypeScope); + + if (D.isFunctionDeclarator()) { + auto &FTI = D.getFunctionTypeInfo(); + if (FTI.Params) + for (auto &Param : ArrayRef(FTI.Params, + FTI.NumParams)){ + auto *ParamDecl = cast(Param.Param); + if (ParamDecl->getIdentifier()) + Actions.PushOnScopeChains(ParamDecl, getCurScope(), + /*AddToContext=*/false); + } + } + + llvm::Optional ThisScope; + InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope); + + TrailingRequiresClause = + Actions.CorrectDelayedTyposInExpr(ParseConstraintLogicalOrExpression()); + } + + if (!D.isDeclarationOfFunction()) { + Diag(RequiresKWLoc, + diag::err_requires_clause_on_declarator_not_declaring_a_function); + return; + } + + if (TrailingRequiresClause.isInvalid()) + SkipUntil({tok::equal, tok::l_brace, tok::arrow, tok::kw_try, tok::comma}, + StopAtSemi | StopBeforeMatch); + else + D.setTrailingRequiresClause(TrailingRequiresClause.get()); + + // Did the user swap the trailing return type and requires clause? + if (D.isFunctionDeclarator() && Tok.is(tok::arrow) && + D.getDeclSpec().getTypeSpecType() == TST_auto) { + SourceLocation ArrowLoc = Tok.getLocation(); + SourceRange Range; + TypeResult TrailingReturnType = + ParseTrailingReturnType(Range, /*MayBeFollowedByDirectInit=*/false); + + if (!TrailingReturnType.isInvalid()) { + Diag(ArrowLoc, + diag::err_requires_clause_must_come_after_trailing_return) + << Range; + auto &FunctionChunk = D.getFunctionTypeInfo(); + FunctionChunk.HasTrailingReturnType = TrailingReturnType.isUsable(); + FunctionChunk.TrailingReturnType = TrailingReturnType.get(); + } else + SkipUntil({tok::equal, tok::l_brace, tok::arrow, tok::kw_try, tok::comma}, + StopAtSemi | StopBeforeMatch); + } +} + /// We have just started parsing the definition of a new class, /// so push that class onto our stack of classes that is currently /// being parsed. Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -22,6 +22,7 @@ #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" @@ -145,7 +146,7 @@ // Silence extension warnings in the sub-expression ExtensionRAIIObject O(Diags); - LHS = ParseCastExpression(false); + LHS = ParseCastExpression(AnyCastExpr); } if (!LHS.isInvalid()) @@ -169,7 +170,7 @@ if (Tok.is(tok::kw_co_yield)) return ParseCoyieldExpression(); - ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false, + ExprResult LHS = ParseCastExpression(AnyCastExpr, /*isAddressOfOperand=*/false, isTypeCast); return ParseRHSOfBinaryExpression(LHS, prec::Assignment); @@ -202,7 +203,7 @@ Sema::ExpressionEvaluationContext::ConstantEvaluated && "Call this function only if your ExpressionEvaluationContext is " "already ConstantEvaluated"); - ExprResult LHS(ParseCastExpression(false, false, isTypeCast)); + ExprResult LHS(ParseCastExpression(AnyCastExpr, false, isTypeCast)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnConstantExpression(Res); } @@ -220,7 +221,7 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - ExprResult LHS(ParseCastExpression(false, false, NotTypeCast)); + ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnCaseExpr(CaseLoc, Res); } @@ -234,13 +235,138 @@ ExprResult Parser::ParseConstraintExpression() { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false)); + ExprResult LHS(ParseCastExpression(AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); - if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) + if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { + Actions.CorrectDelayedTyposInExpr(Res); return ExprError(); + } return Res; } +/// \brief Parse a constraint-logical-and-expression. +/// +/// \param RightMostExpr If provided, will receive the right-most atomic +/// constraint that was parsed. +/// \verbatim +/// C++2a[temp.constr.decl]p1 +/// constraint-logical-and-expression: +/// primary-expression +/// constraint-logical-and-expression '&&' primary-expression +/// +/// \endverbatim +ExprResult Parser::ParseConstraintLogicalAndExpression(Expr **RightMostExpr) { + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ExprResult LHS(ParseCastExpression(PrimaryExprOnly)); + if (!LHS.isUsable()) + return ExprError(); + if (RightMostExpr) + *RightMostExpr = LHS.get(); + while (Tok.is(tok::ampamp)) { + SourceLocation LogicalAndLoc = ConsumeToken(); + ExprResult RHS(ParseCastExpression(PrimaryExprOnly)); + if (!RHS.isUsable()) { + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + if (RightMostExpr) + *RightMostExpr = RHS.get(); + ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalAndLoc, + tok::ampamp, LHS.get(), RHS.get()); + if (!Op.isUsable()) { + Actions.CorrectDelayedTyposInExpr(RHS); + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + LHS = Op; + } + return LHS; +} + +/// \brief Parse a constraint-logical-or-expression. +/// +/// \verbatim +/// C++2a[temp.constr.decl]p1 +/// constraint-logical-or-expression: +/// constraint-logical-and-expression +/// constraint-logical-or-expression '||' +/// constraint-logical-and-expression +/// +/// \endverbatim +ExprResult Parser::ParseConstraintLogicalOrExpression() { + Expr *RightMostExpr = nullptr; + ExprResult LHS(ParseConstraintLogicalAndExpression(&RightMostExpr)); + if (!LHS.isUsable()) + return ExprError(); + while (Tok.is(tok::pipepipe)) { + SourceLocation LogicalOrLoc = ConsumeToken(); + ExprResult RHS(ParseConstraintLogicalAndExpression()); + if (!RHS.isUsable()) { + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + RightMostExpr = RHS.get(); + ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalOrLoc, + tok::pipepipe, LHS.get(), RHS.get()); + if (!Op.isUsable()) { + Actions.CorrectDelayedTyposInExpr(RHS); + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + LHS = Op; + } + + Expr *Culprit; + if (!Actions.CheckConstraintExpression(LHS.get(), &Culprit)) { + if ((Culprit->getType()->isFunctionType() || + Culprit->getType()->isSpecificBuiltinType(BuiltinType::Overload)) && + Tok.is(tok::l_paren)) + // We have the following case: + // template requires func(0) struct S { }; + // The user probably isn't aware of the parentheses required around the + // function call, and we're only going to parse 'func' as the + // primary-expression, and complain that it is of non-bool type. + Diag(Tok.getLocation(), + diag::note_potential_function_call_in_constraint_logical_or); + else if (getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, + getLangOpts().CPlusPlus11)) + // We have the following case: + // template requires size_ == 0 struct S { }; + // The user probably isn't aware of the parentheses required around the + // binary operator, and we're only going to parse 'func' as the + // first operand, and complain that it is of non-bool type. + Diag(Tok.getLocation(), + diag::note_potential_bin_op_in_constraint_logical_or) << + Tok.getKind(); + + Actions.CorrectDelayedTyposInExpr(LHS); + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + if (Tok.is(tok::l_paren) && + isa(RightMostExpr) && + RightMostExpr->isTypeDependent()) { + // We're facing one of the following cases: + // template requires func( + // template void foo() requires func( + // In the first case, '(' cannot start a declaration, and in the second, + // '(' cannot follow the requires-clause in a function-definition nor in + // a member-declarator or init-declarator. + // The user probably tried to call func but didn't realize he had to + // parenthesize the function call for it to be included in the constraint + // expression. + Diag(Tok.getLocation(), + diag::err_potential_function_call_in_constraint_logical_or); + SkipUntil(tok::r_paren, StopAtSemi); + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + + return LHS; +} + bool Parser::isNotExpressionStart() { tok::TokenKind K = Tok.getKind(); if (K == tok::l_brace || K == tok::r_brace || @@ -414,7 +540,7 @@ } else if (getLangOpts().CPlusPlus && NextTokPrec <= prec::Conditional) RHS = ParseAssignmentExpression(); else - RHS = ParseCastExpression(false); + RHS = ParseCastExpression(AnyCastExpr); if (RHS.isInvalid()) { // FIXME: Errors generated by the delayed typo correction should be @@ -519,18 +645,18 @@ } } -/// Parse a cast-expression, or, if \p isUnaryExpression is true, -/// parse a unary-expression. +/// Parse a cast-expression, unary-expression or primary-expression, based +/// on \p ExprType. /// /// \p isAddressOfOperand exists because an id-expression that is the /// operand of address-of gets special treatment due to member pointers. /// -ExprResult Parser::ParseCastExpression(bool isUnaryExpression, +ExprResult Parser::ParseCastExpression(CastParseOption ExprType, bool isAddressOfOperand, TypeCastState isTypeCast, bool isVectorLiteral) { bool NotCastExpr; - ExprResult Res = ParseCastExpression(isUnaryExpression, + ExprResult Res = ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, isTypeCast, @@ -755,7 +881,7 @@ /// '__is_rvalue_expr' /// \endverbatim /// -ExprResult Parser::ParseCastExpression(bool isUnaryExpression, +ExprResult Parser::ParseCastExpression(CastParseOption ExprType, bool isAddressOfOperand, bool &NotCastExpr, TypeCastState isTypeCast, @@ -765,6 +891,17 @@ auto SavedType = PreferredType; NotCastExpr = false; + auto EnsureNotPrimary = [&] { + if (ExprType == PrimaryExprOnly) { + if (Tok.is(tok::annot_typename)) + Diag(Tok, diag::err_expected_primary_got_unary) << "type name"; + else + Diag(Tok, diag::err_expected_primary_got_unary) << Tok.getKind(); + return true; + } + return false; + }; + // This handles all of cast-expression, unary-expression, postfix-expression, // and primary-expression. We handle them together like this for efficiency // and to simplify handling of an expression starting with a '(' token: which @@ -780,9 +917,19 @@ case tok::l_paren: { // If this expression is limited to being a unary-expression, the parent can // not start a cast expression. - ParenParseOption ParenExprType = - (isUnaryExpression && !getLangOpts().CPlusPlus) ? CompoundLiteral - : CastExpr; + ParenParseOption ParenExprType; + switch (ExprType) { + case CastParseOption::UnaryExprOnly: + if (!getLangOpts().CPlusPlus) + ParenExprType = CompoundLiteral; + LLVM_FALLTHROUGH; + case CastParseOption::AnyCastExpr: + ParenExprType = ParenParseOption::CastExpr; + break; + case CastParseOption::PrimaryExprOnly: + ParenExprType = FoldExpr; + break; + } ParsedType CastTy; SourceLocation RParenLoc; Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/, @@ -847,8 +994,8 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super)); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); - + return ParseCastExpression(ExprType, isAddressOfOperand); + case tok::identifier: { // primary-expression: identifier // unqualified-id: identifier // constant: enumeration-constant @@ -935,7 +1082,7 @@ = RevertibleTypeTraits.find(II); if (Known != RevertibleTypeTraits.end()) { Tok.setKind(Known->second); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, + return ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, isTypeCast); } } @@ -947,7 +1094,7 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::identifier)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); + return ParseCastExpression(ExprType, isAddressOfOperand); } } @@ -1062,7 +1209,7 @@ Tok.is(tok::r_paren) ? nullptr : &Replacement); if (!Res.isInvalid() && Res.isUnset()) { UnconsumeToken(Replacement); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, + return ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, isTypeCast); } if (!Res.isInvalid() && Tok.is(tok::less)) @@ -1110,6 +1257,8 @@ case tok::plusplus: // unary-expression: '++' unary-expression [C99] case tok::minusminus: { // unary-expression: '--' unary-expression [C99] + if (EnsureNotPrimary()) + return ExprError(); // C++ [expr.unary] has: // unary-expression: // ++ cast-expression @@ -1122,7 +1271,8 @@ // One special case is implicitly handled here: if the preceding tokens are // an ambiguous cast expression, such as "(T())++", then we recurse to // determine whether the '++' is prefix or postfix. - Res = ParseCastExpression(!getLangOpts().CPlusPlus, + Res = ParseCastExpression(getLangOpts().CPlusPlus ? + UnaryExprOnly : AnyCastExpr, /*isAddressOfOperand*/false, NotCastExpr, NotTypeCast); if (NotCastExpr) { @@ -1138,10 +1288,12 @@ return Res; } case tok::amp: { // unary-expression: '&' cast-expression + if (EnsureNotPrimary()) + return ExprError(); // Special treatment because of member pointers SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc); - Res = ParseCastExpression(false, true); + Res = ParseCastExpression(AnyCastExpr, true); if (!Res.isInvalid()) Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); return Res; @@ -1154,17 +1306,21 @@ case tok::exclaim: // unary-expression: '!' cast-expression case tok::kw___real: // unary-expression: '__real' cast-expression [GNU] case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU] + if (EnsureNotPrimary()) + return ExprError(); SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc); - Res = ParseCastExpression(false); + Res = ParseCastExpression(AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); return Res; } case tok::kw_co_await: { // unary-expression: 'co_await' cast-expression + if (EnsureNotPrimary()) + return ExprError(); SourceLocation CoawaitLoc = ConsumeToken(); - Res = ParseCastExpression(false); + Res = ParseCastExpression(AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get()); return Res; @@ -1172,9 +1328,11 @@ case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU] // __extension__ silences extension warnings in the subexpression. + if (EnsureNotPrimary()) + return ExprError(); ExtensionRAIIObject O(Diags); // Use RAII to do this. SourceLocation SavedLoc = ConsumeToken(); - Res = ParseCastExpression(false); + Res = ParseCastExpression(AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); return Res; @@ -1191,8 +1349,12 @@ case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')' case tok::kw___builtin_omp_required_simd_align: + if (EnsureNotPrimary()) + return ExprError(); return ParseUnaryExprOrTypeTraitExpression(); case tok::ampamp: { // unary-expression: '&&' identifier + if (EnsureNotPrimary()) + return ExprError(); SourceLocation AmpAmpLoc = ConsumeToken(); if (Tok.isNot(tok::identifier)) return ExprError(Diag(Tok, diag::err_expected) << tok::identifier); @@ -1211,9 +1373,13 @@ case tok::kw_dynamic_cast: case tok::kw_reinterpret_cast: case tok::kw_static_cast: + if (EnsureNotPrimary()) + return ExprError(); Res = ParseCXXCasts(); break; case tok::kw_typeid: + if (EnsureNotPrimary()) + return ExprError(); Res = ParseCXXTypeid(); break; case tok::kw___uuidof: @@ -1281,6 +1447,10 @@ return ExprError(); } + // Everything henceforth is a postfix-expression. + if (EnsureNotPrimary()) + return ExprError(); + if (SavedKind == tok::kw_typename) { // postfix-expression: typename-specifier '(' expression-list[opt] ')' // typename-specifier braced-init-list @@ -1317,8 +1487,8 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::annot_cxxscope)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, + isTypeCast); Token Next = NextToken(); if (Next.is(tok::annot_template_id)) { @@ -1331,8 +1501,8 @@ ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); AnnotateTemplateIdTokenAsType(); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, + isTypeCast); } } @@ -1348,7 +1518,7 @@ // translate it into a type and continue parsing as a cast // expression. AnnotateTemplateIdTokenAsType(); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, + return ParseCastExpression(ExprType, isAddressOfOperand, NotCastExpr, isTypeCast); } @@ -1366,15 +1536,21 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::coloncolon)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); + return ParseCastExpression(ExprType, isAddressOfOperand); // ::new -> [C++] new-expression // ::delete -> [C++] delete-expression SourceLocation CCLoc = ConsumeToken(); - if (Tok.is(tok::kw_new)) + if (Tok.is(tok::kw_new)) { + if (EnsureNotPrimary()) + return ExprError(); return ParseCXXNewExpression(true, CCLoc); - if (Tok.is(tok::kw_delete)) + } + if (Tok.is(tok::kw_delete)) { + if (EnsureNotPrimary()) + return ExprError(); return ParseCXXDeleteExpression(true, CCLoc); + } // This is not a type name or scope specifier, it is an invalid expression. Diag(CCLoc, diag::err_expected_expression); @@ -1382,12 +1558,18 @@ } case tok::kw_new: // [C++] new-expression + if (EnsureNotPrimary()) + return ExprError(); return ParseCXXNewExpression(false, Tok.getLocation()); case tok::kw_delete: // [C++] delete-expression + if (EnsureNotPrimary()) + return ExprError(); return ParseCXXDeleteExpression(false, Tok.getLocation()); case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' + if (EnsureNotPrimary()) + return ExprError(); Diag(Tok, diag::warn_cxx98_compat_noexcept_expr); SourceLocation KeyLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); @@ -1465,6 +1647,11 @@ // are compiling for OpenCL, we need to return an error as this implies // that the address of the function is being taken, which is illegal in CL. + if (ExprType == PrimaryExprOnly) + // This is strictly a primary-expression - no postfix-expr pieces should be + // parsed. + return Res; + // These can be followed by postfix-expr pieces. PreferredType = SavedType; Res = ParsePostfixExpressionSuffix(Res); @@ -1912,7 +2099,7 @@ return ExprError(); } - Operand = ParseCastExpression(true/*isUnaryExpression*/); + Operand = ParseCastExpression(UnaryExprOnly); } else { // If it starts with a '(', we know that it is either a parenthesized // type-name, or it is a unary-expression that starts with a compound @@ -2426,8 +2613,8 @@ RParenLoc = T.getCloseLocation(); PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get()); - ExprResult SubExpr = ParseCastExpression(/*isUnaryExpression=*/false); - + ExprResult SubExpr = ParseCastExpression(AnyCastExpr); + if (Ty.isInvalid() || SubExpr.isInvalid()) return ExprError(); @@ -2507,7 +2694,7 @@ // Parse the cast-expression that follows it next. // isVectorLiteral = true will make sure we don't parse any // Postfix expression yet - Result = ParseCastExpression(/*isUnaryExpression=*/false, + Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr, /*isAddressOfOperand=*/false, /*isTypeCast=*/IsTypeCast, /*isVectorLiteral=*/true); @@ -2559,7 +2746,7 @@ PreferredType.enterTypeCast(Tok.getLocation(), CastTy.get()); // Parse the cast-expression that follows it next. // TODO: For cast expression with CastTy. - Result = ParseCastExpression(/*isUnaryExpression=*/false, + Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr, /*isAddressOfOperand=*/false, /*isTypeCast=*/IsTypeCast); if (!Result.isInvalid()) { Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -2995,7 +2995,7 @@ return ExprError(); } - ExprResult Operand(ParseCastExpression(false)); + ExprResult Operand(ParseCastExpression(AnyCastExpr)); if (Operand.isInvalid()) return Operand; @@ -3226,7 +3226,7 @@ // If it is not a cast-expression, NotCastExpr will be true and no token // will be consumed. ColonProt.restore(); - Result = ParseCastExpression(false/*isUnaryExpression*/, + Result = ParseCastExpression(AnyCastExpr, false/*isAddressofOperand*/, NotCastExpr, // type-id has priority. Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -1738,7 +1738,7 @@ SourceLocation ELoc = Tok.getLocation(); ExprResult LHS(ParseCastExpression( - /*isUnaryExpression=*/false, /*isAddressOfOperand=*/false, NotTypeCast)); + AnyCastExpr, /*isAddressOfOperand=*/false, NotTypeCast)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); @@ -1997,7 +1997,7 @@ Kind == OMPC_if; if (NeedAnExpression) { SourceLocation ELoc = Tok.getLocation(); - ExprResult LHS(ParseCastExpression(false, false, NotTypeCast)); + ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast)); Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); Index: lib/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -130,7 +130,8 @@ if (TryConsumeToken(tok::kw_requires)) { OptionalRequiresClauseConstraintER = - Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + Actions.CorrectDelayedTyposInExpr( + ParseConstraintLogicalOrExpression()); if (!OptionalRequiresClauseConstraintER.isUsable()) { // Skip until the semi-colon or a '}'. SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); @@ -254,9 +255,31 @@ }); LateParsedAttrList LateParsedAttrs(true); - if (DeclaratorInfo.isFunctionDeclarator()) + if (DeclaratorInfo.isFunctionDeclarator()) { MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs); + if (Tok.is(tok::kw_requires)) { + ParseScope RequiresClauseScope(this, + Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); + auto &FunctionInfo = DeclaratorInfo.getFunctionTypeInfo(); + for (unsigned I = 0, C = FunctionInfo.NumParams; I != C; ++I) { + auto *ND = dyn_cast(FunctionInfo.Params[I].Param); + if (!ND) + continue; + if (ND->getDeclName()) + Actions.PushOnScopeChains(ND, getCurScope(), /*AddToContext=*/false); + + if (auto *ED = dyn_cast(ND)) + for (auto *EI : ED->enumerators()) + Actions.PushOnScopeChains(EI, getCurScope(), + /*AddToContext=*/false); + } + ParseTrailingRequiresClause(DeclaratorInfo); + } + } + if (DeclaratorInfo.isFunctionDeclarator() && isStartOfFunctionDefinition(DeclaratorInfo)) { Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -1005,6 +1005,10 @@ // 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. + TPR = TPResult::True; } else { break; } @@ -1970,7 +1974,6 @@ /// 'throw' '(' type-id-list[opt] ')' /// Parser::TPResult Parser::TryParseFunctionDeclarator() { - // The '(' is already parsed. TPResult TPR = TryParseParameterDeclarationClause(); Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -1973,7 +1973,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 @@ -22,7 +22,8 @@ using namespace clang; using namespace sema; -bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) { +bool Sema::CheckConstraintExpression(Expr *ConstraintExpression, + Expr **Culprit) { // C++2a [temp.constr.atomic]p1 // ..E shall be a constant expression of type bool. @@ -41,6 +42,8 @@ Diag(ConstraintExpression->getExprLoc(), diag::err_non_bool_atomic_constraint) << Type << ConstraintExpression->getSourceRange(); + if (Culprit) + *Culprit = ConstraintExpression; return false; } return true; @@ -399,6 +402,25 @@ const ASTTemplateArgumentListInfo *ParameterMapping = nullptr) : ConstraintExpr{ConstraintExpr}, ParameterMapping{ParameterMapping} {} + bool hasMatchingParameterMapping(ASTContext &C, + const AtomicConstraint &Other) const { + 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; + } + bool subsumes(ASTContext &C, const AtomicConstraint &Other) const { // C++ [temp.constr.order] p2 // - an atomic constraint A subsumes another atomic constraint B @@ -416,23 +438,7 @@ 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; + return hasMatchingParameterMapping(C, Other); } const Expr *ConstraintExpr; @@ -774,22 +780,22 @@ return Res; } +template static bool subsumes(Sema &S, ArrayRef P, - ArrayRef Q) { + ArrayRef Q, bool &DoesSubsume, + AtomicSubsumptionEvaluator E) { // 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; + return true; const NormalForm PDNF = makeDNF(*PNormalized); auto QNormalized = NormalizedConstraint::fromConstraintExprs(S, Q); if (!QNormalized) - // Program is ill formed at this point. - return false; + return true; const NormalForm QCNF = makeCNF(*QNormalized); // C++ [temp.constr.order] p2 @@ -806,7 +812,7 @@ bool Found = false; for (const AtomicConstraint *Pia : Pi) { for (const AtomicConstraint *Qjb : Qj) { - if (Pia->subsumes(S.Context, *Qjb)) { + if (E(*Pia, *Qjb)) { Found = true; break; } @@ -815,11 +821,13 @@ break; } if (!Found) { + DoesSubsume = false; return false; } } } - return true; + DoesSubsume = true; + return false; } bool Sema::IsMoreConstrained(NamedDecl *D1, ArrayRef AC1, @@ -832,10 +840,79 @@ std::pair Key{D1, D2}; auto CacheEntry = SubsumptionCache.find(Key); - if (CacheEntry != SubsumptionCache.end()) { + if (CacheEntry != SubsumptionCache.end()) return CacheEntry->second; - } - bool Subsumes = subsumes(*this, AC1, AC2); + + bool Subsumes; + if (subsumes(*this, AC1, AC2, Subsumes, + [this] (const AtomicConstraint &A, const AtomicConstraint &B) { + return A.subsumes(Context, B); + })) + // Program is ill-formed at this point. + return false; SubsumptionCache.try_emplace(Key, Subsumes); return Subsumes; -} \ No newline at end of file +} + +bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, + ArrayRef AC1, NamedDecl *D2, ArrayRef AC2) { + if (AC1.empty() || AC2.empty()) + return false; + + auto NormalExprEvaluator = + [this] (const AtomicConstraint &A, const AtomicConstraint &B) { + return A.subsumes(Context, B); + }; + + const Expr *AmbiguousAtomic1 = nullptr, *AmbiguousAtomic2 = nullptr; + auto IdenticalExprEvaluator = + [&] (const AtomicConstraint &A, const AtomicConstraint &B) { + if (!A.hasMatchingParameterMapping(Context, B)) + return false; + const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; + if (EA == EB) + return true; + + // Not the same source level expression - are the expressions + // identical? + llvm::FoldingSetNodeID IDA, IDB; + EA->Profile(IDA, Context, /*Cannonical=*/true); + EB->Profile(IDB, Context, /*Cannonical=*/true); + if (IDA != IDB) + return false; + + AmbiguousAtomic1 = EA; + AmbiguousAtomic2 = EB; + return true; + }; + + { + // The subsumption checks might cause diagnostics + SFINAETrap Trap(*this); + bool Is1AtLeastAs2Normally, Is2AtLeastAs1Normally; + if (subsumes(*this, AC1, AC2, Is1AtLeastAs2Normally, NormalExprEvaluator)) + return false; + if (subsumes(*this, AC2, AC1, Is2AtLeastAs1Normally, NormalExprEvaluator)) + return false; + bool Is1AtLeastAs2, Is2AtLeastAs1; + if (subsumes(*this, AC1, AC2, Is1AtLeastAs2, IdenticalExprEvaluator)) + return false; + if (subsumes(*this, AC2, AC1, Is2AtLeastAs1, IdenticalExprEvaluator)) + return false; + if (Is1AtLeastAs2 == Is1AtLeastAs2Normally && + Is2AtLeastAs1 == Is2AtLeastAs1Normally) + // Same result - no ambiguity was caused by identical atomic expressions. + return false; + } + + // A different result! Some ambiguous atomic constraint(s) caused a difference + assert(AmbiguousAtomic1 && AmbiguousAtomic2); + + Diag(AmbiguousAtomic1->getBeginLoc(), diag::note_ambiguous_atomic_constraints) + << const_cast(AmbiguousAtomic1) + << AmbiguousAtomic1->getSourceRange(); + Diag(AmbiguousAtomic2->getBeginLoc(), + diag::note_ambiguous_atomic_constraints_second) + << AmbiguousAtomic2->getSourceRange(); + return true; +} Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -7608,7 +7608,13 @@ Path.Decls = Path.Decls.slice(1)) { NamedDecl *D = Path.Decls.front(); if (CXXMethodDecl *MD = dyn_cast(D)) { - if (MD->isVirtual() && !S->IsOverload(Method, MD, false)) + if (MD->isVirtual() && + !S->IsOverload( + Method, MD, /*UseMemberUsingDeclRules=*/false, + /*ConsiderCudaAttrs=*/true, + // C++2a [class.virtual]p2 does not consider requires clauses + // when overriding. + /*ConsiderRequiresClauses=*/false)) return true; } } @@ -7961,6 +7967,7 @@ bool isExplicit = D.getDeclSpec().isExplicitSpecified(); bool isConstexpr = D.getDeclSpec().isConstexprSpecified(); + Expr *TrailingRequiresClause = D.getTrailingRequiresClause(); // Check that the return type is not an abstract class type. // For record types, this is done by the AbstractClassUsageDiagnoser once @@ -7980,7 +7987,8 @@ return CXXConstructorDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, isExplicit, isInline, - /*isImplicitlyDeclared=*/false, isConstexpr); + /*isImplicitlyDeclared=*/false, isConstexpr, InheritedConstructor(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. @@ -7990,7 +7998,8 @@ CXXDestructorDecl *NewDD = CXXDestructorDecl::Create(SemaRef.Context, Record, D.getBeginLoc(), NameInfo, R, TInfo, isInline, - /*isImplicitlyDeclared=*/false); + /*isImplicitlyDeclared=*/false, + TrailingRequiresClause); // If the destructor needs an implicit exception specification, set it // now. FIXME: It'd be nice to be able to create the right type to start @@ -8010,7 +8019,8 @@ return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), Name, R, TInfo, SC, isInline, - /*hasPrototype=*/true, isConstexpr); + /*hasPrototype=*/true, isConstexpr, + TrailingRequiresClause); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -8024,7 +8034,8 @@ IsVirtualOkay = true; return CXXConversionDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, isInline, isExplicit, isConstexpr, SourceLocation()); + TInfo, isInline, isExplicit, isConstexpr, SourceLocation(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) { SemaRef.CheckDeductionGuideDeclarator(D, R, SC); @@ -8048,7 +8059,8 @@ // This is a C++ method declaration. CXXMethodDecl *Ret = CXXMethodDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, SC, isInline, isConstexpr, SourceLocation()); + TInfo, SC, isInline, isConstexpr, SourceLocation(), + TrailingRequiresClause); IsVirtualOkay = !Ret->isStatic(); return Ret; } else { @@ -8062,7 +8074,7 @@ // - we're in C++ (where every function has a prototype), return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC, isInline, true /*HasPrototype*/, - isConstexpr); + isConstexpr, TrailingRequiresClause); } } @@ -10217,6 +10229,17 @@ } } } + if (Method->isVirtual() && + (NewFD->getTrailingRequiresClause() || + std::any_of(Method->overridden_methods().begin(), + Method->overridden_methods().end(), + [](const CXXMethodDecl *M) { + return M->getTrailingRequiresClause() != nullptr; + }))) + // C++2a [class.virtual]p6 + // A virtual method shall not have a requires-clause. + Diag(NewFD->getTrailingRequiresClause()->getBeginLoc(), + diag::err_constrained_virtual_method); if (Method->isStatic()) checkThisInStaticMemberFunctionType(Method); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7449,7 +7449,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 @@ -11126,7 +11126,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(); @@ -15422,6 +15423,11 @@ if (checkThisInStaticMemberFunctionExceptionSpec(Method)) return true; + // Check the trailing requires clause + if (Expr *E = Method->getTrailingRequiresClause()) + if (!Finder.TraverseStmt(E)) + return true; + return checkThisInStaticMemberFunctionAttributes(Method); } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -330,6 +330,30 @@ diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); + // [expr.prim.id]p4 + // A program that refers explicitly or implicitly to a function with a + // trailing requires-clause whose constraint-expression is not satisfied, + // other than to declare it, is ill-formed. [...] + // + // See if this is a function with constraints that need to be satisfied. + if (FunctionDecl *FD = dyn_cast(D)) { + if (Expr *RC = FD->getTrailingRequiresClause()) { + ConstraintSatisfaction Satisfaction; + bool Failed = CheckConstraintSatisfaction(RC, Satisfaction); + if (Failed) + // A diagnostic will have already been generated (non-constant + // constraint expression, for example) + return true; + if (!Satisfaction.IsSatisfied) { + Diag(Loc, + diag::err_reference_to_function_with_unsatisfied_constraints) + << D; + DiagnoseUnsatisfiedConstraint(Satisfaction); + return true; + } + } + } + return false; } @@ -16859,7 +16883,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 @@ -1101,7 +1101,8 @@ } bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, - bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs) { + bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs, + bool ConsiderRequiresClauses) { // C++ [basic.start.main]p2: This function shall not be overloaded. if (New->isMain()) return false; @@ -1237,23 +1238,38 @@ if (getLangOpts().CUDA && ConsiderCudaAttrs) { // Don't allow overloading of destructors. (In theory we could, but it // would be a giant change to clang.) - if (isa(New)) - return false; - - CUDAFunctionTarget NewTarget = IdentifyCUDATarget(New), - OldTarget = IdentifyCUDATarget(Old); - if (NewTarget == CFT_InvalidTarget) - return false; + if (!isa(New)) { + CUDAFunctionTarget NewTarget = IdentifyCUDATarget(New), + OldTarget = IdentifyCUDATarget(Old); + if (NewTarget != CFT_InvalidTarget) { + assert((OldTarget != CFT_InvalidTarget) && + "Unexpected invalid target."); + + // Allow overloading of functions with same signature and different CUDA + // target attributes. + if (NewTarget != OldTarget) + return true; + } + } + } - assert((OldTarget != CFT_InvalidTarget) && "Unexpected invalid target."); + if (ConsiderRequiresClauses) { + Expr *NewRC = New->getTrailingRequiresClause(), + *OldRC = Old->getTrailingRequiresClause(); + if ((NewRC != nullptr) != (OldRC != nullptr)) + // RC are most certainly different - these are overloads. + return true; - // Allow overloading of functions with same signature and different CUDA - // target attributes. - return NewTarget != OldTarget; + 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; + } } - // TODO: Concepts: Check function trailing requires clauses here. - // The signatures match; this is not an overload. return false; } @@ -6159,6 +6175,17 @@ 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; + return; + } + } + // Determine the implicit conversion sequences for each of the // arguments. for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) { @@ -6660,6 +6687,17 @@ 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; + } + } + // Determine the implicit conversion sequences for each of the // arguments. for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) { @@ -7011,6 +7049,17 @@ return; } + Expr *RequiresClause = Conversion->getTrailingRequiresClause(); + if (LangOpts.ConceptsTS && RequiresClause) { + ConstraintSatisfaction Satisfaction; + if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + !Satisfaction.IsSatisfied) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_constraints_not_satisfied; + return; + } + } + // We won't go through a user-defined type conversion function to convert a // derived to base as such conversions are given Conversion Rank. They only // go through a copy constructor. 13.3.3.1.2-p4 [over.ics.user] @@ -9308,6 +9357,30 @@ return Cmp == Comparison::Better; } + // -— F1 and F2 are non-template functions with the same + // parameter-type-lists, and F1 is more constrained than F2 [...], + if (Cand1.Function && Cand2.Function && !Cand1IsSpecialization && + !Cand2IsSpecialization && Cand1.Function->hasPrototype() && + Cand2.Function->hasPrototype()) { + auto *PT1 = cast(Cand1.Function->getFunctionType()); + auto *PT2 = cast(Cand2.Function->getFunctionType()); + if (PT1->getNumParams() == PT2->getNumParams() && + PT1->isVariadic() == PT2->isVariadic() && + S.FunctionParamTypesAreEqual(PT1, PT2)) { + Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); + Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); + if (RC1 && RC2) { + bool AtLeastAsConstrained1 = S.IsMoreConstrained(Cand1.Function, RC1, + Cand2.Function, RC2); + bool AtLeastAsConstrained2 = S.IsMoreConstrained(Cand2.Function, RC2, + Cand1.Function, RC1); + if (AtLeastAsConstrained1 != AtLeastAsConstrained2) + return AtLeastAsConstrained1; + } 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) > @@ -9598,6 +9671,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->getBeginLoc(), + 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(); }); @@ -9654,6 +9745,42 @@ MaybeEmitInheritedConstructorNote(*this, Found); } +template +static void MaybeDiagnoseAmbiguousConstraints(Sema &S, + CandidateIterator CandBegin, CandidateIterator CandEnd, + ConstraintExtractor Extractor) { + // Perhaps the ambiguity was caused by two atomic constraints that are + // 'identical' but not equivalent: + // + // void foo() requires (sizeof(T) > 4) { } // #1 + // void foo() requires (sizeof(T) > 4) && T::value { } // #2 + // + // The 'sizeof(T) > 4' constraints are seemingly equivalent and should cause + // #2 to subsume #1, but these constraint are not considered equivalent + // according to the subsumption rules because they are not the same + // source-level construct. This behavior is quite confusing and we should try + // to help the user figure out what happened. + for (auto I = CandBegin; I != CandEnd; ++I) { + SmallVector IAC; + NamedDecl *ICand = Extractor(*I, IAC); + if (!ICand || IAC.empty()) + continue; + for (auto J = I + 1; J != CandEnd; ++J) { + SmallVector JAC; + NamedDecl *JCand = Extractor(*J, JAC); + if (!JCand || JAC.empty()) + continue; + // The diagnostic can only happen if there are associated constraints on + // both sides (there needs to be some identical atomic constraint). + if (S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(ICand, IAC, JCand, + JAC)) + // Just show the user one diagnostic, he'll probably figure it out from + // here. + return; + } + } +} + // Notes the location of all overload candidates designated through // OverloadedExpr void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType, @@ -9675,6 +9802,21 @@ NoteOverloadCandidate(*I, Fun, DestType, TakingAddress); } } + + MaybeDiagnoseAmbiguousConstraints(*this, OvlExpr->decls_begin(), + OvlExpr->decls_end(), + [] (NamedDecl *ND, SmallVectorImpl &AC) -> NamedDecl * { + if (auto *FunTmpl = + dyn_cast(ND->getUnderlyingDecl())) { + FunTmpl->getAssociatedConstraints(AC); + return ND; + } else if (FunctionDecl *Fun + = dyn_cast(ND->getUnderlyingDecl())) { + Fun->getAssociatedConstraints(AC); + return ND; + } + return nullptr; + }); } /// Diagnoses an ambiguous conversion. The partial diagnostic is the @@ -10470,6 +10612,20 @@ case ovl_non_default_multiversion_function: // Do nothing, these should simply be ignored. break; + + case ovl_fail_constraints_not_satisfied: { + std::string Description; + OverloadCandidateKind FnKind = + ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Description).first; + 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); + } } } @@ -10858,8 +11014,20 @@ } } - if (I != E) + if (I != E) { S.Diag(OpLoc, diag::note_ovl_too_many_candidates) << int(E - I); + return; + } + + MaybeDiagnoseAmbiguousConstraints(S, Cands.begin(), E, + [] (const OverloadCandidate *Cand, SmallVectorImpl &AC) + -> NamedDecl * { + if (FunctionDecl *FD = Cand->Function) { + FD->getAssociatedConstraints(AC); + return FD; + } + return nullptr; + }); } static SourceLocation @@ -10964,6 +11132,18 @@ if (I != E) S.Diag(Loc, diag::note_ovl_too_many_candidates) << int(E - I); + + MaybeDiagnoseAmbiguousConstraints(S, Cands.begin(), E, + [] (const TemplateSpecCandidate *Cand, + SmallVectorImpl &AC) -> NamedDecl * { + if (Cand->Specialization) + if (FunctionDecl *FD = dyn_cast(Cand->Specialization)) + if (TemplateDecl *TD = FD->getPrimaryTemplate()) { + TD->getAssociatedConstraints(AC); + return FD; + } + return nullptr; + }); } // [PossiblyAFunctionType] --> [Return] @@ -11461,13 +11641,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 @@ -11481,32 +11662,49 @@ 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) { + SmallVector ResultAC, FDAC; + Result->getAssociatedConstraints(ResultAC); + FD->getAssociatedConstraints(FDAC); + bool ResultMoreConstrained = IsMoreConstrained(Result, ResultAC, FD, + FDAC); + bool FDMoreConstrained = IsMoreConstrained(FD, FDAC, Result, ResultAC); + 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; } /// Given an overloaded function, tries to turn it into a non-overloaded -/// function reference using resolveAddressOfOnlyViableOverloadCandidate. This +/// 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 || Found->isCPUDispatchMultiVersion() || Found->isCPUSpecificMultiVersion()) return false; Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -3610,6 +3610,11 @@ } S.Diag(Template->getLocation(), diag::note_template_decl_here); + SmallVector PartialAC, TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + Partial->getAssociatedConstraints(PartialAC); + S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Partial, PartialAC, Template, + TemplateAC); } static void Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -3356,11 +3356,6 @@ 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, @@ -3406,6 +3401,23 @@ return TDK_SubstitutionFailure; } + // C++2a [temp.deduct]p5 + // [...] When all template arguments have been deduced [...] all uses of + // template parameters [...] are replaced with the corresponding deduced + // or default argument values. + // [...] If the function template has associated constraints + // ([temp.constr.decl]), those constraints are checked for satisfaction + // ([temp.constr.constr]). If the constraints are not satisfied, type + // deduction fails. + if (CheckInstantiatedFunctionTemplateConstraints(Info.getLocation(), + Specialization, Builder, Info.AssociatedConstraintsSatisfaction)) + return TDK_MiscellaneousDeductionFailure; + + if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(TemplateArgumentList::CreateCopy(Context, Builder)); + return TDK_ConstraintsNotSatisfied; + } + if (OriginalCallArgs) { // C++ [temp.deduct.call]p4: // In general, the deduction process attempts to find template argument @@ -3526,7 +3538,7 @@ DeclAccessPair DAP; if (FunctionDecl *Viable = - S.resolveAddressOfOnlyViableOverloadCandidate(Arg, DAP)) + S.resolveAddressOfSingleOverloadCandidate(Arg, DAP)) return GetTypeOfFunction(S, R, Viable); return {}; Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -146,7 +146,7 @@ break; // If this function is a generic lambda specialization, we are done. - if (isGenericLambdaCallOperatorSpecialization(Function)) + if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; } else if (FunctionTemplateDecl *FunTmpl Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1704,6 +1704,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. @@ -1763,6 +1774,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 @@ -2010,6 +2024,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) { @@ -2039,24 +2064,29 @@ StartLoc, NameInfo, T, TInfo, Constructor->isExplicit(), Constructor->isInlineSpecified(), - false, Constructor->isConstexpr()); + false, Constructor->isConstexpr(), + InheritedConstructor(), + TrailingRequiresClause); Method->setRangeEnd(Constructor->getEndLoc()); } 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->getEndLoc()); } else if (CXXConversionDecl *Conversion = dyn_cast(D)) { Method = CXXConversionDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, Conversion->isInlineSpecified(), Conversion->isExplicit(), - Conversion->isConstexpr(), Conversion->getEndLoc()); + Conversion->isConstexpr(), Conversion->getEndLoc(), + 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->getEndLoc()); + Method = CXXMethodDecl::Create(SemaRef.Context, Record, + StartLoc, NameInfo, T, TInfo, + SC, D->isInlineSpecified(), + D->isConstexpr(), D->getEndLoc(), + TrailingRequiresClause); } if (D->isInlined()) @@ -3917,6 +3947,36 @@ TemplateArgs); } +bool Sema::CheckInstantiatedFunctionTemplateConstraints( + SourceLocation PointOfInstantiation, FunctionDecl *Decl, + ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction) { + // Enter the scope of this instantiation. We don't use + // PushDeclContext because we don't have a scope. + Sema::ContextRAII savedContext(*this, Decl); + LocalInstantiationScope Scope(*this); + + MultiLevelTemplateArgumentList MLTAL = + getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true); + + // If this is not an explicit specialization - we need to get the instantiated + // version of the template arguments and add them to scope for the + // substitution. + if (Decl->isTemplateInstantiation()) + if (addInstantiatedParametersToScope(*this, Decl, + Decl->getTemplateInstantiationPattern(), + Scope, MLTAL)) + return true; + + FunctionTemplateDecl *Template = Decl->getPrimaryTemplate(); + // Note - code synthesis context for the constraints check is created + // inside CheckConstraintsSatisfaction. + SmallVector TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs, + PointOfInstantiation, Satisfaction); +} + /// Initializes the common fields of an instantiation function /// declaration (New) from the corresponding fields of its template (Tmpl). /// Index: lib/Sema/SemaTemplateVariadic.cpp =================================================================== --- lib/Sema/SemaTemplateVariadic.cpp +++ lib/Sema/SemaTemplateVariadic.cpp @@ -918,6 +918,10 @@ } } + if (Expr *TRC = D.getTrailingRequiresClause()) + if (TRC->containsUnexpandedParameterPack()) + return true; + return false; } Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -824,6 +824,8 @@ if (Record.readInt()) { // hasExtInfo auto *Info = new (Reader.getContext()) DeclaratorDecl::ExtInfo(); ReadQualifierInfo(*Info); + if (Record.readInt()) // has TrailingRequiresClause + Info->TrailingRequiresClause = cast(Record.readStmt()); DD->DeclInfo = Info; } QualType TSIType = Record.readType(); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -518,8 +518,13 @@ VisitValueDecl(D); Record.AddSourceLocation(D->getInnerLocStart()); Record.push_back(D->hasExtInfo()); - if (D->hasExtInfo()) - Record.AddQualifierInfo(*D->getExtInfo()); + if (D->hasExtInfo()) { + DeclaratorDecl::ExtInfo *Info = D->getExtInfo(); + Record.AddQualifierInfo(*Info); + Record.push_back(Info->TrailingRequiresClause ? 1 : 0); + if (Info->TrailingRequiresClause) + Record.AddStmt(Info->TrailingRequiresClause); + } // The location information is deferred until the end of the record. Record.AddTypeRef(D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() : QualType()); 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,21 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template +class A { + virtual void f1() requires (sizeof(T) == 0); + // expected-error@-1{{virtual function cannot have a requires clause}} + virtual void f2() requires (sizeof(T) == 1); + // expected-error@-1{{virtual function cannot have a requires clause}} +}; + +template +class B : A { + virtual void f1() requires (sizeof(T) == 0) override {} + // expected-error@-1{{virtual function cannot have a requires clause}} +}; + +template struct C : T {void f() requires true; }; +// expected-error@-1{{virtual function cannot have a requires clause}} +struct D { virtual void f(); }; +template struct C; +// expected-note@-1{{in instantiation of template class 'C' requested here}} \ 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,27 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +void f1(int a) requires true; // OK +auto f2(int a) -> bool requires true; // OK +auto f3(int a) -> bool (*)(int b) requires true; // OK +auto f4(int a) requires true -> bool; // expected-error{{trailing return type must come before trailing requires clause}} +int f5(int a) requires; // expected-error{{expected expression}} +int f6(int a) requires {} // expected-error{{expected expression}} +void (f7()) requires true; +void (f8() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}} +void (*(f9 requires true[10]))(); // expected-error{{trailing requires clause should be placed outside parentheses}} +static_assert(is_same_v); +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 should be placed outside parentheses}} +using T = void (); +T x requires true; +struct S { + T m1 requires true, m2 requires true; +}; 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,18 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template requires (sizeof(T) >= 4 && sizeof(T) <= 10) +// expected-note@-1{{because 'sizeof(char [20]) <= 10' (20 <= 10) evaluated to false}} +// expected-note@-2{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}} +void foo() requires (sizeof(T) <= 8) {} +// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = char]}} +// expected-note@-2{{candidate template ignored: constraints not satisfied [with T = char [9]]}} +// expected-note@-3{{candidate template ignored: constraints not satisfied [with T = char [20]]}} +// expected-note@-4{{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,58 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +namespace functions +{ + void foo(int) requires false {} + // expected-note@-1 3{{because 'false' evaluated to false}} + // expected-note@-2 {{candidate function not viable: constraints not satisfied}} + void bar(int) requires true {} + + void a(int); + void a(double); + + void baz() { + foo(1); // expected-error{{no matching function for call to 'foo'}} + 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,115 @@ +// 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'; + } + + static_assert(is_same_v); // expected-error {{call to 'foo' is ambiguous}} + static_assert(is_same_v); + + template + void bar() requires (sizeof(T) == 1) { } + // expected-note@-1{{and here}} + // expected-note@-2{{candidate function [with T = char]}} + + template + void bar() requires (sizeof(T) == 1 && sizeof(T) >= 0) { } + // expected-note@-1{{candidate function [with T = char]}} + // expected-note@-2{{'sizeof(char) == 1' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} + + static_assert(is_same_v()), void>); + // expected-error@-1{{call to 'bar' is ambiguous}} + + template + constexpr int baz() requires AtLeast1 { // expected-note {{candidate function}} + return 1; + } + + template requires AtLeast1 + constexpr int baz() { // expected-note {{candidate function [with T = int]}} + return 2; + } + + static_assert(baz() == 1); // expected-error {{call to 'baz' is ambiguous}} +} + +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() requires (sizeof(long) >= 8) { } + // expected-note@-1 {{candidate function}} + // expected-note@-2 {{and here}} + + void bar() requires (sizeof(long) >= 8 && sizeof(int) <= 30) { } + // expected-note@-1 {{candidate function}} + // expected-note@-2 {{'sizeof(long) >= 8' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} + + static_assert(is_same_v); + static_assert(is_same_v); // expected-error {{call to 'baz' is ambiguous}} + static_assert(is_same_v); // expected-error {{call to 'bar' is ambiguous}} + + constexpr int goo(int a) requires AtLeast2 && true { + return 1; + } + + constexpr int goo(const int b) requires AtLeast2 { + return 2; + } + + // Only trailing requires clauses of redeclarations are compared for overload resolution. + constexpr int doo(int a, ...) requires AtLeast2 && true { // expected-note {{candidate function}} + return 1; + } + + constexpr int doo(int b) requires AtLeast2 { // expected-note {{candidate function}} + return 2; + } + + static_assert(goo(1) == 1); + static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}} +} + Index: test/CXX/concepts-ts/over/over.match/over.match.viable/p3.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/over/over.match/over.match.viable/p3.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +struct S2 {}; +// expected-note@-1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1' to 'const S2' for 1st argument}} +// expected-note@-2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1' to 'S2' for 1st argument}} +// expected-note@-3 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + +struct S1 { + void foo() const requires true {} + void foo() const requires false {} + void bar() const requires false {} + // expected-note@-1 {{because 'false' evaluated to false}} + operator bool() const requires true { return true; } + explicit operator bool() const requires false; + explicit operator S2() const requires false; + // expected-note@-1 {{candidate function not viable: constraints not satisfied}} + // expected-note@-2 {{because 'false' evaluated to false}} +}; + +void foo() { + S1().foo(); + S1().bar(); + // expected-error@-1 {{invalid reference to function 'bar': constraints not satisfied}} + (void) static_cast(S1()); + (void) static_cast(S1()); + // expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}} +} + +// Test that constraints are checked before implicit conversions are formed. + +template +struct invalid_template { using X = typename T::non_existant; }; +struct A { + template::aadasas> + operator T() {} +}; + +void foo(int) requires false; +void foo(A) requires true; + +struct S { + void foo(int) requires false; + void foo(A) requires true; + S(A) requires false; + S(double) requires true; + ~S() requires false; + // expected-note@-1 2{{because 'false' evaluated to false}} + ~S() requires true; + operator int() requires true; + operator int() requires false; +}; + +void bar() { + foo(A{}); + S{1.}.foo(A{}); + // expected-error@-1{{invalid reference to function '~S': constraints not satisfied}} + // Note - this behavior w.r.t. constrained dtors is a consequence of current + // wording, which does not invoke overload resolution when a dtor is called. + // P0848 is set to address this issue. + S s = 1; + // expected-error@-1{{invalid reference to function '~S': constraints not satisfied}} + int a = s; +} \ No newline at end of file Index: test/CXX/concepts-ts/over/over.over/p4.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/over/over.over/p4.cpp @@ -0,0 +1,62 @@ +// 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@-1{{possible target for call}} +// expected-note@-2{{candidate function}} + return 0.0; +} + +int bar() requires AtMost8 && AtLeast2 { +// expected-note@-1{{possible target for call}} +// expected-note@-2{{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@-1{{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}} + // expected-error@-2{{call to 'bar' is ambiguous}} + static_assert(is_same_v); +} \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s - +#if 0 template constexpr bool is_ptr_v = false; @@ -23,14 +23,14 @@ 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')}} +#endif +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 +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]}} Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -1,6 +1,6 @@ // 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}} +#if 0 +template requires (sizeof(T) >= 2) // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}} struct A { static constexpr int value = sizeof(T); }; @@ -9,21 +9,21 @@ 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}} + 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]}} - +#endif 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 = ]}} - +#if 0 template concept IsBig = sizeof(T) > 100; // expected-note{{because 'sizeof(int) > 100' (4 > 100) evaluated to false}} @@ -44,16 +44,16 @@ template struct AA { - template requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}} + 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}} + 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}} + 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; } @@ -85,8 +85,9 @@ // 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')}} +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 +static_assert(D{}); // expected-note{{while checking constraint satisfaction for template 'D' required here}} +#endif \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp @@ -2,10 +2,10 @@ namespace class_templates { - template requires sizeof(T) >= 4 // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}} + 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 + template requires (sizeof(T*) >= 4 && sizeof(T) >= 4) struct is_same { static constexpr bool value = true; }; static_assert(!is_same::value); @@ -23,7 +23,7 @@ // 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')}} + 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}} @@ -35,10 +35,10 @@ namespace variable_templates { - template requires sizeof(T) >= 4 + template requires (sizeof(T) >= 4) constexpr bool is_same_v = false; - template requires sizeof(T*) >= 4 && sizeof(T) >= 4 + template requires (sizeof(T*) >= 4 && sizeof(T) >= 4) constexpr bool is_same_v = true; static_assert(!is_same_v); @@ -55,7 +55,7 @@ // 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')}} + 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}} Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp @@ -2,9 +2,9 @@ namespace nodiag { -template requires bool(T()) +template requires (bool(T())) struct A; -template requires bool(U()) +template requires (bool(U())) struct A; } // end namespace nodiag @@ -21,7 +21,7 @@ template requires true // expected-note{{previous template declaration is here}} struct C; -template requires !0 // expected-error{{requires clause differs in template redeclaration}} +template requires (!0) // expected-error{{requires clause differs in template redeclaration}} struct C; } // end namespace diag @@ -29,15 +29,15 @@ namespace nodiag { struct AA { - template requires someFunc(T()) + template requires (someFunc(T())) struct A; }; -template requires someFunc(U()) +template requires (someFunc(U())) struct AA::A { }; struct AAF { - template requires someFunc(T()) + template requires (someFunc(T())) friend struct AA::A; }; Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp @@ -2,9 +2,9 @@ namespace nodiag { -template requires bool(T()) +template requires (bool(T())) int A(); -template requires bool(U()) +template requires (bool(U())) int A(); } // end namespace nodiag @@ -26,7 +26,7 @@ template requires true int orig::B(); // expected-error@-1{{out-of-line declaration of 'B' does not match any declaration in namespace 'diag::orig'}} -template requires !0 +template requires (!0) int orig::C(); // expected-error@-1{{out-of-line declaration of 'C' does not match any declaration in namespace 'diag::orig'}} @@ -35,11 +35,11 @@ namespace nodiag { struct AA { - template requires someFunc(T()) + template requires (someFunc(T())) int A(); }; -template requires someFunc(T()) +template requires (someFunc(T())) int AA::A() { return sizeof(T); } } // end namespace nodiag Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/var-template-decl.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/var-template-decl.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/var-template-decl.cpp @@ -3,11 +3,11 @@ namespace nodiag { struct B { - template requires bool(T()) + template requires (bool(T())) static int A; }; -template requires bool(U()) +template requires (bool(U())) int B::A = int(U()); } // end namespace nodiag @@ -15,11 +15,11 @@ namespace diag { struct B { - template requires bool(T()) // expected-note{{previous template declaration is here}} + template requires (bool(T())) // expected-note{{previous template declaration is here}} static int A; }; -template requires !bool(U()) // expected-error{{requires clause differs in template redeclaration}} +template requires (!bool(U())) // expected-error{{requires clause differs in template redeclaration}} int B::A = int(U()); } // end namespace diag \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp @@ -8,9 +8,11 @@ // they originate from different source-level constructs, they do not subsume // each other. template requires C + // expected-note@-1{{'C' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} struct A {}; // expected-note{{template is declared here}} template requires C && true + // expected-note@-1{{and here}} struct A {}; // expected-error{{class template partial specialization is not more specialized than the primary template}} } Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -1,9 +1,11 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s -template requires sizeof(T) >= 4 +template requires (sizeof(T) >= 4) +// expected-note@-1{{'sizeof(T) >= 4' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} class A{}; // expected-note{{template is declared here}} -template requires sizeof(T) >= 4 && sizeof(T) <= 10 +template requires (sizeof(T) >= 4 && sizeof(T) <= 10) +// expected-note@-1{{and here}} class A{}; // expected-error{{class template partial specialization is not more specialized than the primary template}} template @@ -12,7 +14,7 @@ template requires C1 class B{}; -template requires C1 && sizeof(T) <= 10 +template requires (C1 && sizeof(T) <= 10) class B{}; template @@ -69,9 +71,11 @@ // because they originate in two different locations in code. template requires C3 +// expected-note@-1{{'C3' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} struct H { }; // expected-note {{template is declared here}} template requires C3 && C4 +// expected-note@-1{{and here}} 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 Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s -template requires sizeof(T) >= 4 +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 +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}} @@ -14,7 +14,7 @@ template requires C1 constexpr bool b() { return false; } -template requires C1 && sizeof(T) <= 10 +template requires (C1 && sizeof(T) <= 10) constexpr bool b() { return true; } static_assert(b()); Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp @@ -1,9 +1,11 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s -template requires sizeof(T) >= 4 +template requires (sizeof(T) >= 4) +// expected-note@-1{{'sizeof(T) >= 4' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} bool a = false; // expected-note{{template is declared here}} -template requires sizeof(T) >= 4 && sizeof(T) <= 10 +template requires (sizeof(T) >= 4 && sizeof(T) <= 10) +// expected-note@-1{{and here}} bool a = true; // expected-error{{variable template partial specialization is not more specialized than the primary template}} template @@ -12,7 +14,7 @@ template requires C1 bool b = false; -template requires C1 && sizeof(T) <= 10 +template requires (C1 && sizeof(T) <= 10) bool b = true; template Index: test/CXX/concepts-ts/temp/temp.explicit/p8.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.explicit/p8.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template requires (sizeof(T) + sizeof(S) < 10) +// expected-note@-1{{because 'sizeof(char [100]) + sizeof(char) < 10' (101 < 10) evaluated to false}} +void f(T t, S s) requires (sizeof(t) == 1 && sizeof(s) == 1) { }; +// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = int, S = char]}} +// expected-note@-2{{because 'sizeof (t) == 1' (4 == 1) evaluated to false}} +// expected-note@-3{{candidate template ignored: constraints not satisfied [with T = char, S = short]}} +// expected-note@-4{{because 'sizeof (s) == 1' (2 == 1) evaluated to false}} +// expected-note@-5{{candidate template ignored: constraints not satisfied [with T = char [100], S = char]}} + +template<> +void f(int t, char s) { }; +// expected-error@-1{{no function template matches function template specialization 'f'}} + +template<> +void f(char t, short s) { }; +// expected-error@-1{{no function template matches function template specialization 'f'}} + +template<> +void f(char t[100], char s) { }; +// expected-error@-1{{no function template matches function template specialization 'f'}} \ No newline at end of file Index: test/Parser/cxx2a-concepts-ambig-constraint-expr.cpp =================================================================== --- test/Parser/cxx2a-concepts-ambig-constraint-expr.cpp +++ test/Parser/cxx2a-concepts-ambig-constraint-expr.cpp @@ -5,25 +5,5 @@ // the syntax is consumed without backtracking. // type-specifier-seq in conversion-type-id -template requires (bool)&T::operator short -unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}} - -// type-specifier-seq in new-type-id -template requires (bool)sizeof new (T::f()) short -unsigned int bar(); // expected-error {{C++ requires a type specifier for all declarations}} - -template requires (bool)sizeof new (T::f()) unsigned // expected-error {{'struct' cannot be signed or unsigned}} -struct X { }; // expected-error {{'X' cannot be defined in a type specifier}} - -// C-style cast -// of function call on function-style cast -template requires (bool(T())) -T (*fp)(); // expected-error {{use of undeclared identifier 'fp'}} - -// function-style cast -// as the callee in a function call -struct A { - static int t; - template requires bool(T()) - (A(T (&t))) { } // expected-error {{called object type 'bool' is not a function or function pointer}} -}; +template requires T::operator short +unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}} \ No newline at end of file Index: test/Parser/cxx2a-concepts-requires-clause.cpp =================================================================== --- test/Parser/cxx2a-concepts-requires-clause.cpp +++ test/Parser/cxx2a-concepts-requires-clause.cpp @@ -1,13 +1,11 @@ // RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ %s -verify -// expected-no-diagnostics // Test parsing of the optional requires-clause in a template-declaration. template requires true void foo() { } - -template requires !0 +template requires (!0) struct A { void foo(); struct AA; @@ -27,31 +25,30 @@ using MQ = M; }; -template requires !0 +template requires (!0) void A::foo() { } -template requires !0 +template requires (!0) struct A::AA { }; -template requires !0 +template requires (!0) enum A::E : int { E0 }; -template requires !0 +template requires (!0) int A::x = 0; -template requires !0 +template requires (!0) template requires true void A::Mfoo() { } -template requires !0 +template requires (!0) template requires true struct A::M { }; -template requires !0 +template requires (!0) template requires true int A::Mx = 0; - template requires true int x = 0; @@ -80,3 +77,76 @@ template requires true int C::Mx = 0; + +// Test behavior with non-primary-expression requires clauses + +template requires foo() +// expected-error@-1{{function call must be parenthesized to be considered part of the requires clause}} +struct B1 { }; + +int func() { } + +template requires func() +// expected-error@-1{{atomic constraint must be of type 'bool' (found '')}} +// expected-note@-2{{function call must be parenthesized to be considered part of the requires clause}} +struct B2 { }; + +template requires (foo()) +struct B3 { }; + +template requires T{} +// expected-error@-1{{expected primary expression before type name; did you forget parentheses?}} +struct B4 { }; + +template requires sizeof(T) == 0 +// expected-error@-1{{expected primary expression before sizeof; did you forget parentheses?}} +struct B5 { }; + +template requires (sizeof(T)) == 0 +// expected-error@-1{{atomic constraint must be of type 'bool' (found 'unsigned long')}} +// expected-note@-2{{'==' is not considered part of the requires clause (use parentheses to include it)}} +struct B6 { }; + +template requires 0 +// expected-error@-1{{atomic constraint must be of type 'bool' (found 'int')}} +(int) bar() { }; + +template requires foo +(int) bar() { }; +// expected-error@-1{{function call must be parenthesized to be considered part of the requires clause}} + +template +void bar() requires foo(); +// expected-error@-1{{function call must be parenthesized to be considered part of the requires clause}} + +template +void bar() requires (foo()); + +template +void bar() requires func(); +// expected-error@-1{{atomic constraint must be of type 'bool' (found '')}} +// expected-note@-2{{function call must be parenthesized to be considered part of the requires clause}} + +template +void bar() requires T{}; +// expected-error@-1{{expected primary expression before type name; did you forget parentheses?}} + +template +void bar() requires sizeof(T) == 0; +// expected-error@-1{{expected primary expression before sizeof; did you forget parentheses?}} + +template +void bar() requires (sizeof(T)) == 0; +// expected-error@-1{{atomic constraint must be of type 'bool' (found 'unsigned long')}} +// expected-note@-2{{'==' is not considered part of the requires clause (use parentheses to include it)}} + +void bar(int x, int y) requires (x, y, true); + +struct B { + int x; + void foo(int y) requires (x, this, this->x, y, true); + static void bar(int y) requires (x, true); + // expected-error@-1{{'this' cannot be implicitly used in a static member function declaration}} + static void baz(int y) requires (this, true); + // expected-error@-1{{'this' cannot be used in a static member function declaration}} +}; \ No newline at end of file Index: test/SemaTemplate/instantiate-requires-clause.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/instantiate-requires-clause.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template requires ((sizeof(Args) == 1), ...) +// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}} +void f1(Args&&... args) { } +// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = ]}} + +using f11 = decltype(f1('a')); +using f12 = decltype(f1(1, 'b')); +using f13 = decltype(f1(1, 'b', 2)); +// expected-error@-1 {{no matching function for call to 'f1'}} + +template +void f2(Args&&... args) requires ((sizeof(args) == 1), ...) { } +// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = ]}} +// expected-note@-2 {{because '(sizeof (args) == 1) , (sizeof (args) == 1) , (sizeof (args) == 1)' evaluated to false}} + +using f21 = decltype(f2('a')); +using f22 = decltype(f2(1, 'b')); +using f23 = decltype(f2(1, 'b', 2)); +// expected-error@-1 {{no matching function for call to 'f2'}} + +template requires ((sizeof(Args) == 1), ...) +// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}} +void f3(Args&&... args) requires ((sizeof(args) == 1), ...) { } +// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = ]}} + +using f31 = decltype(f3('a')); +using f32 = decltype(f3(1, 'b')); +using f33 = decltype(f3(1, 'b', 2)); +// expected-error@-1 {{no matching function for call to 'f3'}}