diff --git a/clang/include/clang/AST/ASTLambda.h b/clang/include/clang/AST/ASTLambda.h --- a/clang/include/clang/AST/ASTLambda.h +++ b/clang/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 diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -384,6 +384,9 @@ for (const auto *Parameter : D->parameters()) Visit(Parameter); + if (const Expr *TRC = D->getTrailingRequiresClause()) + Visit(TRC); + if (const auto *C = dyn_cast(D)) for (const auto *I : C->inits()) Visit(I); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -669,10 +669,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; @@ -739,6 +741,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; } @@ -1903,7 +1920,8 @@ FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, bool isInlineSpecified, - ConstexprSpecKind ConstexprKind); + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause = nullptr); using redeclarable_base = Redeclarable; @@ -1938,11 +1956,12 @@ SourceLocation NLoc, DeclarationName N, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInlineSpecified = false, bool hasWrittenPrototype = true, - ConstexprSpecKind ConstexprKind = CSK_unspecified) { + ConstexprSpecKind ConstexprKind = CSK_unspecified, + Expr *TrailingRequiresClause = nullptr) { DeclarationNameInfo NameInfo(N, NLoc); return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC, isInlineSpecified, hasWrittenPrototype, - ConstexprKind); + ConstexprKind, TrailingRequiresClause); } static FunctionDecl *Create(ASTContext &C, DeclContext *DC, @@ -1950,7 +1969,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, - ConstexprSpecKind ConstexprKind); + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause); static FunctionDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2352,6 +2372,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; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1905,9 +1905,10 @@ SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInline, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation) + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr) : FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, SC, isInline, - ConstexprKind) { + ConstexprKind, TrailingRequiresClause) { if (EndLocation.isValid()) setRangeEnd(EndLocation); } @@ -1918,7 +1919,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInline, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation); + SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr); static CXXMethodDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2363,7 +2365,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - InheritedConstructor Inherited); + InheritedConstructor Inherited, + Expr *TrailingRequiresClause); void anchor() override; @@ -2416,7 +2419,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - InheritedConstructor Inherited = InheritedConstructor()); + InheritedConstructor Inherited = InheritedConstructor(), + Expr *TrailingRequiresClause = nullptr); ExplicitSpecifier getExplicitSpecifier() { return getCanonicalDecl()->getExplicitSpecifierInternal(); @@ -2623,9 +2627,11 @@ CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, - bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) + bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause = nullptr) : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, ConstexprKind, SourceLocation()) { + SC_None, isInline, ConstexprKind, SourceLocation(), + TrailingRequiresClause) { setImplicit(isImplicitlyDeclared); } @@ -2637,7 +2643,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, bool isImplicitlyDeclared, - ConstexprSpecKind ConstexprKind); + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause = nullptr); static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID); void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg); @@ -2676,9 +2683,11 @@ CXXConversionDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES, - ConstexprSpecKind ConstexprKind, SourceLocation EndLocation) + ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, + Expr *TrailingRequiresClause = nullptr) : CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, ConstexprKind, EndLocation), + SC_None, isInline, ConstexprKind, EndLocation, + TrailingRequiresClause), ExplicitSpec(ES) {} void anchor() override; @@ -2694,7 +2703,7 @@ Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation); + SourceLocation EndLocation, Expr *TrailingRequiresClause = nullptr); static CXXConversionDecl *CreateDeserialized(ASTContext &C, unsigned ID); ExplicitSpecifier getExplicitSpecifier() { diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2030,6 +2030,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()) { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -181,6 +181,13 @@ 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_unparenthesized_non_primary_expr_in_requires_clause : Error< + "parentheses are required around this expression in a requires clause">; +def note_unparenthesized_non_primary_expr_in_requires_clause : Note< + "parentheses are required around this expression in a 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">; @@ -309,6 +316,12 @@ def warn_cxx98_compat_trailing_return_type : Warning< "trailing return types are incompatible with C++98">, InGroup, DefaultIgnore; +def err_requires_clause_must_appear_after_trailing_return : Error< + "trailing return type must appear 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>; @@ -880,7 +893,7 @@ InGroup, DefaultIgnore; def err_lambda_missing_parens : Error< "lambda requires '()' before %select{'mutable'|return type|" - "attribute specifier|'constexpr'|'consteval'}0">; + "attribute specifier|'constexpr'|'consteval'|'requires' clause}0">; def err_lambda_decl_specifier_repeated : Error< "%select{'mutable'|'constexpr'|'consteval'}0 cannot appear multiple times in a lambda declarator">; def err_lambda_capture_misplaced_ellipsis : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2596,6 +2596,18 @@ "%select{and |because }0%1 does not satisfy %2">; def note_atomic_constraint_evaluated_to_false_elaborated : Note< "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">; +def err_constrained_virtual_method : Error< + "virtual function cannot have a requires clause">; +def err_trailing_requires_clause_on_deduction_guide : Error< + "deduction guide 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< + "similar constraint expressions not considered equivalent; constraint " + "expressions cannot be considered equivalent unless they originate from the " + "same concept">; +def note_ambiguous_atomic_constraints_similar_expression : Note< + "similar constraint expression here">; def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; @@ -3920,6 +3932,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< @@ -4033,6 +4048,9 @@ "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 %sub{select_ovl_candidate_kind}0,1,2 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 " diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1679,6 +1679,9 @@ ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast); ExprResult ParseCaseExpression(SourceLocation CaseLoc); ExprResult ParseConstraintExpression(); + ExprResult + ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause); + ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause); // Expr that doesn't include commas. ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast); @@ -1693,15 +1696,23 @@ ExprResult ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec); - ExprResult ParseCastExpression(bool isUnaryExpression, + /// Control what ParseCastExpression will parse. + enum CastParseKind { + AnyCastExpr = 0, + UnaryExprOnly, + PrimaryExprOnly + }; + ExprResult ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, bool &NotCastExpr, TypeCastState isTypeCast, - bool isVectorLiteral = false); - ExprResult ParseCastExpression(bool isUnaryExpression, + bool isVectorLiteral = false, + bool *NotPrimaryExpression = nullptr); + ExprResult ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand = false, TypeCastState isTypeCast = NotTypeCast, - bool isVectorLiteral = false); + bool isVectorLiteral = false, + bool *NotPrimaryExpression = nullptr); /// Returns true if the next token cannot start an expression. bool isNotExpressionStart(); @@ -1910,6 +1921,11 @@ ExprResult ParseCoyieldExpression(); //===--------------------------------------------------------------------===// + // C++ Concepts + + void ParseTrailingRequiresClause(Declarator &D); + + //===--------------------------------------------------------------------===// // C99 6.7.8: Initialization. /// ParseInitializer @@ -2730,6 +2746,9 @@ 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(); @@ -2837,10 +2856,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( diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1826,6 +1826,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 @@ -1855,7 +1859,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(); @@ -2401,6 +2406,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. /// diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -754,7 +754,11 @@ /// This constructor/conversion candidate fail due to an address space /// mismatch between the object being constructed and the overload /// candidate. - ovl_fail_object_addrspace_mismatch + ovl_fail_object_addrspace_mismatch, + + /// 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 diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -47,6 +47,7 @@ #include "clang/Sema/ObjCMethodList.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" @@ -2300,6 +2301,8 @@ SkipBodyInfo *SkipBody = nullptr); Decl *ActOnStartOfFunctionDef(Scope *S, Decl *D, SkipBodyInfo *SkipBody = nullptr); + void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D); + ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr); void ActOnStartOfObjCMethodDef(Scope *S, Decl *D); bool isObjCMethodDecl(Decl *D) { return D && isa(D); @@ -2884,7 +2887,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, @@ -3249,10 +3253,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 * @@ -6035,7 +6038,8 @@ TypeSourceInfo *MethodType, SourceLocation EndLoc, ArrayRef Params, - ConstexprSpecKind ConstexprKind); + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause); /// Number lambda for linkage purposes if necessary. void handleLambdaNumbering( @@ -6169,16 +6173,35 @@ 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 + /// PossibleNonPrimary will be set to true if the failure might be due to a + /// non-primary expression being used as an atomic constraint. + bool CheckConstraintExpression(Expr *CE, Token NextToken = Token(), + bool *PossibleNonPrimary = nullptr, + bool IsTrailingRequiresClause = false); + + /// Check whether the given type-dependent expression will be the name of a + /// function or another callable function-like entity (e.g. a function + // template or overload set) for any substitution. + bool IsDependentFunctionNameExpr(Expr *E); private: - /// \brief Caches pairs of template-like decls whose associated constraints - /// were checked for subsumption and whether or not the first's constraints - /// did in fact subsume the second's. + /// Caches pairs of template-like decls whose associated constraints were + /// checked for subsumption and whether or not the first's constraints did in + /// fact subsume the second's. llvm::DenseMap, bool> SubsumptionCache; + /// Caches the normalized associated constraints of declarations (concepts or + /// constrained declarations). If an error occurred while normalizing the + /// associated constraints of the template or concept, nullptr will be cached + /// here. + llvm::DenseMap + NormalizationCache; public: + const NormalizedConstraint * + getNormalizedAssociatedConstraints( + NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); + /// \brief Check whether the given declaration's associated constraints are /// at least as constrained than another declaration's according to the /// partial ordering of constraints. @@ -6191,6 +6214,13 @@ NamedDecl *D2, ArrayRef AC2, bool &Result); + /// 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 @@ -8644,6 +8674,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); diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Sema/SemaConcept.h @@ -0,0 +1,145 @@ +//===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +// This file provides semantic analysis for C++ constraints and concepts. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H +#define LLVM_CLANG_SEMA_SEMACONCEPT_H +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +namespace clang { +class Sema; + +struct AtomicConstraint { + const Expr *ConstraintExpr; + Optional> ParameterMapping; + + AtomicConstraint(Sema &S, const Expr *ConstraintExpr) : + ConstraintExpr(ConstraintExpr) { }; + + bool hasMatchingParameterMapping(ASTContext &C, + const AtomicConstraint &Other) const { + if (!ParameterMapping != !Other.ParameterMapping) + return false; + if (!ParameterMapping) + return true; + if (ParameterMapping->size() != Other.ParameterMapping->size()) + return false; + + for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) + if (!C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) + .structurallyEquals(C.getCanonicalTemplateArgument( + (*Other.ParameterMapping)[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 + // if and only if the A and B are identical [...] + // + // C++ [temp.constr.atomic] p2 + // Two atomic constraints are identical if they are formed from the + // same expression and the targets of the parameter mappings are + // equivalent according to the rules for expressions [...] + + // We do not actually substitute the parameter mappings into the + // constraint expressions, therefore the constraint expressions are + // the originals, and comparing them will suffice. + if (ConstraintExpr != Other.ConstraintExpr) + return false; + + // Check that the parameter lists are identical + return hasMatchingParameterMapping(C, Other); + } +}; + +/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is +/// either an atomic constraint, a conjunction of normalized constraints or a +/// disjunction of normalized constraints. +struct NormalizedConstraint { + friend class Sema; + + enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; + + using CompoundConstraint = llvm::PointerIntPair< + std::pair *, 1, + CompoundConstraintKind>; + + llvm::PointerUnion Constraint; + + NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; + NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, + NormalizedConstraint RHS, CompoundConstraintKind Kind) + : Constraint{CompoundConstraint{ + new (C) std::pair{ + std::move(LHS), std::move(RHS)}, Kind}} { }; + + NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) { + if (Other.isAtomic()) { + Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint()); + } else { + Constraint = CompoundConstraint( + new (C) std::pair{ + NormalizedConstraint(C, Other.getLHS()), + NormalizedConstraint(C, Other.getRHS())}, + Other.getCompoundKind()); + } + } + NormalizedConstraint(NormalizedConstraint &&Other): + Constraint(Other.Constraint) { + Other.Constraint = nullptr; + } + NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete; + NormalizedConstraint &operator=(NormalizedConstraint &&Other) { + if (&Other != this) { + NormalizedConstraint Temp(std::move(Other)); + std::swap(Constraint, Temp.Constraint); + } + return *this; + } + + CompoundConstraintKind getCompoundKind() const { + assert(!isAtomic() && "getCompoundKind called on atomic constraint."); + return Constraint.get().getInt(); + } + + bool isAtomic() const { return Constraint.is(); } + + NormalizedConstraint &getLHS() const { + assert(!isAtomic() && "getLHS called on atomic constraint."); + return Constraint.get().getPointer()->first; + } + + NormalizedConstraint &getRHS() const { + assert(!isAtomic() && "getRHS called on atomic constraint."); + return Constraint.get().getPointer()->second; + } + + AtomicConstraint *getAtomicConstraint() const { + assert(isAtomic() && + "getAtomicConstraint called on non-atomic constraint."); + return Constraint.get(); + } + +private: + static Optional + fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef E); + static Optional + fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E); +}; + +} // clang + +#endif //LLVM_CLANG_SEMA_SEMACONCEPT_H diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3279,10 +3279,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(); @@ -3311,7 +3313,10 @@ ExplicitSpecifier( ExplicitExpr, FromConstructor->getExplicitSpecifier().getKind()), - D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind())) + D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind(), + InheritedConstructor(), // FIXME: Properly import inherited + // constructor info + TrailingRequiresClause)) return ToFunction; } else if (CXXDestructorDecl *FromDtor = dyn_cast(D)) { @@ -3329,7 +3334,7 @@ if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - D->isImplicit(), D->getConstexprKind())) + D->isImplicit(), D->getConstexprKind(), TrailingRequiresClause)) return ToFunction; CXXDestructorDecl *ToDtor = cast(ToFunction); @@ -3349,20 +3354,21 @@ ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), ExplicitSpecifier(ExplicitExpr, FromConversion->getExplicitSpecifier().getKind()), - D->getConstexprKind(), SourceLocation())) + D->getConstexprKind(), 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->getConstexprKind(), - SourceLocation())) + SourceLocation(), TrailingRequiresClause)) return ToFunction; } else { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, NameInfo, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), - D->hasWrittenPrototype(), D->getConstexprKind())) + D->hasWrittenPrototype(), D->getConstexprKind(), + TrailingRequiresClause)) return ToFunction; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1839,21 +1839,25 @@ } // Set qualifier info. getExtInfo()->QualifierLoc = QualifierLoc; - } else { + } else if (hasExtInfo()) { // Here Qualifier == 0, i.e., we are removing the qualifier (if any). - if (hasExtInfo()) { - if (getExtInfo()->NumTemplParamLists == 0) { - // Save type source info pointer. - TypeSourceInfo *savedTInfo = getExtInfo()->TInfo; - // Deallocate the extended decl info. - getASTContext().Deallocate(getExtInfo()); - // Restore savedTInfo into (non-extended) decl info. - DeclInfo = savedTInfo; - } - else - getExtInfo()->QualifierLoc = QualifierLoc; - } + getExtInfo()->QualifierLoc = QualifierLoc; + } +} + +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( @@ -2777,7 +2781,8 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, bool isInlineSpecified, - ConstexprSpecKind ConstexprKind) + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), @@ -2807,6 +2812,8 @@ FunctionDeclBits.IsMultiVersion = false; FunctionDeclBits.IsCopyDeductionCandidate = false; FunctionDeclBits.HasODRHash = false; + if (TrailingRequiresClause) + setTrailingRequiresClause(TrailingRequiresClause); } void FunctionDecl::getNameForDiagnostic( @@ -4683,10 +4690,12 @@ QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, - ConstexprSpecKind ConstexprKind) { + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause) { FunctionDecl *New = new (C, DC) FunctionDecl(Function, C, DC, StartLoc, NameInfo, T, TInfo, - SC, isInlineSpecified, ConstexprKind); + SC, isInlineSpecified, ConstexprKind, + TrailingRequiresClause); New->setHasWrittenPrototype(hasWrittenPrototype); return New; } @@ -4694,7 +4703,7 @@ FunctionDecl *FunctionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) FunctionDecl(Function, C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - SC_None, false, CSK_unspecified); + SC_None, false, CSK_unspecified, nullptr); } BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2041,16 +2041,19 @@ QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool isInline, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation) { + SourceLocation EndLocation, + Expr *TrailingRequiresClause) { return new (C, RD) CXXMethodDecl(CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, - isInline, ConstexprKind, EndLocation); + isInline, ConstexprKind, EndLocation, + TrailingRequiresClause); } CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXMethodDecl( CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, SC_None, false, CSK_unspecified, SourceLocation()); + QualType(), nullptr, SC_None, false, CSK_unspecified, SourceLocation(), + nullptr); } CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, @@ -2431,9 +2434,11 @@ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, - ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited) + ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited, + Expr *TrailingRequiresClause) : CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, ConstexprKind, SourceLocation()) { + SC_None, isInline, ConstexprKind, SourceLocation(), + TrailingRequiresClause) { setNumCtorInitializers(0); setInheritingConstructor(static_cast(Inherited)); setImplicit(isImplicitlyDeclared); @@ -2457,7 +2462,7 @@ auto *Result = new (C, ID, Extra) CXXConstructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, ExplicitSpecifier(), false, false, - CSK_unspecified, InheritedConstructor()); + CSK_unspecified, InheritedConstructor(), nullptr); Result->setInheritingConstructor(isInheritingConstructor); Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier = hasTraillingExplicit; @@ -2469,7 +2474,8 @@ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, - ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited) { + ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited, + Expr *TrailingRequiresClause) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName && "Name must refer to a constructor"); @@ -2478,7 +2484,8 @@ Inherited ? 1 : 0, ES.getExpr() ? 1 : 0); return new (C, RD, Extra) CXXConstructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, ES, isInline, - isImplicitlyDeclared, ConstexprKind, Inherited); + isImplicitlyDeclared, ConstexprKind, Inherited, + TrailingRequiresClause); } CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const { @@ -2599,19 +2606,22 @@ CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, false, false, CSK_unspecified); + QualType(), nullptr, false, false, CSK_unspecified, + nullptr); } CXXDestructorDecl *CXXDestructorDecl::Create( ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) { + bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, + 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, ConstexprKind); + isImplicitlyDeclared, ConstexprKind, + TrailingRequiresClause); } void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) { @@ -2630,20 +2640,20 @@ CXXConversionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXConversionDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - false, ExplicitSpecifier(), CSK_unspecified, SourceLocation()); + false, ExplicitSpecifier(), CSK_unspecified, SourceLocation(), nullptr); } CXXConversionDecl *CXXConversionDecl::Create( ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation) { + 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, ES, - ConstexprKind, EndLocation); + ConstexprKind, EndLocation, TrailingRequiresClause); } bool CXXConversionDecl::isLambdaToBlockPointerConversion() const { diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -740,6 +740,11 @@ Proto.clear(); } Out << Proto; + + if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) { + Out << " requires "; + TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation); + } } else { Ty.print(Out, Policy, Proto); } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/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; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2014,6 +2014,9 @@ return nullptr; } + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); + // Save late-parsed attributes for now; they need to be parsed in the // appropriate function scope after the function Decl has been constructed. // These will be parsed in ParseFunctionDefinition or ParseLexedAttrList. @@ -2165,6 +2168,12 @@ ParseDeclarator(D); if (!D.isInvalidType()) { + // C++2a [dcl.decl]p1 + // init-declarator: + // declarator initializer[opt] + // declarator requires-clause + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); Decl *ThisDecl = ParseDeclarationAfterDeclarator(D); D.complete(ThisDecl); if (ThisDecl) @@ -6032,6 +6041,22 @@ PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); + } else if (Tok.is(tok::kw_requires) && 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); + ConsumeToken(); + ExprResult TrailingRequiresClause = Actions.CorrectDelayedTyposInExpr( + ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true)); + 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; } @@ -6212,6 +6237,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().hasConstexprSpecifier() && !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. @@ -6225,7 +6291,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 @@ -6320,43 +6387,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().hasConstexprSpecifier() && !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() && @@ -6624,6 +6656,17 @@ // 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(/*IsTrailingRequiresClause=*/true)); + } + // Remember this parsed parameter in ParamInfo. IdentifierInfo *ParmII = ParmDeclarator.getIdentifier(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2301,6 +2301,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)) @@ -2314,6 +2315,8 @@ BitfieldSize = ParseConstantExpression(); if (BitfieldSize.isInvalid()) SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); + } else if (Tok.is(tok::kw_requires)) { + ParseTrailingRequiresClause(DeclaratorInfo); } else { ParseOptionalCXX11VirtSpecifierSeq( VS, getCurrentClass().IsInterface, @@ -2436,6 +2439,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 @@ -2669,6 +2673,7 @@ SmallVector DeclsInGroup; ExprResult BitfieldSize; + ExprResult TrailingRequiresClause; bool ExpectSemi = true; // Parse the first declarator. @@ -3793,6 +3798,62 @@ : DeclaratorContext::TrailingReturnContext); } +/// 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::FunctionDeclarationScope | + Scope::FunctionPrototypeScope); + + Actions.ActOnStartTrailingRequiresClause(getCurScope(), D); + + llvm::Optional ThisScope; + InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope); + + TrailingRequiresClause = + ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true); + + TrailingRequiresClause = + Actions.ActOnFinishTrailingRequiresClause(TrailingRequiresClause); + + if (!D.isDeclarationOfFunction()) { + Diag(RequiresKWLoc, + diag::err_requires_clause_on_declarator_not_declaring_a_function); + return; + } + + if (TrailingRequiresClause.isInvalid()) + SkipUntil({tok::l_brace, tok::arrow, tok::kw_try, tok::comma, tok::colon}, + 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_appear_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. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/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,143 @@ 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(bool IsTrailingRequiresClause) { + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + bool NotPrimaryExpression = false; + auto ParsePrimary = [&] () { + ExprResult E = ParseCastExpression(PrimaryExprOnly, + /*isAddressOfOperand=*/false, + /*isTypeCast=*/NotTypeCast, + /*isVectorLiteral=*/false, + &NotPrimaryExpression); + if (E.isInvalid()) + return ExprError(); + auto RecoverFromNonPrimary = [&] (ExprResult E, bool Note) { + E = ParsePostfixExpressionSuffix(E); + // Use InclusiveOr, the precedence just after '&&' to not parse the + // next arguments to the logical and. + E = ParseRHSOfBinaryExpression(E, prec::InclusiveOr); + if (!E.isInvalid()) + Diag(E.get()->getExprLoc(), + Note + ? diag::note_unparenthesized_non_primary_expr_in_requires_clause + : diag::err_unparenthesized_non_primary_expr_in_requires_clause) + << FixItHint::CreateInsertion(E.get()->getBeginLoc(), "(") + << FixItHint::CreateInsertion( + PP.getLocForEndOfToken(E.get()->getEndLoc()), ")") + << E.get()->getSourceRange(); + return E; + }; + + if (NotPrimaryExpression || + // Check if the following tokens must be a part of a non-primary + // expression + getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, + /*CPlusPlus11=*/true) > prec::LogicalAnd || + // Postfix operators other than '(' (which will be checked for in + // CheckConstraintExpression). + Tok.isOneOf(tok::period, tok::plusplus, tok::minusminus) || + (Tok.is(tok::l_square) && !NextToken().is(tok::l_square))) { + E = RecoverFromNonPrimary(E, /*Note=*/false); + if (E.isInvalid()) + return ExprError(); + NotPrimaryExpression = false; + } + bool PossibleNonPrimary; + bool IsConstraintExpr = + Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary, + IsTrailingRequiresClause); + if (!IsConstraintExpr || PossibleNonPrimary) { + // Atomic constraint might be an unparenthesized non-primary expression + // (such as a binary operator), in which case we might get here (e.g. in + // 'requires 0 + 1 && true' we would now be at '+', and parse and ignore + // the rest of the addition expression). Try to parse the rest of it here. + if (PossibleNonPrimary) + E = RecoverFromNonPrimary(E, /*Note=*/!IsConstraintExpr); + Actions.CorrectDelayedTyposInExpr(E); + return ExprError(); + } + return E; + }; + ExprResult LHS = ParsePrimary(); + if (LHS.isInvalid()) + return ExprError(); + while (Tok.is(tok::ampamp)) { + SourceLocation LogicalAndLoc = ConsumeToken(); + ExprResult RHS = ParsePrimary(); + if (RHS.isInvalid()) { + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + 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(bool IsTrailingRequiresClause) { + ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause)); + if (!LHS.isUsable()) + return ExprError(); + while (Tok.is(tok::pipepipe)) { + SourceLocation LogicalOrLoc = ConsumeToken(); + ExprResult RHS = + ParseConstraintLogicalAndExpression(IsTrailingRequiresClause); + if (!RHS.isUsable()) { + Actions.CorrectDelayedTyposInExpr(LHS); + return ExprError(); + } + 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; + } + return LHS; +} + bool Parser::isNotExpressionStart() { tok::TokenKind K = Tok.getKind(); if (K == tok::l_brace || K == tok::r_brace || @@ -414,7 +545,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,22 +650,24 @@ } } -/// 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(CastParseKind ParseKind, bool isAddressOfOperand, TypeCastState isTypeCast, - bool isVectorLiteral) { + bool isVectorLiteral, + bool *NotPrimaryExpression) { bool NotCastExpr; - ExprResult Res = ParseCastExpression(isUnaryExpression, + ExprResult Res = ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, - isVectorLiteral); + isVectorLiteral, + NotPrimaryExpression); if (NotCastExpr) Diag(Tok, diag::err_expected_expression); return Res; @@ -759,11 +892,12 @@ /// '__is_rvalue_expr' /// \endverbatim /// -ExprResult Parser::ParseCastExpression(bool isUnaryExpression, +ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, bool &NotCastExpr, TypeCastState isTypeCast, - bool isVectorLiteral) { + bool isVectorLiteral, + bool *NotPrimaryExpression) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); auto SavedType = PreferredType; @@ -782,11 +916,21 @@ // ParsePostfixExpressionSuffix. switch (SavedKind) { case tok::l_paren: { - // If this expression is limited to being a unary-expression, the parent can + // If this expression is limited to being a unary-expression, the paren can // not start a cast expression. - ParenParseOption ParenExprType = - (isUnaryExpression && !getLangOpts().CPlusPlus) ? CompoundLiteral - : CastExpr; + ParenParseOption ParenExprType; + switch (ParseKind) { + case CastParseKind::UnaryExprOnly: + if (!getLangOpts().CPlusPlus) + ParenExprType = CompoundLiteral; + LLVM_FALLTHROUGH; + case CastParseKind::AnyCastExpr: + ParenExprType = ParenParseOption::CastExpr; + break; + case CastParseKind::PrimaryExprOnly: + ParenExprType = FoldExpr; + break; + } ParsedType CastTy; SourceLocation RParenLoc; Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/, @@ -861,8 +1005,9 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super)); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); - + return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, + isVectorLiteral, NotPrimaryExpression); + case tok::identifier: { // primary-expression: identifier // unqualified-id: identifier // constant: enumeration-constant @@ -949,8 +1094,9 @@ = RevertibleTypeTraits.find(II); if (Known != RevertibleTypeTraits.end()) { Tok.setKind(Known->second); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ParseKind, isAddressOfOperand, + NotCastExpr, isTypeCast, + isVectorLiteral, NotPrimaryExpression); } } @@ -961,7 +1107,10 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::identifier)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); + return ParseCastExpression(ParseKind, isAddressOfOperand, + NotCastExpr, isTypeCast, + isVectorLiteral, + NotPrimaryExpression); } } @@ -1076,8 +1225,10 @@ Tok.is(tok::r_paren) ? nullptr : &Replacement); if (!Res.isInvalid() && Res.isUnset()) { UnconsumeToken(Replacement); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ParseKind, isAddressOfOperand, + NotCastExpr, isTypeCast, + /*isVectorLiteral=*/false, + NotPrimaryExpression); } if (!Res.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(Res); @@ -1122,12 +1273,16 @@ case tok::kw___builtin_FILE: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_LINE: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseBuiltinPrimaryExpression(); case tok::kw___null: return Actions.ActOnGNUNullExpr(ConsumeToken()); case tok::plusplus: // unary-expression: '++' unary-expression [C99] case tok::minusminus: { // unary-expression: '--' unary-expression [C99] + if (NotPrimaryExpression) + *NotPrimaryExpression = true; // C++ [expr.unary] has: // unary-expression: // ++ cast-expression @@ -1140,7 +1295,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) { @@ -1156,10 +1312,12 @@ return Res; } case tok::amp: { // unary-expression: '&' cast-expression + if (NotPrimaryExpression) + *NotPrimaryExpression = true; // 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; @@ -1172,17 +1330,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 (NotPrimaryExpression) + *NotPrimaryExpression = true; 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 (NotPrimaryExpression) + *NotPrimaryExpression = true; SourceLocation CoawaitLoc = ConsumeToken(); - Res = ParseCastExpression(false); + Res = ParseCastExpression(AnyCastExpr); if (!Res.isInvalid()) Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get()); return Res; @@ -1190,9 +1352,11 @@ case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU] // __extension__ silences extension warnings in the subexpression. + if (NotPrimaryExpression) + *NotPrimaryExpression = true; 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; @@ -1209,8 +1373,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 (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseUnaryExprOrTypeTraitExpression(); case tok::ampamp: { // unary-expression: '&&' identifier + if (NotPrimaryExpression) + *NotPrimaryExpression = true; SourceLocation AmpAmpLoc = ConsumeToken(); if (Tok.isNot(tok::identifier)) return ExprError(Diag(Tok, diag::err_expected) << tok::identifier); @@ -1229,15 +1397,23 @@ case tok::kw_dynamic_cast: case tok::kw_reinterpret_cast: case tok::kw_static_cast: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Res = ParseCXXCasts(); break; case tok::kw___builtin_bit_cast: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Res = ParseBuiltinBitCast(); break; case tok::kw_typeid: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Res = ParseCXXTypeid(); break; case tok::kw___uuidof: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Res = ParseCXXUuidof(); break; case tok::kw_this: @@ -1302,6 +1478,10 @@ return ExprError(); } + // Everything henceforth is a postfix-expression. + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + if (SavedKind == tok::kw_typename) { // postfix-expression: typename-specifier '(' expression-list[opt] ')' // typename-specifier braced-init-list @@ -1338,8 +1518,9 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::annot_cxxscope)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + isTypeCast, isVectorLiteral, + NotPrimaryExpression); Token Next = NextToken(); if (Next.is(tok::annot_template_id)) { @@ -1352,8 +1533,9 @@ ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); AnnotateTemplateIdTokenAsType(); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + isTypeCast, isVectorLiteral, + NotPrimaryExpression); } } @@ -1369,8 +1551,9 @@ // translate it into a type and continue parsing as a cast // expression. AnnotateTemplateIdTokenAsType(); - return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, isTypeCast); + return ParseCastExpression(ParseKind, isAddressOfOperand, + NotCastExpr, isTypeCast, isVectorLiteral, + NotPrimaryExpression); } // Fall through to treat the template-id as an id-expression. @@ -1387,15 +1570,22 @@ if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::coloncolon)) - return ParseCastExpression(isUnaryExpression, isAddressOfOperand); + return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, + isVectorLiteral, NotPrimaryExpression); // ::new -> [C++] new-expression // ::delete -> [C++] delete-expression SourceLocation CCLoc = ConsumeToken(); - if (Tok.is(tok::kw_new)) + if (Tok.is(tok::kw_new)) { + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseCXXNewExpression(true, CCLoc); - if (Tok.is(tok::kw_delete)) + } + if (Tok.is(tok::kw_delete)) { + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseCXXDeleteExpression(true, CCLoc); + } // This is not a type name or scope specifier, it is an invalid expression. Diag(CCLoc, diag::err_expected_expression); @@ -1403,12 +1593,18 @@ } case tok::kw_new: // [C++] new-expression + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseCXXNewExpression(false, Tok.getLocation()); case tok::kw_delete: // [C++] delete-expression + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseCXXDeleteExpression(false, Tok.getLocation()); case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Diag(Tok, diag::warn_cxx98_compat_noexcept_expr); SourceLocation KeyLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); @@ -1437,13 +1633,19 @@ case tok::kw___array_rank: case tok::kw___array_extent: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseArrayTypeTrait(); case tok::kw___is_lvalue_expr: case tok::kw___is_rvalue_expr: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; return ParseExpressionTrait(); case tok::at: { + if (NotPrimaryExpression) + *NotPrimaryExpression = true; SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); } @@ -1465,8 +1667,13 @@ // expression, or we have something that doesn't appear to be a lambda. // If we're in the last case, we fall back to ParseObjCMessageExpression. Res = TryParseLambdaExpression(); - if (!Res.isInvalid() && !Res.get()) + if (!Res.isInvalid() && !Res.get()) { + // We assume Objective-C++ message expressions are not + // primary-expressions. + if (NotPrimaryExpression) + *NotPrimaryExpression = true; Res = ParseObjCMessageExpression(); + } break; } Res = ParseLambdaExpression(); @@ -1486,6 +1693,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 (ParseKind == 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); @@ -1929,7 +2141,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 @@ -2474,8 +2686,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(); @@ -2555,7 +2767,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); @@ -2607,7 +2819,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()) { diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1371,10 +1371,6 @@ DeclEndLoc = Range.getEnd(); } - PrototypeScope.Exit(); - - WarnIfHasCUDATargetAttr(); - SourceLocation NoLoc; D.AddTypeInfo(DeclaratorChunk::getFunction( /*HasProto=*/true, @@ -1389,13 +1385,22 @@ /*DeclsInPrototype=*/None, LParenLoc, FunLocalRangeEnd, D, TrailingReturnType, &DS), std::move(Attr), DeclEndLoc); + + // Parse requires-clause[opt]. + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); + + PrototypeScope.Exit(); + + WarnIfHasCUDATargetAttr(); } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute, tok::kw_constexpr, tok::kw_consteval, tok::kw___private, tok::kw___global, tok::kw___local, - tok::kw___constant, tok::kw___generic) || + tok::kw___constant, tok::kw___generic, + tok::kw_requires) || (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) { // It's common to forget that one needs '()' before 'mutable', an attribute - // specifier, or the result type. Deal with this. + // specifier, the result type, or the requires clause. Deal with this. unsigned TokKind = 0; switch (Tok.getKind()) { case tok::kw_mutable: TokKind = 0; break; @@ -1409,6 +1414,7 @@ case tok::l_square: TokKind = 2; break; case tok::kw_constexpr: TokKind = 3; break; case tok::kw_consteval: TokKind = 4; break; + case tok::kw_requires: TokKind = 5; break; default: llvm_unreachable("Unknown token kind"); } @@ -1440,8 +1446,6 @@ DeclEndLoc = Range.getEnd(); } - WarnIfHasCUDATargetAttr(); - SourceLocation NoLoc; D.AddTypeInfo(DeclaratorChunk::getFunction( /*HasProto=*/true, @@ -1462,6 +1466,12 @@ /*DeclsInPrototype=*/None, DeclLoc, DeclEndLoc, D, TrailingReturnType), std::move(Attr), DeclEndLoc); + + // Parse the requires-clause, if present. + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(D); + + WarnIfHasCUDATargetAttr(); } // FIXME: Rename BlockScope -> ClosureScope if we decide to continue using @@ -3238,7 +3248,7 @@ return ExprError(); } - ExprResult Operand(ParseCastExpression(false)); + ExprResult Operand(ParseCastExpression(AnyCastExpr)); if (Operand.isInvalid()) return Operand; @@ -3469,7 +3479,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. diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2264,8 +2264,8 @@ return ExprError(); SourceLocation ELoc = Tok.getLocation(); - ExprResult LHS(ParseCastExpression( - /*isUnaryExpression=*/false, IsAddressOfOperand, NotTypeCast)); + ExprResult LHS(ParseCastExpression(AnyCastExpr, IsAddressOfOperand, + NotTypeCast)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); @@ -2513,7 +2513,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); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -130,7 +130,9 @@ if (TryConsumeToken(tok::kw_requires)) { OptionalRequiresClauseConstraintER = - Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + Actions.CorrectDelayedTyposInExpr( + ParseConstraintLogicalOrExpression( + /*IsTrailingRequiresClause=*/false)); if (!OptionalRequiresClauseConstraintER.isUsable()) { // Skip until the semi-colon or a '}'. SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); @@ -254,8 +256,12 @@ }); LateParsedAttrList LateParsedAttrs(true); - if (DeclaratorInfo.isFunctionDeclarator()) + if (DeclaratorInfo.isFunctionDeclarator()) { + if (Tok.is(tok::kw_requires)) + ParseTrailingRequiresClause(DeclaratorInfo); + MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs); + } if (DeclaratorInfo.isFunctionDeclarator() && isStartOfFunctionDefinition(DeclaratorInfo)) { diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1031,6 +1031,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; } @@ -2014,7 +2018,6 @@ /// 'throw' '(' type-id-list[opt] ')' /// Parser::TPResult Parser::TryParseFunctionDeclarator() { - // The '(' is already parsed. TPResult TPR = TryParseParameterDeclarationClause(); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2008,7 +2008,7 @@ // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization // preserves Result. Result = E; - if (!Self.resolveAndFixAddressOfOnlyViableOverloadCandidate( + if (!Self.resolveAndFixAddressOfSingleOverloadCandidate( Result, /*DoFunctionPointerConversion=*/true)) return false; return Result.isUsable(); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaDiagnostic.h" @@ -18,12 +19,16 @@ #include "clang/Sema/Template.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/OperatorPrecedence.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" using namespace clang; using namespace sema; -bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) { +bool +Sema::CheckConstraintExpression(Expr *ConstraintExpression, Token NextToken, + bool *PossibleNonPrimary, + bool IsTrailingRequiresClause) { // C++2a [temp.constr.atomic]p1 // ..E shall be a constant expression of type bool. @@ -31,22 +36,56 @@ if (auto *BinOp = dyn_cast(ConstraintExpression)) { if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr) - return CheckConstraintExpression(BinOp->getLHS()) && - CheckConstraintExpression(BinOp->getRHS()); + return CheckConstraintExpression(BinOp->getLHS(), NextToken, + PossibleNonPrimary) && + CheckConstraintExpression(BinOp->getRHS(), NextToken, + PossibleNonPrimary); } else if (auto *C = dyn_cast(ConstraintExpression)) - return CheckConstraintExpression(C->getSubExpr()); + return CheckConstraintExpression(C->getSubExpr(), NextToken, + PossibleNonPrimary); + + QualType Type = ConstraintExpression->getType(); + + auto CheckForNonPrimary = [&] { + if (PossibleNonPrimary) + *PossibleNonPrimary = + // 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. + (NextToken.is(tok::l_paren) && + (IsTrailingRequiresClause || + (Type->isDependentType() && + IsDependentFunctionNameExpr(ConstraintExpression)) || + Type->isFunctionType() || + Type->isSpecificBuiltinType(BuiltinType::Overload))) || + // 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. + getBinOpPrecedence(NextToken.getKind(), + /*GreaterThanIsOperator=*/true, + getLangOpts().CPlusPlus11) > prec::LogicalAnd; + }; // An atomic constraint! - if (ConstraintExpression->isTypeDependent()) + if (ConstraintExpression->isTypeDependent()) { + CheckForNonPrimary(); return true; + } - QualType Type = ConstraintExpression->getType(); if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) { Diag(ConstraintExpression->getExprLoc(), diag::err_non_bool_atomic_constraint) << Type << ConstraintExpression->getSourceRange(); + CheckForNonPrimary(); return false; } + + if (PossibleNonPrimary) + *PossibleNonPrimary = false; return true; } @@ -417,123 +456,25 @@ } } -namespace { -struct AtomicConstraint { - const Expr *ConstraintExpr; - llvm::Optional> ParameterMapping; - - AtomicConstraint(Sema &S, const Expr *ConstraintExpr) : - ConstraintExpr(ConstraintExpr) { }; - - bool hasMatchingParameterMapping(ASTContext &C, - const AtomicConstraint &Other) const { - if (!ParameterMapping != !Other.ParameterMapping) - return false; - if (!ParameterMapping) - return true; - if (ParameterMapping->size() != Other.ParameterMapping->size()) - return false; - - for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) - if (!C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) - .structurallyEquals(C.getCanonicalTemplateArgument( - (*Other.ParameterMapping)[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 - // if and only if the A and B are identical [...] - // - // C++ [temp.constr.atomic] p2 - // Two atomic constraints are identical if they are formed from the - // same expression and the targets of the parameter mappings are - // equivalent according to the rules for expressions [...] - - // We do not actually substitute the parameter mappings into the - // constraint expressions, therefore the constraint expressions are - // the originals, and comparing them will suffice. - if (ConstraintExpr != Other.ConstraintExpr) - return false; - - // Check that the parameter lists are identical - return hasMatchingParameterMapping(C, Other); - } -}; - -/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is -/// either an atomic constraint, a conjunction of normalized constraints or a -/// disjunction of normalized constraints. -struct NormalizedConstraint { - enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; - - using CompoundConstraint = llvm::PointerIntPair< - std::pair *, 1, - CompoundConstraintKind>; - - llvm::PointerUnion Constraint; - - NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; - NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, - NormalizedConstraint RHS, CompoundConstraintKind Kind) - : Constraint{CompoundConstraint{ - new (C) std::pair{LHS, - RHS}, - Kind}} { }; - - CompoundConstraintKind getCompoundKind() const { - assert(!isAtomic() && "getCompoundKind called on atomic constraint."); - return Constraint.get().getInt(); - } - - bool isAtomic() const { return Constraint.is(); } - - NormalizedConstraint &getLHS() const { - assert(!isAtomic() && "getLHS called on atomic constraint."); - return Constraint.get().getPointer()->first; - } - - NormalizedConstraint &getRHS() const { - assert(!isAtomic() && "getRHS called on atomic constraint."); - return Constraint.get().getPointer()->second; +const NormalizedConstraint * +Sema::getNormalizedAssociatedConstraints( + NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints) { + auto CacheEntry = NormalizationCache.find(ConstrainedDecl); + if (CacheEntry == NormalizationCache.end()) { + auto Normalized = + NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl, + AssociatedConstraints); + CacheEntry = + NormalizationCache + .try_emplace(ConstrainedDecl, + Normalized + ? new (Context) NormalizedConstraint( + std::move(*Normalized)) + : nullptr) + .first; } - - AtomicConstraint *getAtomicConstraint() const { - assert(isAtomic() && - "getAtomicConstraint called on non-atomic constraint."); - return Constraint.get(); - } - - static llvm::Optional - fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef E) { - assert(E.size() != 0); - auto First = fromConstraintExpr(S, D, E[0]); - if (E.size() == 1) - return First; - auto Second = fromConstraintExpr(S, D, E[1]); - if (!Second) - return llvm::Optional{}; - llvm::Optional Conjunction; - Conjunction.emplace(S.Context, std::move(*First), std::move(*Second), - CCK_Conjunction); - for (unsigned I = 2; I < E.size(); ++I) { - auto Next = fromConstraintExpr(S, D, E[I]); - if (!Next) - return llvm::Optional{}; - NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction), - std::move(*Next), CCK_Conjunction); - *Conjunction = std::move(NewConjunction); - } - return Conjunction; - } - -private: - static llvm::Optional fromConstraintExpr(Sema &S, - NamedDecl *D, - const Expr *E); -}; + return CacheEntry->second; +} static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, ConceptDecl *Concept, ArrayRef TemplateArgs, @@ -555,11 +496,13 @@ llvm::SmallBitVector OccurringIndices(TemplateParams->size()); S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false, /*Depth=*/0, OccurringIndices); - Atomic.ParameterMapping.emplace(); - Atomic.ParameterMapping->reserve(OccurringIndices.size()); - for (unsigned I = 0, C = TemplateParams->size(); I != C; ++I) + Atomic.ParameterMapping.emplace( + MutableArrayRef( + new (S.Context) TemplateArgumentLoc[OccurringIndices.count()], + OccurringIndices.count())); + for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) if (OccurringIndices[I]) - Atomic.ParameterMapping->push_back( + new (&(*Atomic.ParameterMapping)[J++]) TemplateArgumentLoc( S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I], // Here we assume we do not support things like // template @@ -585,6 +528,30 @@ return false; } +Optional +NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D, + ArrayRef E) { + assert(E.size() != 0); + auto First = fromConstraintExpr(S, D, E[0]); + if (E.size() == 1) + return First; + auto Second = fromConstraintExpr(S, D, E[1]); + if (!Second) + return None; + llvm::Optional Conjunction; + Conjunction.emplace(S.Context, std::move(*First), std::move(*Second), + CCK_Conjunction); + for (unsigned I = 2; I < E.size(); ++I) { + auto Next = fromConstraintExpr(S, D, E[I]); + if (!Next) + return llvm::Optional{}; + NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction), + std::move(*Next), CCK_Conjunction); + *Conjunction = std::move(NewConjunction); + } + return Conjunction; +} + llvm::Optional NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) { assert(E != nullptr); @@ -604,11 +571,11 @@ return None; return NormalizedConstraint( - S.Context, *LHS, *RHS, + S.Context, std::move(*LHS), std::move(*RHS), BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction); } } else if (auto *CSE = dyn_cast(E)) { - Optional SubNF; + const NormalizedConstraint *SubNF; { Sema::InstantiatingTemplate Inst( S, CSE->getExprLoc(), @@ -623,24 +590,26 @@ // constraint. If any such substitution results in an invalid type or // expression, the program is ill-formed; no diagnostic is required. // [...] - SubNF = fromConstraintExpr(S, CSE->getNamedConcept(), - CSE->getNamedConcept()->getConstraintExpr()); + ConceptDecl *CD = CSE->getNamedConcept(); + SubNF = S.getNormalizedAssociatedConstraints(CD, + {CD->getConstraintExpr()}); if (!SubNF) return None; } + Optional New; + New.emplace(S.Context, *SubNF); + if (substituteParameterMappings( - S, *SubNF, CSE->getNamedConcept(), + S, *New, CSE->getNamedConcept(), CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten())) return None; - return SubNF; + return New; } return NormalizedConstraint{new (S.Context) AtomicConstraint(S, E)}; } -} // namespace - using NormalForm = llvm::SmallVector, 4>; @@ -703,22 +672,9 @@ return Res; } -static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef P, - NamedDecl *DQ, ArrayRef Q, bool &Subsumes) { - // 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, DP, P); - if (!PNormalized) - return true; - const NormalForm PDNF = makeDNF(*PNormalized); - - auto QNormalized = NormalizedConstraint::fromConstraintExprs(S, DQ, Q); - if (!QNormalized) - return true; - const NormalForm QCNF = makeCNF(*QNormalized); - +template +static bool subsumes(NormalForm PDNF, NormalForm QCNF, + AtomicSubsumptionEvaluator E) { // C++ [temp.constr.order] p2 // Then, P subsumes Q if and only if, for every disjunctive clause Pi in the // disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in @@ -733,7 +689,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; } @@ -741,13 +697,32 @@ if (Found) break; } - if (!Found) { - Subsumes = false; + if (!Found) return false; - } } } - Subsumes = true; + return true; +} + +template +static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef P, + NamedDecl *DQ, ArrayRef Q, bool &Subsumes, + 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 = S.getNormalizedAssociatedConstraints(DP, P); + if (!PNormalized) + return true; + const NormalForm PDNF = makeDNF(*PNormalized); + + auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q); + if (!QNormalized) + return true; + const NormalForm QCNF = makeCNF(*QNormalized); + + Subsumes = subsumes(PDNF, QCNF, E); return false; } @@ -770,8 +745,84 @@ Result = CacheEntry->second; return false; } - if (subsumes(*this, D1, AC1, D2, AC2, Result)) + + if (subsumes(*this, D1, AC1, D2, AC2, Result, + [this] (const AtomicConstraint &A, const AtomicConstraint &B) { + return A.subsumes(Context, B); + })) return true; SubsumptionCache.try_emplace(Key, Result); return false; -} \ No newline at end of file +} + +bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, + ArrayRef AC1, NamedDecl *D2, ArrayRef AC2) { + if (isSFINAEContext()) + // No need to work here because our notes would be discarded. + return false; + + 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); + auto *Normalized1 = getNormalizedAssociatedConstraints(D1, AC1); + if (!Normalized1) + return false; + const NormalForm DNF1 = makeDNF(*Normalized1); + const NormalForm CNF1 = makeCNF(*Normalized1); + + auto *Normalized2 = getNormalizedAssociatedConstraints(D2, AC2); + if (!Normalized2) + return false; + const NormalForm DNF2 = makeDNF(*Normalized2); + const NormalForm CNF2 = makeCNF(*Normalized2); + + bool Is1AtLeastAs2Normally = subsumes(DNF1, CNF2, NormalExprEvaluator); + bool Is2AtLeastAs1Normally = subsumes(DNF2, CNF1, NormalExprEvaluator); + bool Is1AtLeastAs2 = subsumes(DNF1, CNF2, IdenticalExprEvaluator); + bool Is2AtLeastAs1 = subsumes(DNF2, CNF1, IdenticalExprEvaluator); + 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) + << AmbiguousAtomic1->getSourceRange(); + Diag(AmbiguousAtomic2->getBeginLoc(), + diag::note_ambiguous_atomic_constraints_similar_expression) + << AmbiguousAtomic2->getSourceRange(); + return true; +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7895,7 +7895,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; } } @@ -8240,7 +8246,8 @@ NewFD = FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC, isInline, HasPrototype, - CSK_unspecified); + CSK_unspecified, + /*TrailingRequiresClause=*/nullptr); if (D.isInvalidType()) NewFD->setInvalidDecl(); @@ -8257,6 +8264,7 @@ ConstexprKind = CSK_unspecified; D.getMutableDeclSpec().ClearConstexprSpec(); } + 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 @@ -8276,7 +8284,8 @@ return CXXConstructorDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, ExplicitSpecifier, isInline, - /*isImplicitlyDeclared=*/false, ConstexprKind); + /*isImplicitlyDeclared=*/false, ConstexprKind, InheritedConstructor(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. @@ -8285,8 +8294,8 @@ CXXRecordDecl *Record = cast(DC); CXXDestructorDecl *NewDD = CXXDestructorDecl::Create( SemaRef.Context, Record, D.getBeginLoc(), NameInfo, R, TInfo, - isInline, - /*isImplicitlyDeclared=*/false, ConstexprKind); + isInline, /*isImplicitlyDeclared=*/false, ConstexprKind, + 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 @@ -8306,7 +8315,8 @@ return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), Name, R, TInfo, SC, isInline, - /*hasPrototype=*/true, ConstexprKind); + /*hasPrototype=*/true, ConstexprKind, + TrailingRequiresClause); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -8323,9 +8333,14 @@ IsVirtualOkay = true; return CXXConversionDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, isInline, ExplicitSpecifier, ConstexprKind, SourceLocation()); + TInfo, isInline, ExplicitSpecifier, ConstexprKind, SourceLocation(), + TrailingRequiresClause); } else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) { + if (TrailingRequiresClause) + SemaRef.Diag(TrailingRequiresClause->getBeginLoc(), + diag::err_trailing_requires_clause_on_deduction_guide) + << TrailingRequiresClause->getSourceRange(); SemaRef.CheckDeductionGuideDeclarator(D, R, SC); return CXXDeductionGuideDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), @@ -8347,7 +8362,8 @@ // This is a C++ method declaration. CXXMethodDecl *Ret = CXXMethodDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, SC, isInline, ConstexprKind, SourceLocation()); + TInfo, SC, isInline, ConstexprKind, SourceLocation(), + TrailingRequiresClause); IsVirtualOkay = !Ret->isStatic(); return Ret; } else { @@ -8361,7 +8377,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*/, - ConstexprKind); + ConstexprKind, TrailingRequiresClause); } } @@ -10572,6 +10588,11 @@ } } } + if (Method->isVirtual() && NewFD->getTrailingRequiresClause()) + // 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); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7569,7 +7569,8 @@ NewFD = FunctionDecl::Create( FD->getASTContext(), FD->getDeclContext(), Loc, Loc, DeclarationName(II), FD->getType(), FD->getTypeSourceInfo(), SC_None, - false /*isInlineSpecified*/, FD->hasPrototype(), CSK_unspecified); + false /*isInlineSpecified*/, FD->hasPrototype(), CSK_unspecified, + FD->getTrailingRequiresClause()); NewD = NewFD; if (FD->getQualifier()) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3868,6 +3868,26 @@ PushFunctionScope(); } +void Sema::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) { + if (!D.isFunctionDeclarator()) + return; + auto &FTI = D.getFunctionTypeInfo(); + if (!FTI.Params) + return; + for (auto &Param : ArrayRef(FTI.Params, + FTI.NumParams)) { + auto *ParamDecl = cast(Param.Param); + if (ParamDecl->getDeclName()) + PushOnScopeChains(ParamDecl, S, /*AddToContext=*/false); + } +} + +ExprResult Sema::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) { + if (ConstraintExpr.isInvalid()) + return ExprError(); + return CorrectDelayedTyposInExpr(ConstraintExpr); +} + /// This is invoked after parsing an in-class initializer for a /// non-static C++ class member, and after instantiating an in-class initializer /// in a class template. Such actions are deferred until the class is complete. @@ -12702,7 +12722,8 @@ BaseCtor->getExplicitSpecifier(), /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr ? BaseCtor->getConstexprKind() : CSK_unspecified, - InheritedConstructor(Shadow, BaseCtor)); + InheritedConstructor(Shadow, BaseCtor), + BaseCtor->getTrailingRequiresClause()); if (Shadow->isInvalidDecl()) DerivedCtor->setInvalidDecl(); @@ -17092,6 +17113,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); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -326,6 +326,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; } @@ -17975,7 +17999,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. @@ -18112,3 +18136,8 @@ return new (Context) ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy); } + +bool Sema::IsDependentFunctionNameExpr(Expr *E) { + assert(E->isTypeDependent()); + return isa(E); +} diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -361,7 +361,8 @@ TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc, ArrayRef Params, - ConstexprSpecKind ConstexprKind) { + ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause) { QualType MethodType = MethodTypeInfo->getType(); TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -395,7 +396,7 @@ DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), MethodNameLoc), MethodType, MethodTypeInfo, SC_None, - /*isInline=*/true, ConstexprKind, EndLoc); + /*isInline=*/true, ConstexprKind, EndLoc, TrailingRequiresClause); Method->setAccess(AS_public); if (!TemplateParams) Class->addDecl(Method); @@ -972,7 +973,8 @@ KnownDependent, Intro.Default); CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, - ParamInfo.getDeclSpec().getConstexprSpecifier()); + ParamInfo.getDeclSpec().getConstexprSpecifier(), + ParamInfo.getTrailingRequiresClause()); if (ExplicitParams) CheckCXXDefaultArguments(Method); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1137,7 +1137,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; @@ -1273,23 +1274,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; } @@ -6258,6 +6274,16 @@ return; } + if (Expr *RequiresClause = Function->getTrailingRequiresClause()) { + 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) { @@ -6774,6 +6800,16 @@ return; } + if (Expr *RequiresClause = Method->getTrailingRequiresClause()) { + 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) { @@ -7130,6 +7166,17 @@ return; } + Expr *RequiresClause = Conversion->getTrailingRequiresClause(); + if (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] @@ -9461,6 +9508,35 @@ return BetterTemplate == Cand1.Function->getPrimaryTemplate(); } + // -— 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, AtLeastAsConstrained2; + if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function, + {RC2}, AtLeastAsConstrained1)) + return false; + if (!AtLeastAsConstrained1) + return false; + if (S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function, + {RC1}, AtLeastAsConstrained2)) + return false; + if (!AtLeastAsConstrained2) + return true; + } else if (RC1 || RC2) + return RC1 != nullptr; + } + } + // -- F1 is a constructor for a class D, F2 is a constructor for a base // class B of D, and for all arguments the corresponding parameters of // F1 and F2 have the same type. @@ -9829,6 +9905,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(); }); @@ -9886,6 +9980,55 @@ MaybeEmitInheritedConstructorNote(*this, Found); } +static void +MaybeDiagnoseAmbiguousConstraints(Sema &S, ArrayRef Cands) { + // 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. + + SmallVector FirstAC, SecondAC; + FunctionDecl *FirstCand = nullptr, *SecondCand = nullptr; + for (auto I = Cands.begin(), E = Cands.end(); I != E; ++I) { + if (!I->Function) + continue; + SmallVector AC; + if (auto *Template = I->Function->getPrimaryTemplate()) + Template->getAssociatedConstraints(AC); + else + I->Function->getAssociatedConstraints(AC); + if (AC.empty()) + continue; + if (FirstCand == nullptr) { + FirstCand = I->Function; + FirstAC = AC; + } else if (SecondCand == nullptr) { + SecondCand = I->Function; + SecondAC = AC; + } else { + // We have more than one pair of constrained functions - this check is + // expensive and we'd rather not try to diagnose it. + return; + } + } + if (!SecondCand) + return; + // The diagnostic can only happen if there are associated constraints on + // both sides (there needs to be some identical atomic constraint). + if (S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(FirstCand, FirstAC, + SecondCand, SecondAC)) + // Just show the user one diagnostic, they'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, @@ -10771,6 +10914,23 @@ case ovl_non_default_multiversion_function: // Do nothing, these should simply be ignored. break; + + case ovl_fail_constraints_not_satisfied: { + std::string FnDesc; + std::pair FnKindPair = + ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, + Cand->getRewriteKind(), FnDesc); + + S.Diag(Fn->getLocation(), + diag::note_ovl_candidate_constraints_not_satisfied) + << (unsigned)FnKindPair.first << (unsigned)ocs_non_template + << FnDesc /* Ignored */; + ConstraintSatisfaction Satisfaction; + if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(), + Satisfaction)) + break; + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + } } } @@ -11156,6 +11316,9 @@ S.Diag(PD.first, PD.second); NoteCandidates(S, Args, Cands, Opc, OpLoc); + + if (OCD == OCD_AmbiguousCandidates) + MaybeDiagnoseAmbiguousConstraints(S, {begin(), end()}); } void OverloadCandidateSet::NoteCandidates(Sema &S, ArrayRef Args, @@ -11804,15 +11967,33 @@ /// 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; + SmallVector AmbiguousDecls; + + auto CheckMoreConstrained = + [&] (FunctionDecl *FD1, FunctionDecl *FD2) -> Optional { + SmallVector AC1, AC2; + FD1->getAssociatedConstraints(AC1); + FD2->getAssociatedConstraints(AC2); + bool AtLeastAsConstrained1, AtLeastAsConstrained2; + if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1)) + return None; + if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2)) + return None; + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) + return None; + return AtLeastAsConstrained1; + }; + // Don't use the AddressOfResolver because we're specifically looking for // cases where we have one overload candidate that lacks // enable_if/pass_object_size/... @@ -11824,32 +12005,54 @@ 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) { + Optional MoreConstrainedThanPrevious = CheckMoreConstrained(FD, + Result); + if (!MoreConstrainedThanPrevious) { + IsResultAmbiguous = true; + AmbiguousDecls.push_back(FD); + continue; + } + if (!*MoreConstrainedThanPrevious) + continue; + // FD is more constrained - replace Result with it. + } + IsResultAmbiguous = false; DAP = I.getPair(); Result = FD; } - if (Result) + if (IsResultAmbiguous) + return nullptr; + + if (Result) { + SmallVector ResultAC; + // We skipped over some ambiguous declarations which might be ambiguous with + // the selected result. + for (FunctionDecl *Skipped : AmbiguousDecls) + if (!CheckMoreConstrained(Skipped, Result).hasValue()) + return nullptr; 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; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3750,6 +3750,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 diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3389,11 +3389,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, @@ -3439,6 +3434,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 @@ -3559,7 +3571,7 @@ DeclAccessPair DAP; if (FunctionDecl *Viable = - S.resolveAddressOfOnlyViableOverloadCandidate(Arg, DAP)) + S.resolveAddressOfSingleOverloadCandidate(Arg, DAP)) return GetTypeOfFunction(S, R, Viable); return {}; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -150,7 +150,7 @@ break; // If this function is a generic lambda specialization, we are done. - if (isGenericLambdaCallOperatorSpecialization(Function)) + if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; } else if (FunctionTemplateDecl *FunTmpl diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1845,6 +1845,18 @@ return nullptr; } + // FIXME: Concepts: Do not substitute into constraint expressions + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + if (TrailingRequiresClause) { + ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, + TemplateArgs); + if (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. @@ -1881,7 +1893,8 @@ Function = FunctionDecl::Create( SemaRef.Context, DC, D->getInnerLocStart(), NameInfo, T, TInfo, D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(), - D->hasWrittenPrototype(), D->getConstexprKind()); + D->hasWrittenPrototype(), D->getConstexprKind(), + TrailingRequiresClause); Function->setRangeEnd(D->getSourceRange().getEnd()); } @@ -1908,6 +1921,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 @@ -2167,6 +2183,18 @@ return nullptr; } + // FIXME: Concepts: Do not substitute into constraint expressions + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + if (TrailingRequiresClause) { + ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, + TemplateArgs); + if (SubstRC.isInvalid()) + return nullptr; + TrailingRequiresClause = SubstRC.get(); + if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) + return nullptr; + } + DeclContext *DC = Owner; if (isFriend) { if (QualifierLoc) { @@ -2199,23 +2227,27 @@ Method = CXXConstructorDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, InstantiatedExplicitSpecifier, Constructor->isInlineSpecified(), false, - Constructor->getConstexprKind()); + Constructor->getConstexprKind(), 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, Destructor->getConstexprKind()); + Destructor->isInlineSpecified(), false, Destructor->getConstexprKind(), + TrailingRequiresClause); Method->setRangeEnd(Destructor->getEndLoc()); } else if (CXXConversionDecl *Conversion = dyn_cast(D)) { Method = CXXConversionDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, Conversion->isInlineSpecified(), InstantiatedExplicitSpecifier, - Conversion->getConstexprKind(), Conversion->getEndLoc()); + Conversion->getConstexprKind(), 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->getConstexprKind(), D->getEndLoc()); + D->getConstexprKind(), D->getEndLoc(), + TrailingRequiresClause); } if (D->isInlined()) @@ -4117,6 +4149,48 @@ TemplateArgs); } +bool Sema::CheckInstantiatedFunctionTemplateConstraints( + SourceLocation PointOfInstantiation, FunctionDecl *Decl, + ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction) { + // In most cases we're not going to have constraints, so check for that first. + FunctionTemplateDecl *Template = Decl->getPrimaryTemplate(); + // Note - code synthesis context for the constraints check is created + // inside CheckConstraintsSatisfaction. + SmallVector TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + if (TemplateAC.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + // 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()) { + InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(), + InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(), + MLTAL.getInnermost(), SourceRange()); + if (Inst.isInvalid()) + return true; + if (addInstantiatedParametersToScope(*this, Decl, + Decl->getTemplateInstantiationPattern(), + Scope, MLTAL)) + return true; + } + + 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). /// diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -937,6 +937,10 @@ } } + if (Expr *TRC = D.getTrailingRequiresClause()) + if (TRC->containsUnexpandedParameterPack()) + return true; + return false; } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -11552,6 +11552,13 @@ NewCallOpType); } + // Transform the trailing requires clause + ExprResult NewTrailingRequiresClause; + if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) + // FIXME: Concepts: Substitution into requires clause should only happen + // when checking satisfaction. + NewTrailingRequiresClause = getDerived().TransformExpr(TRC); + // Create the local class that will describe the lambda. CXXRecordDecl *OldClass = E->getLambdaClass(); CXXRecordDecl *Class @@ -11572,7 +11579,8 @@ Class, E->getIntroducerRange(), NewCallOpTSI, E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs().getParams(), - E->getCallOperator()->getConstexprKind()); + E->getCallOperator()->getConstexprKind(), + NewTrailingRequiresClause.get()); LSI->CallOperator = NewCallOperator; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -822,6 +822,7 @@ if (Record.readInt()) { // hasExtInfo auto *Info = new (Reader.getContext()) DeclaratorDecl::ExtInfo(); Record.readQualifierInfo(*Info); + Info->TrailingRequiresClause = Record.readExpr(); DD->DeclInfo = Info; } QualType TSIType = Record.readType(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -523,8 +523,11 @@ 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.AddStmt(Info->TrailingRequiresClause); + } // The location information is deferred until the end of the record. Record.AddTypeRef(D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() : QualType()); diff --git a/clang/test/CXX/class.derived/class.virtual/p6.cpp b/clang/test/CXX/class.derived/class.virtual/p6.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/dcl/dcl.decl/p3.cpp b/clang/test/CXX/dcl/dcl.decl/p3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl/dcl.decl/p3.cpp @@ -0,0 +1,35 @@ +// 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 appear 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)))(); // 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; +}; + +template +struct R { + R(T t); +}; + +template +R(T) -> R requires true; // expected-error{{deduction guide cannot have a requires clause}} diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/mixed-constraints.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/mixed-constraints.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +auto l1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { return x; }; +// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = int]}} +// expected-note@-2{{because 'sizeof(decltype(x)) == 1' (4 == 1) evaluated to false}} + +auto l1t1 = l1('a'); +auto l1t2 = l1(1); +// expected-error@-1{{no matching function for call to object of type '(lambda at}} + +auto l2 = [] (auto... x) requires ((sizeof(decltype(x)) >= 2) && ...) { return (x + ...); }; +// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = ]}} +// expected-note@-2{{candidate template ignored: constraints not satisfied [with $0 = ]}} +// expected-note@-3 2{{because 'sizeof(decltype(x)) >= 2' (1 >= 2) evaluated to false}} + +auto l2t1 = l2('a'); +// expected-error@-1{{no matching function for call to object of type '(lambda at}} +auto l2t2 = l2(1, 'a'); +// expected-error@-1{{no matching function for call to object of type '(lambda at}} +auto l2t3 = l2((short)1, (short)1); \ No newline at end of file diff --git a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp @@ -0,0 +1,114 @@ +// 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{{similar constraint expressions not considered equivalent}} + // 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{{similar constraint expression 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 {{similar constraint expressions not considered equivalent}} + + void bar() requires (sizeof(long) >= 8 && sizeof(int) <= 30) { } + // expected-note@-1 {{candidate function}} + // expected-note@-2 {{similar constraint expression 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}} +} + diff --git a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/over/over.over/p4-2a.cpp b/clang/test/CXX/over/over.over/p4-2a.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/over/over.over/p4-2a.cpp @@ -0,0 +1,61 @@ +// 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 diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp @@ -23,14 +23,13 @@ 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')}} +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]}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ b/clang/test/CXX/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}} +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,8 +9,8 @@ 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); @@ -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,8 @@ // 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}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp +++ b/clang/test/CXX/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}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp +++ b/clang/test/CXX/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; }; diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/func-template-decl.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/func-template-decl.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/func-template-decl.cpp +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/var-template-decl.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/var-template-decl.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/var-template-decl.cpp +++ b/clang/test/CXX/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 diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -1,9 +1,12 @@ // 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{{similar constraint expressions not considered equivalen}} 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{{similar constraint expression here}} + class A{}; // expected-error{{class template partial specialization is not more specialized than the primary template}} template @@ -12,7 +15,7 @@ template requires C1 class B{}; -template requires C1 && sizeof(T) <= 10 +template requires (C1 && sizeof(T) <= 10) class B{}; template @@ -48,3 +51,15 @@ static_assert(F::value == 2); static_assert(F::value == 3); static_assert(F::value == 1); + +// Make sure atomic constraints subsume each other only if their parameter +// mappings are identical. + +template requires C2 +struct I { }; // expected-note {{template is declared here}} + +template requires C2 +struct I { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +template requires C2 && C2 +struct I { }; diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/function-templates.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.order/function-templates.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/function-templates.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{{similar constraint expressions not considered equivalent}} 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) +// expected-note@-1{{similar constraint expression here}} bool a() { return true; } // expected-note {{candidate function [with T = unsigned int]}} bool av = a(); // expected-error {{call to 'a' is ambiguous}} @@ -14,7 +16,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()); @@ -86,4 +88,4 @@ template struct X {}; template int h(X<0>); template int h(X); -static_assert(sizeof(h(X<0>{}))); \ No newline at end of file +static_assert(sizeof(h(X<0>{}))); diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp +++ b/clang/test/CXX/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{{similar constraint expressions not considered equivalent}} 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{{similar constraint expression 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 diff --git a/clang/test/CXX/temp/temp.explicit/p8.cpp b/clang/test/CXX/temp/temp.explicit/p8.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/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 diff --git a/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp b/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp --- a/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp +++ b/clang/test/Parser/cxx-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 diff --git a/clang/test/Parser/cxx-concepts-requires-clause.cpp b/clang/test/Parser/cxx-concepts-requires-clause.cpp --- a/clang/test/Parser/cxx-concepts-requires-clause.cpp +++ b/clang/test/Parser/cxx-concepts-requires-clause.cpp @@ -1,13 +1,11 @@ -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ %s -verify -// expected-no-diagnostics +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify // 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,81 @@ template requires true int C::Mx = 0; + +// Test behavior with non-primary-expression requires clauses + +template requires foo() +// expected-error@-1{{parentheses are required around this expression in a requires clause}} +struct B1 { }; + +int func() { } + +template requires func() +// expected-error@-1{{atomic constraint must be of type 'bool' (found '')}} +// expected-note@-2{{parentheses are required around this expression in a requires clause}} +struct B2 { }; + +template requires (foo()) +struct B3 { }; + +template requires T{} +// expected-error@-1{{parentheses are required around this expression in a requires clause}} +struct B4 { }; + +template requires sizeof(T) == 0 +// expected-error@-1{{parentheses are required around this expression in a requires clause}} +struct B5 { }; + +template requires (sizeof(T)) == 0 +// expected-error@-1{{parentheses are required around this expression in a requires clause}} +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{{expected '(' for function-style cast or type construction}} + +template +void bar() requires foo(); +// expected-error@-1{{parentheses are required around this expression in a 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{{parentheses are required around this expression in a requires clause}} + +template +void bar() requires T{}; +// expected-error@-1{{parentheses are required around this expression in a requires clause}} + +template +void bar() requires sizeof(T) == 0; +// expected-error@-1{{parentheses are required around this expression in a requires clause}} + +template +void bar() requires (sizeof(T)) == 0; +// expected-error@-1{{parentheses are required around this expression in a requires clause}} + +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}} +}; + +auto lambda1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { }; + +auto lambda2 = [] (auto x) constexpr -> int requires (sizeof(decltype(x)) == 1) { return 0; }; + +auto lambda3 = [] requires (sizeof(char) == 1) { }; +// expected-error@-1{{lambda requires '()' before 'requires' clause}} \ No newline at end of file diff --git a/clang/test/SemaTemplate/instantiate-requires-clause.cpp b/clang/test/SemaTemplate/instantiate-requires-clause.cpp new file mode 100644 --- /dev/null +++ b/clang/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'}}