Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -2041,6 +2041,37 @@ static bool classofKind(Kind K) { return K == CXXDeductionGuide; } }; +/// \brief Represents the body of a requires-expression. +/// +/// This decl exists merely to serve as the DeclContext for the local +/// parameters of the requires expression as well as other declarations inside +/// it. +/// +/// \code +/// template requires requires (T t) { {t++} -> Regular }; +/// \endcode +/// +/// In this example, a RequiresExpr object will be generated for the expression, +/// and a RequiresExprBodyDecl will be created to hold the parameter t and the +/// template argument list imposed by the compound requirement. +class RequiresExprBodyDecl : public Decl, public DeclContext { + RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc) + : Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {} + +public: + friend class ASTDeclReader; + friend class ASTDeclWriter; + + static RequiresExprBodyDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation StartLoc); + + static RequiresExprBodyDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == RequiresExprBody; } +}; + /// Represents a static or instance method of a struct/union/class. /// /// In the terminology of the C++ Standard, these are the (static and Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -4660,6 +4660,68 @@ } }; +/// C++2a [expr.prim.req]: +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// [...] +/// A requires-expression is a prvalue of type bool [...] +class RequiresExpr final : public Expr { + bool IsSatisfied; + SourceLocation RequiresKWLoc; + RequiresExprBodyDecl *Body; + llvm::SmallVector LocalParameters; + llvm::SmallVector Requirements; + SourceLocation RBraceLoc; + +public: + RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc); + RequiresExpr(ASTContext &C, EmptyShell Empty) : + RequiresExpr(C, SourceLocation(), nullptr, {}, {}, SourceLocation()) {} + + void setLocalParameters(ArrayRef LocalParameters); + ArrayRef getLocalParameters() const { return LocalParameters; } + + void setBody(RequiresExprBodyDecl *Body) { this->Body = Body; } + const RequiresExprBodyDecl *getBody() const { return Body; } + RequiresExprBodyDecl *getBody() { return Body; } + + void setRequirements(ArrayRef Requirements); + ArrayRef getRequirements() const { return Requirements; } + + /// \brief Whether or not the requires clause is satisfied. + /// The expression must not be dependent. + bool isSatisfied() const { + assert(!isValueDependent() + && "isSatisfied called on a dependent RequiresExpr"); + return IsSatisfied; + } + + SourceLocation getRequiresKWLoc() const { return RequiresKWLoc; } + void setRequiresKWLoc(SourceLocation Loc) { RequiresKWLoc = Loc; } + + SourceLocation getRBraceLoc() const { return RBraceLoc; } + void setRBraceLoc(SourceLocation Loc) { RBraceLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == RequiresExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { return RequiresKWLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { + return RBraceLoc; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2116,6 +2116,8 @@ TRY_TO(TraverseStmt(D->getDefaultArg())); }) +DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {}) + #undef DEF_TRAVERSE_DECL // ----------------- Stmt traversal ----------------- @@ -2641,6 +2643,34 @@ S->getTemplateArgsAsWritten()->NumTemplateArgs)); }) +DEF_TRAVERSE_STMT(RequiresExpr, { + TRY_TO(TraverseDecl(S->getBody())); + for (ParmVarDecl *Parm : S->getLocalParameters()) + TRY_TO(TraverseDecl(Parm)); + for (Requirement *Req : S->getRequirements()) + if (auto *TypeReq = dyn_cast(Req)) { + if (!TypeReq->isSubstitutionFailure()) + TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc())); + } else if (auto *ExprReq = dyn_cast(Req)) { + if (!ExprReq->isExprSubstitutionFailure()) + TRY_TO(TraverseStmt(ExprReq->getExpr())); + auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isTrailingReturnType()) + TRY_TO(TraverseTypeLoc(RetReq.getTrailingReturnTypeExpectedType() + ->getTypeLoc())); + else if (RetReq.isConstrainedParameter()) { + TRY_TO(TraverseTemplateParameterListHelper( + RetReq.getConstrainedParamTemplateParameterList())); + TRY_TO(TraverseTypeLoc(RetReq.getConstrainedParamExpectedType() + ->getTypeLoc())); + } + } else { + auto *NestedReq = cast(Req); + if (!NestedReq->isSubstitutionFailure()) + TRY_TO(TraverseStmt(NestedReq->getConstraintExpr())); + } +}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(FixedPointLiteral, {}) Index: include/clang/Basic/DeclNodes.td =================================================================== --- include/clang/Basic/DeclNodes.td +++ include/clang/Basic/DeclNodes.td @@ -101,5 +101,6 @@ def OMPThreadPrivate : Decl; def OMPAllocate : Decl; def OMPRequires : Decl; +def RequiresExprBody : Decl, DeclContext; def Empty : Decl; Index: include/clang/Basic/DiagnosticIDs.h =================================================================== --- include/clang/Basic/DiagnosticIDs.h +++ include/clang/Basic/DiagnosticIDs.h @@ -32,11 +32,11 @@ DIAG_SIZE_FRONTEND = 150, DIAG_SIZE_SERIALIZATION = 120, DIAG_SIZE_LEX = 400, - DIAG_SIZE_PARSE = 500, + DIAG_SIZE_PARSE = 600, DIAG_SIZE_AST = 150, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 3500, + DIAG_SIZE_SEMA = 3600, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, }; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -733,6 +733,30 @@ def err_constrained_parameter_missing_arguments : Error< "concept %0 requires more than 1 template argument; provide the remaining " "arguments explicitly to use it here">; +def note_ill_formed_requires_expression_outside_template : Note< + "requires expression outside a template declaration may not contain invalid " + "types or expressions">; +def err_empty_requires_expr : Error< + "a requires expression must contain at least one requirement">; +def err_requires_expr_parameter_list_ellipsis : Error< + "varargs not allowed in requires expression">; +def err_requires_expr_type_req_illegal_identifier : Error< + "expected identifier or template-id in type requirement">; +def err_requires_expr_type_req_template_args_on_non_template : Error< + "template arguments provided for non-template '%0'">; +def err_expected_semi_requirement : Error< + "expected ';' at end of requirement">; +def err_requires_expr_missing_arrow : Error< + "expected '->' before expression type requirement">; +def err_requires_expr_simple_requirement_noexcept : Error< + "'noexcept' can only be used in a compound requirements (with '{' '}' around " + "the expression)">; +def err_requires_expr_simple_requirement_unexpected_tok : Error< + "unexpected %0 after expression. Did you intend to use a compound " + "requirement? (with '{' '}' around the expression)">; +def err_requires_expr_compound_requirement_non_type_concept : Error< + "only type concepts can be used to constrain the type of an expression " + "(%0 is a %select{template|value}1 concept)">; def err_missing_dependent_template_keyword : Error< "use 'template' keyword to treat '%0' as a dependent template name">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2002,7 +2002,11 @@ "|in template argument|in typedef|in type alias|in function return type" "|in conversion function type|here|in lambda parameter" "|in type allocated by 'new'|in K&R-style function parameter" - "|in template parameter|in friend declaration}1">; + "|in template parameter|in friend declaration" + "|in requires expression parameter}1">; +def err_auto_not_allowed_in_return_type_requirement : Error< + "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression " + "type requirement">; def err_dependent_deduced_tst : Error< "typename specifier refers to " "%select{class template|function template|variable template|alias template|" @@ -2458,13 +2462,13 @@ def note_substituted_constraint_expr_is_ill_formed : Note< "because substituted constraint expression is ill-formed%0">; def note_atomic_constraint_evaluated_to_false : Note< - "%select{and |because }0'%1' evaluated to false">; + "%select{and|because}0 '%1' evaluated to false">; def note_concept_specialization_constraint_evaluated_to_false : Note< - "%select{and |because }0'%1' evaluated to false">; + "%select{and|because}0 '%1' evaluated to false">; def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note< - "%select{and |because }0%1 does not satisfy %2">; + "%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">; + "%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">; def note_could_not_normalize_argument_substitution_failed : Note< "when substituting into %0 %1. Make sure concept arguments are " "valid for any substitution">; @@ -2472,6 +2476,51 @@ "virtual function cannot have a requires clause">; def err_reference_to_function_with_unsatisfied_constraints : Error< "invalid reference to function %0: constraints not satisfied">; +def note_requires_expr_ill_formed_expr : Note< + "expression is invalid: %0">; +def note_requires_expr_no_implicit_conversion : Note< + "no implicit conversion exists between expression type %0 and expected type " + "%1">; +def err_requires_expr_local_parameter_default_argument : Error< + "default arguments not allowed for parameters of a requires expression">; +def err_requires_expr_parameter_referenced_in_evaluated_context : Error< + "constraint variable %0 cannot be used in an evaluated context">; +def note_expr_requirement_expr_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_expr_requirement_expr_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def note_expr_requirement_noexcept_not_met : Note< + "%select{and|because}0 '%1' may throw an exception">; +def note_expr_requirement_type_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_expr_requirement_type_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def note_expr_requirement_ambiguous_conversion : Note< + "%select{and|because}0 conversion from expression type %1 to required type %2" + " is ambiguous">; +def note_expr_requirement_no_implicit_conversion : Note< + "%select{and|because}0 expression type %1 not implicitly convertible to " + "required type %2">; +def note_expr_requirement_deduction_failed : Note< + "%select{and|because}0 expression type %1 does not match given pattern '%2'">; +def note_expr_requirement_constraints_not_satisfied : Note< + "%select{and|because}0 type constraint '%1' was not satisfied:">; +def note_expr_requirement_constraints_not_satisfied_simple : Note< + "%select{and|because}0 expression type %1 does not satisfy %2:">; +def note_type_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_type_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def err_type_requirement_non_type_template : Error< + "'%0' refers to a %select{class template|function template|" + "variable template|alias template|template template parameter|template}1 " + "and not a type template">; +def err_type_requirement_no_such_type : Error< + "'%0' does not name a type">; +def note_nested_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_nested_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; def note_ambiguous_atomic_constraints : Note< "'%0' in the two declarations is not considered equivalent - move it to a " "concept and reference it from here:">; @@ -4336,6 +4385,8 @@ "in instantiation of template type alias %0 requested here">; def note_template_exception_spec_instantiation_here : Note< "in instantiation of exception specification for %0 requested here">; +def note_template_requirement_instantiation_here : Note< + "in instantiation of requirement here">; def warn_var_template_missing : Warning<"instantiation of variable %q0 " "required here, but no definition is available">, InGroup; @@ -4371,6 +4422,8 @@ "while checking a default template argument used here">; def note_concept_specialization_here : Note< "while checking the satisfaction of concept '%0' requested here">; +def note_nested_requirement_here : Note< + "while checking the satisfaction of nested requirement requested here">; def note_checking_constraints_for_template_id_here : Note< "while checking constraint satisfaction for template '%0' required here">; def note_checking_constraints_for_var_spec_id_here : Note< Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -164,6 +164,7 @@ // C++2a Concepts expressions def ConceptSpecializationExpr : DStmt; +def RequiresExpr : DStmt; // Obj-C Expressions. def ObjCStringLiteral : DStmt; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1847,6 +1847,7 @@ //===--------------------------------------------------------------------===// // C++ Concepts + ExprResult ParseRequiresExpression(); void ParseTrailingRequiresClause(Declarator &D); //===--------------------------------------------------------------------===// @@ -2671,7 +2672,7 @@ Declarator &D, SmallVectorImpl &ParamInfo); void ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, SourceLocation &EllipsisLoc); Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -1745,7 +1745,8 @@ TemplateArgContext, // Any template argument (in template argument list). TemplateTypeArgContext, // Template type argument (in default argument). AliasDeclContext, // C++11 alias-declaration. - AliasTemplateContext // C++11 alias-declaration template. + AliasTemplateContext, // C++11 alias-declaration template. + RequiresExprContext // C++2a requires-expression. }; @@ -1969,6 +1970,7 @@ case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::RequiresExprContext: return true; } llvm_unreachable("unknown context kind!"); @@ -1991,6 +1993,7 @@ case DeclaratorContext::TemplateParamContext: case DeclaratorContext::CXXCatchContext: case DeclaratorContext::ObjCCatchContext: + case DeclaratorContext::RequiresExprContext: return true; case DeclaratorContext::TypeNameContext: @@ -2027,6 +2030,7 @@ case DeclaratorContext::MemberContext: case DeclaratorContext::PrototypeContext: case DeclaratorContext::TemplateParamContext: + case DeclaratorContext::RequiresExprContext: // Maybe one day... return false; @@ -2104,6 +2108,7 @@ case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: + case DeclaratorContext::RequiresExprContext: return false; } llvm_unreachable("unknown context kind!"); @@ -2325,6 +2330,7 @@ case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::RequiresExprContext: return false; } llvm_unreachable("unknown context kind!"); @@ -2358,6 +2364,7 @@ case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: case DeclaratorContext::TemplateTypeArgContext: + case DeclaratorContext::RequiresExprContext: return false; case DeclaratorContext::BlockContext: Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5870,8 +5870,11 @@ /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. + /// \param First whether this is the first time an unsatisfied constraint is + /// diagnosed for this error. void - DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction); + DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First = true); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied because it was ill-formed. @@ -6807,10 +6810,41 @@ const TemplateArgument *Args, unsigned NumArgs); - // Concepts - Decl *ActOnConceptDefinition(Scope *S, - MultiTemplateParamsArg TemplateParameterLists, IdentifierInfo *Name, - SourceLocation NameLoc, Expr *ConstraintExpr); + //===--------------------------------------------------------------------===// + // C++ Concepts + //===--------------------------------------------------------------------===// + Decl *ActOnConceptDefinition( + Scope *S, + MultiTemplateParamsArg TemplateParameterLists, + IdentifierInfo *Name, SourceLocation NameLoc, + Expr *ConstraintExpr); + + RequiresExprBodyDecl * + ActOnEnterRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope); + void ActOnExitRequiresExpr(); + Requirement *ActOnSimpleRequirement(Expr *E); + Requirement *ActOnTypeRequirement(SourceLocation TypenameKWLoc, + CXXScopeSpec TypeScope, + UnqualifiedId &TypeName); + Requirement *ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc); + Requirement *ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc, + TypeSourceInfo *ExpectedType); + Requirement *ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc, + SourceLocation ConceptNameLoc, + const CXXScopeSpec &SS, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + TemplateArgumentListInfo TemplateArgs, + Declarator &DeducedDeclarator, + unsigned Depth); + Requirement *ActOnNestedRequirement(Expr *Constraint); + ExprResult CreateRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc); //===--------------------------------------------------------------------===// // C++ Variadic Templates (C++0x [temp.variadic]) @@ -7379,6 +7413,14 @@ bool isTemplateTemplateParameterAtLeastAsSpecializedAs( TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc); + + /// \brief Given a template parameter list containing a single type parameter, + /// the type of a parameter of a hypothetical function template with the + /// given template parameter list, and the argument passed to that function + /// template, attempts to deduce the type parameter. + QualType matchTypeByDeduction(TemplateParameterList *TemplateParams, + QualType ParamType, Expr *Arg); + void MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs, bool OnlyDeduced, unsigned Depth, @@ -7452,6 +7494,13 @@ /// template which was deferred until it was needed. ExceptionSpecInstantiation, + /// We are instantiating a requirement of a requires expression. + RequirementInstantiation, + + /// We are checking the satisfaction of a nested requirement of a requires + /// expression. + NestedRequirementConstraintsCheck, + /// We are declaring an implicit special member function. DeclaringSpecialMember, @@ -7742,6 +7791,19 @@ sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange); + /// \brief Note that we are substituting template arguments into a part of + /// a requirement of a requires expression. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + Requirement *Req, + sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange = SourceRange()); + + /// \brief Note that we are checking the satisfaction of the constraint + /// expression inside of a nested requirement. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + NestedRequirement *Req, ConstraintsCheck, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); Index: include/clang/Sema/SemaConcept.h =================================================================== --- include/clang/Sema/SemaConcept.h +++ include/clang/Sema/SemaConcept.h @@ -22,7 +22,9 @@ #include #include namespace clang { +class ConceptSpecializationExpr; class Sema; + /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. struct ConstraintSatisfaction { @@ -38,6 +40,349 @@ llvm::SmallVector, 4> Details; }; +/// \brief A static requirement that can be used in a requires-expression to +/// check properties of types and expression. +class Requirement { +public: + // Note - simple and compound requirements are both represented by the same + // class (ExprRequirement). + enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested }; +private: + const RequirementKind Kind; + bool Dependent : 1; + bool ContainsUnexpandedParameterPack : 1; + bool Satisfied : 1; +public: + struct SubstitutionDiagnostic { + std::string SubstitutedEntity; + SourceLocation DiagLoc; + std::string DiagMessage; + }; + + Requirement(RequirementKind Kind, bool IsDependent, + bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) : + Kind(Kind), Dependent(IsDependent), + ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack), + Satisfied(IsSatisfied) {} + + RequirementKind getKind() const { return Kind; } + + bool isSatisfied() const { + assert(!Dependent && + "isSatisfied can only be called on non-dependent requirements."); + return Satisfied; + } + + void setSatisfied(bool IsSatisfied) { + assert(!Dependent && + "setSatisfied can only be called on non-dependent requirements."); + Satisfied = IsSatisfied; + } + + void setDependent(bool IsDependent) { Dependent = IsDependent; } + bool isDependent() const { return Dependent; } + + void setContainsUnexpandedParameterPack(bool Contains) { + ContainsUnexpandedParameterPack = Contains; + } + bool containsUnexpandedParameterPack() const { + return ContainsUnexpandedParameterPack; + } + + virtual void Diagnose(Sema &S, bool First) const = 0; + + virtual ~Requirement() = default; +}; + +/// \brief A requires-expression requirement which queries the existence of a +/// type name or type template specialization ('type' requirements). +class TypeRequirement : public Requirement { +public: + enum SatisfactionStatus { + SS_Dependent, + SS_SubstitutionFailure, + SS_Satisfied + }; +private: + llvm::PointerUnion Value; + SatisfactionStatus Status; +public: + friend class ASTStmtReader; + friend class ASTStmtWriter; + + /// \brief Construct a type requirement from a type. If the given type is not + /// dependent, this indicates that the type exists and the requirement will be + /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be + /// used. + TypeRequirement(TypeSourceInfo *T); + + /// \brief Construct a type requirement when the nested name specifier is + /// invalid due to a bad substitution. The requirement is unsatisfied. + TypeRequirement(SubstitutionDiagnostic *Diagnostic) : + Requirement(RK_Type, false, false, false), Value(Diagnostic), + Status(SS_SubstitutionFailure) {} + + SatisfactionStatus getSatisfactionStatus() const { return Status; } + void setSatisfactionStatus(SatisfactionStatus Status) { + this->Status = Status; + } + + bool isSubstitutionFailure() const { + return Status == SS_SubstitutionFailure; + } + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(Status == SS_SubstitutionFailure && + "Attempted to get substitution diagnostic when there has been no " + "substitution failure."); + return Value.get(); + } + + TypeSourceInfo *getType() const { + assert(!isSubstitutionFailure() && + "Attempted to get type when there has been a substitution failure."); + return Value.get(); + } + + void Diagnose(Sema &S, bool First) const override; + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Type; + } +}; + +/// \brief A requires-expression requirement which queries the validity and +/// properties of an expression ('simple' and 'compound' requirements). +class ExprRequirement : public Requirement { +public: + enum SatisfactionStatus { + SS_Dependent, + SS_ExprSubstitutionFailure, + SS_NoexceptNotMet, + SS_TypeRequirementSubstitutionFailure, + SS_ImplicitConversionAmbiguous, + SS_NoImplicitConversionExists, + SS_DeductionFailed, + SS_ConstraintsNotSatisfied, + SS_Satisfied + }; + class ReturnTypeRequirement { + using ConstrainedParam = std::tuple; + bool Dependent : 1; + bool ContainsUnexpandedParameterPack : 1; + llvm::PointerUnion3 Value; + public: + friend class ASTStmtReader; + friend class ASTStmtWriter; + + /// \brief No return type requirement was specified. + ReturnTypeRequirement() : Dependent(false), + ContainsUnexpandedParameterPack(false) {} + + /// \brief A return type requirement was specified but it was a + /// substitution failure. + ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) : + Dependent(false), ContainsUnexpandedParameterPack(false), + Value(SubstDiag) {} + + /// \brief A 'trailing return type' style return type requirement. + ReturnTypeRequirement(ASTContext &C, TypeSourceInfo *ExpectedType); + + /// \brief A 'constrained parameter' style return type requirement. + /// \param TPL an invented template parameter list containing a single + /// type parameter and a requires clause. + /// \param ExpectedType a qualified type referring to the type parameter + /// of TPL. + ReturnTypeRequirement(ASTContext &C, TemplateParameterList *TPL, + TypeSourceInfo *ExpectedType); + + /// \brief A 'constrained parameter' style return type with + /// pre-instantiated constraint expression. Should only be used by + /// ASTStmtReader. + ReturnTypeRequirement(ASTContext &C, TemplateParameterList *TPL, + TypeSourceInfo *ExpectedType, + ConceptSpecializationExpr *CSE); + + bool isDependent() const { return Dependent; } + + bool containsUnexpandedParameterPack() const { + return ContainsUnexpandedParameterPack; + } + + bool isEmpty() const { return Value.isNull(); } + + bool isSubstitutionFailure() const { + return Value && Value.is(); + } + + bool isConstrainedParameter() const { + return Value && Value.is(); + } + + bool isTrailingReturnType() const { + return Value && Value.is(); + } + + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(isSubstitutionFailure()); + return Value.get(); + } + + TypeSourceInfo *getTrailingReturnTypeExpectedType() const { + assert(isTrailingReturnType()); + return Value.get(); + } + + TypeSourceInfo *getConstrainedParamExpectedType() const { + assert(isConstrainedParameter()); + return std::get<0>(*Value.get()); + } + + TemplateParameterList *getConstrainedParamTemplateParameterList() const { + assert(isConstrainedParameter()); + return std::get<1>(*Value.get()); + } + + ConceptSpecializationExpr *getConstrainedParamConstraintExpr() const { + assert(isConstrainedParameter()); + return std::get<2>(*Value.get()); + } + + SatisfactionStatus calculateSatisfaction(Sema &S, Expr *E); + }; +private: + llvm::PointerUnion Value; + SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified. + ReturnTypeRequirement TypeReq; + SatisfactionStatus Status; +public: + friend class ASTStmtReader; + friend class ASTStmtWriter; + + /// \brief Construct a compound requirement, employing semantic analysis to + /// determine its satisfaction status. + /// \param E the expression which is checked by this requirement. + /// \param IsSimple whether this was a simple requirement in source. + /// \param NoexceptLoc the location of the noexcept keyword, if it was + /// specified, otherwise an empty location. + /// \param Req the requirement for the type of the checked expression (omit + /// if no requirement was specified). + ExprRequirement(Sema &S, Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req = {}); + + /// \brief Construct a compound requirement with a predetermined satisfaction + /// status. + /// \param E the expression which is checked by this requirement. + /// \param IsSimple whether this was a simple requirement in source. + /// \param NoexceptLoc the location of the noexcept keyword, if it was + /// specified, otherwise an empty location. + /// \param Req the requirement for the type of the checked expression. + ExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, SatisfactionStatus Status); + + /// \brief Construct a compound requirement whose expression was a + /// substitution failure. The requirement is not satisfied. + /// \param E the diagnostic emitted while instantiating the original + /// expression. + /// \param IsSimple whether this was a simple requirement in source. + /// \param NoexceptLoc the location of the noexcept keyword, if it was + /// specified, otherwise an empty location. + /// \param Req the requirement for the type of the checked expression (omit + /// if no requirement was specified). + ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple, + SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {}); + + bool isSimple() const { return getKind() == RK_Simple; } + bool isCompound() const { return getKind() == RK_Compound; } + + bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); } + SourceLocation getNoexceptLoc() const { return NoexceptLoc; } + + SatisfactionStatus getSatisfactionStatus() const { return Status; } + + bool isExprSubstitutionFailure() const { + return Status == SS_ExprSubstitutionFailure; + } + + const ReturnTypeRequirement &getReturnTypeRequirement() const { + return TypeReq; + } + + SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const { + assert(isExprSubstitutionFailure() && + "Attempted to get expression substitution diagnostic when there has " + "been no expression substitution failure"); + return Value.get(); + } + + Expr *getExpr() const { + assert(!isExprSubstitutionFailure() && + "ExprRequirement has no expression because there has been a " + "substitution failure."); + return Value.get(); + } + + void Diagnose(Sema &S, bool First) const override; + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Compound || R->getKind() == RK_Simple; + } +}; + +/// \brief A requires-expression requirement which is satisfied when a general +/// constraint expression is satisfied ('nested' requirements). +class NestedRequirement : public Requirement { + llvm::PointerUnion Value; + ConstraintSatisfaction Satisfaction; + +public: + friend class ASTStmtReader; + friend class ASTStmtWriter; + + NestedRequirement(SubstitutionDiagnostic *SubstDiag) : + Requirement(RK_Nested, /*Dependent=*/false, + /*ContainsUnexpandedParameterPack*/false, + /*Satisfied=*/false), Value(SubstDiag) {} + + NestedRequirement(Expr *Constraint) : + Requirement(RK_Nested, /*Dependent=*/true, + Constraint->containsUnexpandedParameterPack()), + Value(Constraint) { + assert(Constraint->isInstantiationDependent() && + "Nested requirement with Non-dependent constraint must be " + "constructed with a Sema& or a ConstraintSatisfaction object"); + } + NestedRequirement(Sema &S, Expr *Constraint); + NestedRequirement(Expr *Constraint, ConstraintSatisfaction Satisfaction) : + Requirement(RK_Nested, false, false, Satisfaction.IsSatisfied), + Value(Constraint), Satisfaction(std::move(Satisfaction)) {} + + bool isSubstitutionFailure() const { + return Value.is(); + } + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(isSubstitutionFailure() && + "getSubstitutionDiagnostic() may not be called when there was no " + "substitution failure."); + return Value.get(); + } + + Expr *getConstraintExpr() const { + assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called " + "on nested requirements with " + "substitution failures."); + return Value.get(); + } + + void Diagnose(Sema &S, bool First) const override; + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Nested; + } +}; + } // clang #endif \ No newline at end of file Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1907,6 +1907,7 @@ EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr + EXPR_REQUIRES, // RequiresExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -817,6 +817,7 @@ case OMPRequires: case OMPCapturedExpr: case Empty: + case RequiresExprBody: // Never looked up by name. return 0; } @@ -1169,6 +1170,7 @@ case Decl::Captured: case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: + case Decl::RequiresExprBody: // There is only one DeclContext for these entities. return this; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -1877,6 +1877,16 @@ nullptr, SourceLocation()); } +RequiresExprBodyDecl *RequiresExprBodyDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc) { + return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc); +} + +RequiresExprBodyDecl *RequiresExprBodyDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation()); +} + void CXXMethodDecl::anchor() {} bool CXXMethodDecl::isStatic() const { Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -3156,6 +3156,7 @@ case CXXUuidofExprClass: case OpaqueValueExprClass: case ConceptSpecializationExprClass: + case RequiresExprClass: // These never have a side-effect. return false; Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/LambdaCapture.h" @@ -31,6 +32,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/Sema.h" +#include "clang/Sema/Overload.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -38,6 +40,7 @@ #include #include #include +#include using namespace clang; @@ -1741,4 +1744,63 @@ void *Buffer = C.Allocate(totalSizeToAlloc( NumTemplateArgs)); return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +} + +RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc) + : Expr(RequiresExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TD=*/false, /*VD=*/false, /*ID=*/false, + /*ContainsUnexpandedParameterPack=*/false), IsSatisfied(false), + RequiresKWLoc(RequiresKWLoc), Body(Body), RBraceLoc(RBraceLoc) { + setLocalParameters(LocalParameters); + setRequirements(Requirements); +} + +void RequiresExpr::setLocalParameters(ArrayRef LocalParameters) { + assert(this->LocalParameters.empty() && !isInstantiationDependent() + && !containsUnexpandedParameterPack() && + "setLocalParameters must be called before setRequirements and may not " + "be called twice"); + bool Dependent = false; + bool ContainsUnexpandedParameterPack = false; + for (ParmVarDecl *P : LocalParameters) { + Dependent |= P->getType()->isInstantiationDependentType(); + ContainsUnexpandedParameterPack |= + P->getType()->containsUnexpandedParameterPack(); + } + this->LocalParameters.assign(LocalParameters.begin(), LocalParameters.end()); + setValueDependent(Dependent); + setInstantiationDependent(Dependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); +} + +void RequiresExpr::setRequirements(ArrayRef Requirements) { + assert(this->Requirements.empty() && "setRequirements may only be called " + "once per object."); + bool Dependent = false; + bool ContainsUnexpandedParameterPack = false; + IsSatisfied = true; + for (Requirement *R : Requirements) { + Dependent |= R->isDependent(); + ContainsUnexpandedParameterPack |= R->containsUnexpandedParameterPack(); + if (!Dependent) { + IsSatisfied = R->isSatisfied(); + if (!IsSatisfied) + break; + } + } + this->Requirements.assign(Requirements.begin(), Requirements.end()); + IsSatisfied |= Dependent; + // SetLocalParameters may have been called and may have updated the + // flags + if (!isInstantiationDependent() && Dependent) { + setValueDependent(Dependent); + setInstantiationDependent(Dependent); + } + if (!containsUnexpandedParameterPack() && ContainsUnexpandedParameterPack) { + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + } } \ No newline at end of file Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -192,6 +192,7 @@ case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -7492,6 +7492,7 @@ bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + bool VisitRequiresExpr(const RequiresExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -9948,6 +9949,13 @@ return Success(E->isSatisfied(), E); } +bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) { + if (E->isValueDependent()) { + return Error(E); + } + return Success(E->isSatisfied(), E); +} + bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { @@ -11419,6 +11427,7 @@ case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3558,6 +3558,7 @@ case Expr::StmtExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2198,6 +2198,102 @@ Policy); } +void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { + OS << "requires "; + auto LocalParameters = E->getLocalParameters(); + if (!LocalParameters.empty()) { + OS << "("; + for (ParmVarDecl *LocalParam : LocalParameters) { + PrintRawDecl(LocalParam); + if (LocalParam != LocalParameters.back()) + OS << ", "; + } + + OS << ") "; + } + OS << "{ "; + auto Requirements = E->getRequirements(); + for (Requirement *Req : Requirements) { + if (auto *TypeReq = dyn_cast(Req)) { + OS << "typename "; + if (TypeReq->isSubstitutionFailure()) + OS << "<>"; + else + TypeReq->getType()->getType().print(OS, Policy); + } else if (auto *ExprReq = dyn_cast(Req)) { + if (ExprReq->isCompound()) + OS << "{ "; + if (ExprReq->isExprSubstitutionFailure()) + OS << "<>"; + else + PrintExpr(ExprReq->getExpr()); + if (ExprReq->isCompound()) { + OS << " }"; + if (ExprReq->getNoexceptLoc().isValid()) + OS << " noexcept"; + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (!RetReq.isEmpty()) { + OS << " -> "; + if (RetReq.isSubstitutionFailure()) + OS << "<>"; + else if (RetReq.isTrailingReturnType()) + RetReq.getTrailingReturnTypeExpectedType()->getType().print(OS, + Policy); + else if (RetReq.isConstrainedParameter()) { + // This is kind of hacky - print out the type name, and replace its + // name with the constraint expr, omitting the first argument + // "const volatile &" + // -> "const volatile ConstructibleWith&" + // The invented template parameter should therefore have a + // non-user-typable name. + std::string ConstraintExprBuf; + llvm::raw_string_ostream ConstraintExprOS(ConstraintExprBuf); + auto *CSE = cast( + RetReq.getConstrainedParamTemplateParameterList() + ->getRequiresClause()); + ConstraintExprOS << CSE->getNamedConcept()->getName(); + if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs > 1) { + ConstraintExprOS << "<"; + for (unsigned I = 1, + C = CSE->getTemplateArgsAsWritten()->NumTemplateArgs; + I < C; ++I) { + CSE->getTemplateArgsAsWritten()->arguments()[I].getArgument() + .print(Policy, ConstraintExprOS); + if (I != 1) + ConstraintExprOS << ", "; + } + ConstraintExprOS << ">"; + } + ConstraintExprOS.flush(); + + std::string ExpectedTypeBuf; + llvm::raw_string_ostream ExpectedTypeOS(ExpectedTypeBuf); + RetReq.getConstrainedParamExpectedType()->getType() + .print(ExpectedTypeOS, Policy); + ExpectedTypeOS.flush(); + StringRef InventedParamName = + RetReq.getConstrainedParamTemplateParameterList()->getParam(0) + ->getName(); + ExpectedTypeBuf.replace(ExpectedTypeBuf.find(InventedParamName), + InventedParamName.size(), + ConstraintExprBuf); + OS << ExpectedTypeBuf; + } + } + } + } else { + auto *NestedReq = cast(Req); + OS << "requires "; + if (NestedReq->isSubstitutionFailure()) + OS << "<>"; + else + PrintExpr(NestedReq->getConstraintExpr()); + } + OS << "; "; + } + OS << "}"; +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1302,6 +1302,54 @@ S->getTemplateArgsAsWritten()->NumTemplateArgs); } +void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getLocalParameters().size()); + for (ParmVarDecl *LocalParam : S->getLocalParameters()) + VisitDecl(LocalParam); + ID.AddInteger(S->getRequirements().size()); + for (Requirement *Req : S->getRequirements()) { + if (auto *TypeReq = dyn_cast(Req)) { + ID.AddInteger(Requirement::RK_Type); + ID.AddBoolean(TypeReq->isSubstitutionFailure()); + if (!TypeReq->isSubstitutionFailure()) + VisitType(TypeReq->getType()->getType()); + } else if (auto *ExprReq = dyn_cast(Req)) { + ID.AddInteger(Requirement::RK_Compound); + ID.AddBoolean(ExprReq->isExprSubstitutionFailure()); + if (!ExprReq->isExprSubstitutionFailure()) + Visit(ExprReq->getExpr()); + // C++2a [expr.prim.req.compound]p1 Example: + // [...] The compound-requirement in C1 requires that x++ is a valid + // expression. It is equivalent to the simple-requirement x++; [...] + // We therefore do not profile isSimple() here. + ID.AddBoolean(ExprReq->getNoexceptLoc().isValid()); + const ExprRequirement::ReturnTypeRequirement &RetReq = + ExprReq->getReturnTypeRequirement(); + if (RetReq.isEmpty()) { + ID.AddInteger(0); + } else if (RetReq.isTrailingReturnType()) { + ID.AddInteger(1); + VisitType(RetReq.getTrailingReturnTypeExpectedType()->getType()); + } else if (RetReq.isConstrainedParameter()) { + ID.AddInteger(2); + Visit(RetReq.getConstrainedParamTemplateParameterList() + ->getRequiresClause()); + VisitType(RetReq.getConstrainedParamExpectedType()->getType()); + } else { + assert(RetReq.isSubstitutionFailure()); + ID.AddInteger(3); + } + } else { + ID.AddInteger(Requirement::RK_Nested); + auto *NestedReq = cast(Req); + ID.AddBoolean(NestedReq->isSubstitutionFailure()); + if (!NestedReq->isSubstitutionFailure()) + Visit(NestedReq->getConstraintExpr()); + } + } +} + static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -108,6 +108,7 @@ case Decl::OMPRequires: case Decl::Empty: case Decl::Concept: + case Decl::RequiresExprBody: // None of these decls require codegen support. return; Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -669,6 +669,10 @@ return Builder.getInt1(E->isSatisfied()); } + Value *VisitRequiresExpr(const RequiresExpr *E) { + return Builder.getInt1(E->isSatisfied()); + } + Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue()); } Index: lib/Frontend/FrontendActions.cpp =================================================================== --- lib/Frontend/FrontendActions.cpp +++ lib/Frontend/FrontendActions.cpp @@ -420,6 +420,10 @@ return "ConstraintsCheck"; case CodeSynthesisContext::ConstraintSubstitution: return "ConstraintSubstitution"; + case CodeSynthesisContext::RequirementInstantiation: + return "RequirementInstantiation"; + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + return "NestedRequirementConstraintsCheck"; } return ""; } Index: lib/Frontend/InitPreprocessor.cpp =================================================================== --- lib/Frontend/InitPreprocessor.cpp +++ lib/Frontend/InitPreprocessor.cpp @@ -384,6 +384,9 @@ else Builder.defineMacro("__cplusplus", "199711L"); + if (LangOpts.ConceptsTS) + Builder.defineMacro("__cpp_concepts", "201707L"); + // C++1z [cpp.predefined]p1: // An integer literal of type std::size_t whose value is the alignment // guaranteed by a call to operator new(std::size_t) Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -6166,7 +6166,7 @@ ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6419,9 +6419,9 @@ /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6447,7 +6447,7 @@ /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6483,9 +6483,11 @@ // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorContext == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorContext == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6538,7 +6540,7 @@ SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorContext == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -751,6 +751,7 @@ /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -1567,6 +1568,9 @@ return ExprError(); return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw_requires: // [C++2a] requires-expression + return ParseRequiresExpression(); + case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (EnsureNotPrimary()) return ExprError(); Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -1147,8 +1149,9 @@ if (Tok.isNot(tok::r_paren)) { Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - // For a generic lambda, each 'auto' within the parameter declaration + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. if (Actions.getCurGenericLambda()) ++CurTemplateDepthTracker; @@ -3015,6 +3018,364 @@ return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector LocalParameters; + DiagnosticErrorTrap Trap(Diags); + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast(ParamInfo.Param)); + if (Trap.hasErrorOccurred()) + SkipUntil(tok::r_paren, StopBeforeMatch); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + Braces.consumeClose(); + return ExprError(); + } + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnEnterRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + while (!Tok.is(tok::r_brace)) { + auto FindSemi = [&] { + RevertingTentativeParsingAction RTPA(*this); + if (SkipUntil(tok::semi, StopBeforeMatch)) + return Tok.getLocation(); + ExpectAndConsumeSemi(diag::err_expected_semi_requirement); + return SourceLocation(); + }; + switch (Tok.getKind()) { + case tok::kw_typename: { + // Type requirement + // C++ [expr.prim.req.type] + // type-requirement: + // 'typename' nested-name-specifier[opt] type-name ';' + SourceLocation TypenameKWLoc = ConsumeToken(); + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/true) || + SS.isInvalid()) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + SourceLocation TemplateKWLoc; + UnqualifiedId TypeName; + SourceLocation IdLoc = Tok.getLocation(); + if (ParseUnqualifiedId(SS, /*EnteringContext=*/false, + /*AllowDestructorName*/false, + /*AllowConstructorName=*/false, + /*AllowDeductionGuide=*/false, ParsedType(), + &TemplateKWLoc, TypeName)) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if ((TemplateKWLoc.isValid() && + TypeName.getKind() != UnqualifiedIdKind::IK_TemplateId) || + (TemplateKWLoc.isInvalid() && + TypeName.getKind() != UnqualifiedIdKind::IK_Identifier && + TypeName.getKind() != UnqualifiedIdKind::IK_TemplateId)) { + Diag(IdLoc, diag::err_requires_expr_type_req_illegal_identifier); + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if (TypeName.getKind() == UnqualifiedIdKind::IK_Identifier && + Tok.is(tok::less)) { + SourceLocation ArgListLoc = ConsumeToken(); + TemplateArgList List; + bool IsTAL; + { + bool WasSuppressed = Diags.getSuppressAllDiagnostics(); + Diags.setSuppressAllDiagnostics(); + RevertingTentativeParsingAction TPA(*this); + GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); + IsTAL = !ParseTemplateArgumentList(List) && + TryConsumeToken(tok::greater); + Diags.setSuppressAllDiagnostics(WasSuppressed); + } + if (IsTAL) { + // Something like typename X where X is not a template - the + // template arguments were not parsed as part of the unqualified id, + // so they are left here. + Diag(ArgListLoc, + diag::err_requires_expr_type_req_template_args_on_non_template) + << TypeName.Identifier->getName(); + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + // This is just an extraneaous '<', handle it below. + } + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, std::move(SS), + TypeName)) + Requirements.push_back(Req); + else { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + break; + } + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (Expression.isInvalid() && !Expression.isUsable()) { + ExprBraces.skipToEnd(); + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (!Req) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + Requirements.push_back(Req); + break; + } + if (!Tok.is(tok::arrow)) { + // User probably forgot the arrow, remind him and try to continue + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + // Try to parse a 'constrained-parameter' requirement first. + auto TryConstrainedParameter = [&] { + TentativeParsingAction TPA(*this); + DeclSpec DS(AttrFactory); + ConsumeToken(); // consume '->' + auto ParseCV = [&] { + unsigned DiagID = 0; + const char *PrevSpec = nullptr; + while (true) + if (TryConsumeToken(tok::kw_const)) { + if (DS.SetTypeQual(DeclSpec::TQ_const, Tok.getLocation(), + PrevSpec, DiagID, getLangOpts())) + Diag(PrevTokLocation, DiagID) << PrevSpec + << FixItHint::CreateRemoval(SourceRange(PrevTokLocation)); + } else if (TryConsumeToken(tok::kw_volatile)) { + if (DS.SetTypeQual(DeclSpec::TQ_volatile, Tok.getLocation(), + PrevSpec, DiagID, getLangOpts())) + Diag(PrevTokLocation, DiagID) << PrevSpec + << FixItHint::CreateRemoval(SourceRange(PrevTokLocation)); + } else + break; + }; + ParseCV(); + CXXScopeSpec SS; + ConceptDecl *CD; + NamedDecl *FoundDecl; + SourceLocation ConceptNameLoc; + TemplateArgumentListInfo TALI; + if (!TryParseConstrainedParameter(SS, CD, FoundDecl, ConceptNameLoc, + TALI)) { + TPA.Revert(); + return false; + } + TPA.Commit(); + if (!CD) + // An error occured while parsing the constrained parameter. + return true; + NamedDecl *ConceptPrototypeParm = CD->getTemplateParameters() + ->getParam(0); + if (!isa(ConceptPrototypeParm)) { + Diag(Tok, + diag::err_requires_expr_compound_requirement_non_type_concept) + << CD + << (isa(ConceptPrototypeParm) ? 0 + : 1); + return true; + } + ParseCV(); + + // Parse the abstract-declarator, if present. + Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext); + ParseDeclarator(DeclaratorInfo); + + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc, + ConceptNameLoc, SS, FoundDecl, + CD, TALI, DeclaratorInfo, + TemplateParameterDepth); + return true; + }; + + if (!TryConstrainedParameter()) { + SourceRange Range(Tok.getLocation(), FindSemi()); + TypeResult ExpectedType = + ParseTrailingReturnType(Range, /*MayBeFollowedByDirectInit=*/false); + if (!ExpectedType.isUsable() || ExpectedType.isInvalid()) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + auto *LIT = cast(ExpectedType.get().get().getTypePtr()); + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc, + LIT->getTypeSourceInfo()); + } + if (!Req) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + Requirements.push_back(Req); + break; + } + case tok::kw_requires: { + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ConsumeToken(); + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if (auto *Req = Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + break; + } + default: { + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (Expression.isInvalid() || !Expression.isUsable()) { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + if (!Tok.is(tok::semi) && !Tok.is(tok::r_brace)) { + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + else + Diag(Tok, diag::err_requires_expr_simple_requirement_unexpected_tok) + << Tok.getKind() << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + Braces.skipToEnd(); + Actions.ActOnExitRequiresExpr(); + return ExprError(); + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) + break; + } + Braces.consumeClose(); + Actions.ActOnExitRequiresExpr(); + return Actions.CreateRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); Index: lib/Sema/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -14,8 +14,12 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/SemaInternal.h" #include "clang/AST/ExprCXX.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -414,6 +418,13 @@ } S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; + } else if (auto *RE = dyn_cast(SubstExpr)) { + for (Requirement *Req : RE->getRequirements()) + if (!Req->isDependent() && !Req->isSatisfied()) { + Req->Diagnose(S, First); + break; + } + return; } S.Diag(SubstExpr->getSourceRange().getBegin(), @@ -434,11 +445,11 @@ diagnoseWellFormedUnsatisfiedConstraintExpr(S, Detail.get(), First); } -void Sema::DiagnoseUnsatisfiedConstraint( - const ConstraintSatisfaction& Satisfaction) { +void +Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction.Details) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -968,3 +979,319 @@ << AmbiguousAtomic2->getSourceRange(); return true; } + + + +ExprRequirement::ExprRequirement(Sema &S, Expr *E, bool IsSimple, + SourceLocation NoexceptLoc, + ReturnTypeRequirement Req) : + Requirement(IsSimple ? RK_Simple : RK_Compound, + E->isInstantiationDependent() || Req.isDependent(), + E->containsUnexpandedParameterPack() || + Req.containsUnexpandedParameterPack(), false), Value(E), + NoexceptLoc(NoexceptLoc), TypeReq(Req) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); + if (isDependent()) { + Status = SS_Dependent; + return; + } + if (NoexceptLoc.isValid() && S.canThrow(E) == CanThrowResult::CT_Can) { + Status = SS_NoexceptNotMet; + setSatisfied(false); + return; + } + Status = TypeReq.calculateSatisfaction(S, E); + setSatisfied(Status == SS_Satisfied); +} + +ExprRequirement::ExprRequirement(Expr *E, bool IsSimple, + SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, + SatisfactionStatus Status) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent, + Status == SS_Dependent && + (E->containsUnexpandedParameterPack() || + Req.containsUnexpandedParameterPack()), + Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc), + TypeReq(Req), Status(Status) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); +} + +ExprRequirement::ExprRequirement(SubstitutionDiagnostic *ExprSubstDiag, + bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(), + Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false), + Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req), + Status(SS_ExprSubstitutionFailure) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); +} + +ExprRequirement::ReturnTypeRequirement::ReturnTypeRequirement(ASTContext &C, + TypeSourceInfo *ExpectedType) : + Dependent(ExpectedType->getType()->isInstantiationDependentType()), + ContainsUnexpandedParameterPack( + ExpectedType->getType()->containsUnexpandedParameterPack()), + Value(ExpectedType) {} + +class NonInventedTemplateParameterFinder : + public RecursiveASTVisitor { +public: + NonInventedTemplateParameterFinder(const TemplateTypeParmType *InventedType) : + InventedType(InventedType) {} + + bool containsNonInventedTemplateParameter(QualType T) { + return !TraverseType(T); + } + + bool containsNonInventedTemplateParameter(Expr *E) { + return !TraverseStmt(E); + } + + bool TraverseTemplateTypeParmType(TemplateTypeParmType *T) { + return T == InventedType; + } + +private: + const TemplateTypeParmType *InventedType; +}; + +ExprRequirement::ReturnTypeRequirement::ReturnTypeRequirement(ASTContext &C, + TemplateParameterList *TPL, TypeSourceInfo *ExpectedType) : + Dependent(false), + ContainsUnexpandedParameterPack( + TPL->getRequiresClause()->containsUnexpandedParameterPack() || + ExpectedType->getType()->containsUnexpandedParameterPack()), + Value(new (C) ConstrainedParam(ExpectedType, TPL, nullptr)) { + assert(TPL->size() == 1 && + isa(TPL->getRequiresClause()) && + "Provided template parameter list is ill-formed (must contain " + "exactly one type parameter and have a concept specialization " + "expression for a requires clause"); + // TODO: Is this really necessary? + NonInventedTemplateParameterFinder Finder( + cast( + cast(TPL->getParam(0))->getTypeForDecl())); + Dependent = Finder.containsNonInventedTemplateParameter(ExpectedType + ->getType()) || + Finder.containsNonInventedTemplateParameter(TPL->getRequiresClause()); +} + +ExprRequirement::ReturnTypeRequirement::ReturnTypeRequirement(ASTContext &C, + TemplateParameterList *TPL, TypeSourceInfo *ExpectedType, + ConceptSpecializationExpr *CSE) : + Dependent(TPL->getRequiresClause()->isInstantiationDependent() || + ExpectedType->getType()->isInstantiationDependentType()), + ContainsUnexpandedParameterPack( + TPL->getRequiresClause()->containsUnexpandedParameterPack() || + ExpectedType->getType()->containsUnexpandedParameterPack()), + Value(new (C) ConstrainedParam(ExpectedType, TPL, CSE)) {} + +ExprRequirement::SatisfactionStatus +ExprRequirement::ReturnTypeRequirement::calculateSatisfaction(Sema &S, + Expr *E) { + if (!Value) + return SS_Satisfied; + if (Value.is()) + return SS_TypeRequirementSubstitutionFailure; + if (auto *TypeReq = Value.dyn_cast()) { + InitializedEntity InventedEntity = + InitializedEntity::InitializeResult(TypeReq->getTypeLoc().getBeginLoc(), + TypeReq->getType(), /*NRVO=*/false); + InitializationSequence Seq(S, InventedEntity, + InitializationKind::CreateCopy(E->getBeginLoc(), + TypeReq->getTypeLoc().getBeginLoc()), E); + if (Seq.isAmbiguous()) + return SS_ImplicitConversionAmbiguous; + if (Seq.Failed()) + return SS_NoImplicitConversionExists; + return SS_Satisfied; + } + auto *Constrained = Value.get(); + TemplateParameterList *TPL = std::get<1>(*Constrained); + QualType MatchedType = S.matchTypeByDeduction(TPL, + std::get<0>(*Constrained)->getType().getCanonicalType(), E); + if (MatchedType.isNull()) + return SS_DeductionFailed; + llvm::SmallVector Args; + Args.push_back(TemplateArgument(MatchedType)); + TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args); + MultiLevelTemplateArgumentList MLTAL(TAL); + for (unsigned I = 0; I < TPL->getDepth(); ++I) + MLTAL.addOuterRetainedLevel(); + ExprResult Constraint = S.SubstExpr(TPL->getRequiresClause(), MLTAL); + assert(!Constraint.isInvalid() && Constraint.isUsable() && + "Substitution cannot fail as it is simply putting a type template " + "argument into a concept specialization expression's parameter."); + + auto *CSE = cast(Constraint.get()); + std::get<2>(*Constrained) = CSE; + if (!CSE->isSatisfied()) + return SS_ConstraintsNotSatisfied; + return SS_Satisfied; +} + +void ExprRequirement::Diagnose(Sema &S, bool First) const { + assert(!isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (getSatisfactionStatus()) { + case SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case SS_ExprSubstitutionFailure: { + auto *SubstDiag = getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case SS_NoexceptNotMet: + S.Diag(getNoexceptLoc(), diag::note_expr_requirement_noexcept_not_met) + << (int)First << getExpr(); + break; + case SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = TypeReq.getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case SS_ImplicitConversionAmbiguous: + S.Diag(TypeReq.getTrailingReturnTypeExpectedType() + ->getTypeLoc().getBeginLoc(), + diag::note_expr_requirement_ambiguous_conversion) << (int)First + << getExpr()->getType() + << TypeReq.getTrailingReturnTypeExpectedType() + ->getType(); + break; + case SS_NoImplicitConversionExists: + S.Diag(TypeReq.getTrailingReturnTypeExpectedType() + ->getTypeLoc().getBeginLoc(), + diag::note_expr_requirement_no_implicit_conversion) << (int)First + << getExpr()->getType() + << TypeReq.getTrailingReturnTypeExpectedType() + ->getType(); + break; + case SS_DeductionFailed: { + std::string ExpectedTypeBuf; + llvm::raw_string_ostream ExpectedTypeOS(ExpectedTypeBuf); + TypeReq.getConstrainedParamExpectedType()->getType() + .print(ExpectedTypeOS, S.getPrintingPolicy()); + ExpectedTypeOS.flush(); + StringRef InventedParamName = + TypeReq.getConstrainedParamTemplateParameterList()->getParam(0) + ->getName(); + ExpectedTypeBuf.replace(ExpectedTypeBuf.find(InventedParamName), + InventedParamName.size(), ""); + S.Diag(TypeReq.getConstrainedParamExpectedType() + ->getTypeLoc().getBeginLoc(), + diag::note_expr_requirement_deduction_failed) << (int)First + << getExpr()->getType() << ExpectedTypeBuf; + break; + } + case SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + TypeReq.getConstrainedParamConstraintExpr(); + if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1 && + TypeReq.getConstrainedParamExpectedType()->getType() + == QualType( + cast( + TypeReq.getConstrainedParamTemplateParameterList()->getParam(0)) + ->getTypeForDecl(), 0)) + // A simple case - expr type is the type being constrained and the concept + // was not provided arguments. + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied_simple) + << (int)First << getExpr()->getType() + << ConstraintExpr->getNamedConcept(); + else + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied) + << (int)First << ConstraintExpr; + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); + break; + } + case SS_Satisfied: + llvm_unreachable("We checked this above"); + } +} + +TypeRequirement::TypeRequirement(TypeSourceInfo *T) : + Requirement(RK_Type, T->getType()->isDependentType(), + T->getType()->containsUnexpandedParameterPack(), + /*IsSatisfied=*/true // We reach this ctor with either dependent + // types (in which IsSatisfied doesn't + // matter) or with non-dependent type in + // which the existance of the type + // indicates satisfaction. + ), Value(T), + Status(T->getType()->isDependentType() ? SS_Dependent : SS_Satisfied) {} + +void TypeRequirement::Diagnose(Sema &S, bool First) const { + assert(!isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (getSatisfactionStatus()) { + case SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + return; + case SS_SubstitutionFailure: { + auto *SubstDiag = getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + default: + llvm_unreachable("Unknown satisfaction status"); + return; + } +} + +NestedRequirement::NestedRequirement(Sema &S, Expr *Constraint) : + Requirement(RK_Nested, Constraint->isInstantiationDependent(), + Constraint->containsUnexpandedParameterPack(), + /*Satisfied=*/false), Value(Constraint) { + if (isDependent()) + return; + S.CheckConstraintSatisfaction(Constraint, Satisfaction); + setSatisfied(Satisfaction.IsSatisfied); +} + +void NestedRequirement::Diagnose(Sema &S, bool First) const { + if (isSubstitutionFailure()) { + auto *SubstDiag = getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + S.DiagnoseUnsatisfiedConstraint(Satisfaction, First); +} \ No newline at end of file Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -6231,6 +6231,8 @@ return true; if (DC->isRecord()) return false; + if (isa(DC)) + return false; llvm_unreachable("Unexpected context"); } Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1290,6 +1290,7 @@ case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: // These expressions can never throw. return CT_Cannot; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -1806,6 +1806,8 @@ return BuildDeclRefExpr(D, Ty, VK, NameInfo, SS); } +static bool isEvaluatableContext(Sema &SemaRef); + /// BuildDeclRefExpr - Build an expression that references a /// declaration that does not require a closure capture. ExprResult @@ -1817,6 +1819,18 @@ isa(D) && NeedToCaptureVariable(cast(D), NameInfo.getLoc()); + if (isa(D) && isa(D->getDeclContext()) && + !isUnevaluatedContext()) { + // C++ [expr.prim.req.nested] p3 + // A local parameter shall only appear as an unevaluated operand + // (Clause 8) within the constraint-expression. + Diag(NameInfo.getBeginLoc(), + diag::err_requires_expr_parameter_referenced_in_evaluated_context) + << D; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return ExprError(); + } + DeclRefExpr *E; if (isa(D)) { VarTemplateSpecializationDecl *VarSpec = Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -7934,3 +7934,247 @@ return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } + +Requirement *Sema::ActOnSimpleRequirement(Expr *E) { + return new (Context) ExprRequirement(*this, E, /*IsSimple=*/true, + /*NoexceptLoc=*/SourceLocation()); +} + +Requirement *Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, + CXXScopeSpec TypeScope, + UnqualifiedId &TypeName) { + llvm::Optional TALI; + assert((TypeName.getKind() == UnqualifiedIdKind::IK_TemplateId || + TypeName.getKind() == UnqualifiedIdKind::IK_Identifier) && + "Only template-id or identifier allowed in type requirement."); + + TemplateArgumentListInfo TemplateArgsBuffer; + + // Decompose the UnqualifiedId into the following data. + DeclarationNameInfo NameInfo; + const TemplateArgumentListInfo *TemplateArgs; + DecomposeUnqualifiedId(TypeName, TemplateArgsBuffer, NameInfo, TemplateArgs); + + QualType T; + if (TemplateArgs) { + llvm::Optional TempName; + if (TypeScope.isSet()) { + UnqualifiedId TemplateId; + TemplateId.setIdentifier(NameInfo.getName().getAsIdentifierInfo(), + NameInfo.getBeginLoc()); + TemplateTy TemplateType; + auto TNK = ActOnDependentTemplateName(CurScope, TypeScope, + SourceLocation(), + TemplateId, ParsedType(), + /*EnteringContext=*/false, + TemplateType); + if (TNK != TNK_Type_template && TNK != TNK_Dependent_template_name) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + TemplateType.get().print(OS, getPrintingPolicy()); + Diag(NameInfo.getBeginLoc(), + diag::err_type_requirement_non_type_template) << OS.str() + << (int)getTemplateNameKindForDiagnostics(TemplateType.get()) + << NameInfo.getSourceRange(); + return nullptr; + } + TempName.emplace(TemplateType.get()); + } else { + LookupResult R(*this, NameInfo, LookupOrdinaryName); + bool MemberOfUnknownSpecialization = false; + LookupTemplateName(R, CurScope, TypeScope, /*ObjectType=*/QualType(), + /*EnteringContext=*/false, + MemberOfUnknownSpecialization); + assert(!MemberOfUnknownSpecialization + && "How did this happen without a type scope"); + + if (!R.isSingleResult()) + return nullptr; + TemplateDecl *Result = R.getAsSingle(); + if (TemplateDecl *TypeTemplate = getAsTypeTemplateDecl(Result)) + TempName.emplace(TypeTemplate); + else { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + Result->printQualifiedName(OS); + Diag(NameInfo.getBeginLoc(), + diag::err_type_requirement_non_type_template) << OS.str() + << (int)getTemplateNameKindForDiagnostics(TemplateName(Result)) + << NameInfo.getSourceRange(); + return nullptr; + } + } + T = CheckTemplateIdType(TempName.getValue(), NameInfo.getBeginLoc(), + TemplateArgsBuffer); + } else if (TypeScope.isSet() && isDependentScopeSpecifier(TypeScope)) + T = ActOnTypenameType(CurScope, SourceLocation(), TypeScope, + *NameInfo.getName().getAsIdentifierInfo(), + NameInfo.getBeginLoc()).get().get(); + else { + ParsedType Type = getTypeName(*NameInfo.getName().getAsIdentifierInfo(), + NameInfo.getBeginLoc(), CurScope, + TypeScope.isSet() ? &TypeScope : nullptr, + /*isClassName=*/false, + /*HasTrailingDot=*/false, + /*ObjectType=*/nullptr, + /*IsCtorOrDtorName=*/false, + /*WantNontrivialTypeSourceInfo=*/true, + /*IsClassTemplateDeductionContext=*/false); + if (!Type) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + if (TypeScope.isSet()) + TypeScope.getScopeRep()->print(OS, getPrintingPolicy()); + OS << NameInfo.getName().getAsString(); + Diag(TypenameKWLoc, diag::err_type_requirement_no_such_type) << OS.str(); + return nullptr; + } + T = Type.get(); + } + + if (T.isNull()) + return nullptr; + + auto *LIT = dyn_cast(T.getTypePtr()); + return new (Context) TypeRequirement( + LIT ? LIT->getTypeSourceInfo() : + Context.getTrivialTypeSourceInfo(T, TypenameKWLoc)); +} + +Requirement *Sema::ActOnCompoundRequirement(Expr *E, + SourceLocation NoexceptLoc) { + return new (Context) ExprRequirement(*this, E, /*IsSimple=*/false, + NoexceptLoc); +} + +Requirement *Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc, + TypeSourceInfo *ExpectedType) { + if (ExpectedType->getType().getTypePtr()->isUndeducedType()) { + auto *Auto = ExpectedType->getType()->getContainedAutoType(); + Diag(ExpectedType->getTypeLoc().getBeginLoc(), + diag::err_auto_not_allowed_in_return_type_requirement) + << (unsigned)Auto->getKeyword(); + return nullptr; + } + return new (Context) ExprRequirement(*this, E, /*IsSimple=*/false, + NoexceptLoc, ExprRequirement::ReturnTypeRequirement(Context, + ExpectedType)); +} + +Requirement * +Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc, + SourceLocation ConceptNameLoc, + const CXXScopeSpec &SS, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + TemplateArgumentListInfo TemplateArgs, + Declarator &DeducedDeclarator, unsigned Depth) { + // C++2a [expr.prim.req.compound] p1.3.3 + // [..] the expression is deduced against an invented function template + // F [...] F is a void function template with a single type template + // parameter T declared with the constrained-parameter. Form a new + // cv-qualifier-seq cv by taking the union of const and volatile specifiers + // around the constrained-parameter. F has a single parameter whose + // type-specifier is cv T followed by the abstract-declarator. [...] + // + // The cv part is done in the calling function - we get the concept with + // arguments and the abstract declarator with the correct CV qualification and + // have to synthesize T and the single parameter of F. + auto &II = Context.Idents.get("expr-type"); + auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext, + SourceLocation(), + SourceLocation(), Depth, + /*Index=*/0, &II, + /*Typename=*/true, + /*ParameterPack=*/false); + QualType TParamType(TParam->getTypeForDecl(), 0); + + TemplateArgs.prependArgument( + TemplateArgumentLoc(TemplateArgument(TParamType), + TemplateArgumentLocInfo( + Context.getTrivialTypeSourceInfo(TParamType, + ConceptNameLoc)))); + ExprResult CSE = CheckConceptTemplateId(SS, + /*TemplateKWLoc=*/SourceLocation(), + ConceptNameLoc, FoundDecl, + NamedConcept, &TemplateArgs); + if (CSE.isInvalid()) + return nullptr; + + auto *TPL = TemplateParameterList::Create(Context, SourceLocation(), + SourceLocation(), + ArrayRef(TParam), + SourceLocation(), + /*RequiresClause=*/CSE.get()); + + const char *PrevSpec; + unsigned DiagID; + ParsedType TParamParsedType = ParsedType::make(TParamType); + const_cast(DeducedDeclarator.getDeclSpec()) + .SetTypeSpecType(DeclSpec::TST_typename, ConceptNameLoc, PrevSpec, + DiagID, TParamParsedType, Context.getPrintingPolicy()); + + return new (Context) ExprRequirement(*this, E, /*IsSimple=*/false, + NoexceptLoc, + ExprRequirement::ReturnTypeRequirement(Context, TPL, + GetTypeForDeclarator(DeducedDeclarator, CurScope))); +} + +Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { + return new (Context) NestedRequirement(*this, Constraint); +} + +RequiresExprBodyDecl * +Sema::ActOnEnterRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope) { + assert(BodyScope); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext, + RequiresKWLoc); + + // Maintain an efficient lookup of params we have seen so far. + llvm::SmallSet ParamsSoFar; + + PushDeclContext(BodyScope, Body); + + for (ParmVarDecl *Param : LocalParameters) { + if (Param->hasDefaultArg()) + // C++2a [expr.prim.req] p4 + // [...] A local parameter of a requires-expression shall not have a + // default argument. [...] + Diag(Param->getDefaultArgRange().getBegin(), + diag::err_requires_expr_local_parameter_default_argument); + // Ignore default argument and move on + + Param->setDeclContext(Body); + // If this has an identifier, add it to the scope stack. + if (Param->getIdentifier()) { + CheckShadow(BodyScope, Param); + + PushOnScopeChains(Param, BodyScope); + + // Verify that the argument identifier has not already been mentioned. + if (!ParamsSoFar.insert(Param->getIdentifier()).second) + Diag(Param->getBeginLoc(), diag::err_param_redefinition) + << Param->getIdentifier(); + } + } + return Body; +} + +void Sema::ActOnExitRequiresExpr() { + assert(CurContext && "DeclContext imbalance!"); + CurContext = CurContext->getLexicalParent(); + assert(CurContext && "Popped translation unit!"); +} + +ExprResult +Sema::CreateRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc) { + return new (Context) RequiresExpr(Context, RequiresKWLoc, Body, + LocalParameters, Requirements, + ClosingBraceLoc); +} \ No newline at end of file Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -1385,7 +1385,9 @@ unsigned N = CodeSynthesisContexts.size(); for (unsigned I = CodeSynthesisContextLookupModules.size(); I != N; ++I) { - Module *M = getDefiningModule(*this, CodeSynthesisContexts[I].Entity); + Module *M = CodeSynthesisContexts[I].Entity ? + getDefiningModule(*this, CodeSynthesisContexts[I].Entity) : + nullptr; if (M && !LookupModulesCache.insert(M).second) M = nullptr; CodeSynthesisContextLookupModules.push_back(M); Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -5771,3 +5771,22 @@ return Deduced.any(); } + +QualType Sema::matchTypeByDeduction(TemplateParameterList *TemplateParams, + QualType ParamType, Expr *Arg) { + assert(TemplateParams->size() == 1 && + isa(TemplateParams->getParam(0)) && + "Only template parameter lists containing a single type parameter are " + "supported"); + llvm::SmallVector Deduced; + Deduced.resize(1); + llvm::SmallVector OriginalCallArgs; + OriginalCallArgs.push_back(OriginalCallArg(ParamType, + /*DecomposedParam=*/false, + /*ArgIdx=*/0, Arg->getType())); + TemplateDeductionInfo Info{SourceLocation(), TemplateParams->getDepth()}; + auto Result = ::DeduceTemplateArgumentsFromCallArgument(*this, TemplateParams, + /*FirstInnerIndex=*/0, ParamType, Arg, Info, Deduced, OriginalCallArgs, + /*DecomposedParam=*/false, /*ArgIdx=*/0, /*TDF=*/0); + return Result == TDK_Success ? Deduced[0].getAsType() : QualType(); +} \ No newline at end of file Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -25,6 +25,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" +#include "clang/Sema/SemaConcept.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -195,8 +196,10 @@ case DeducedTemplateArgumentSubstitution: case PriorTemplateArgumentSubstitution: case ConstraintsCheck: + case NestedRequirementConstraintsCheck: return true; + case RequirementInstantiation: case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: case DefiningSynthesizedFunction: @@ -239,7 +242,7 @@ Inst.InstantiationRange = InstantiationRange; SemaRef.pushCodeSynthesisContext(Inst); - AlreadyInstantiating = + AlreadyInstantiating = !Inst.Entity ? false : !SemaRef.InstantiatingSpecializations .insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind)) .second; @@ -356,6 +359,24 @@ PointOfInstantiation, InstantiationRange, Param, Template, TemplateArgs) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, Requirement *Req, + sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::RequirementInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None, &DeductionInfo) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, NestedRequirement *Req, + ConstraintsCheck, SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::NestedRequirementConstraintsCheck, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None) {} + + Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, ConstraintsCheck, NamedDecl *Template, @@ -417,8 +438,9 @@ if (!Invalid) { if (!AlreadyInstantiating) { auto &Active = SemaRef.CodeSynthesisContexts.back(); - SemaRef.InstantiatingSpecializations.erase( - std::make_pair(Active.Entity, Active.Kind)); + if (Active.Entity) + SemaRef.InstantiatingSpecializations.erase( + std::make_pair(Active.Entity, Active.Kind)); } atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, @@ -656,6 +678,18 @@ << Active->InstantiationRange; break; + case CodeSynthesisContext::RequirementInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_requirement_instantiation_here) + << Active->InstantiationRange; + break; + + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + Diags.Report(Active->PointOfInstantiation, + diag::note_nested_requirement_here) + << Active->InstantiationRange; + break; + case CodeSynthesisContext::DeclaringSpecialMember: Diags.Report(Active->PointOfInstantiation, diag::note_in_declaration_of_implicit_special_member) @@ -727,6 +761,7 @@ case CodeSynthesisContext::DefaultFunctionArgumentInstantiation: case CodeSynthesisContext::ExceptionSpecInstantiation: case CodeSynthesisContext::ConstraintsCheck: + case CodeSynthesisContext::NestedRequirementConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return None; @@ -741,9 +776,10 @@ case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: case CodeSynthesisContext::ConstraintSubstitution: - // We're either substituting explicitly-specified template arguments - // or deduced template arguments or a constraint expression, so SFINAE - // applies. + case CodeSynthesisContext::RequirementInstantiation: + // We're either substituting explicitly-specified template arguments, + // deduced template arguments, a constraint expression or a requirement + // in a requires expression, so SFINAE applies. assert(Active->DeductionInfo && "Missing deduction info pointer"); return Active->DeductionInfo; @@ -982,6 +1018,39 @@ return TreeTransform::TransformLambdaExpr(E); } + ExprResult TransformRequiresExpr(RequiresExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform::TransformRequiresExpr(E); + } + + bool TransformRequiresExprRequirements(ArrayRef Reqs, + SmallVectorImpl &Transformed) { + bool SatisfcationDetermined = false; + for (Requirement *Req : Reqs) { + Requirement *TransReq = nullptr; + if (!SatisfcationDetermined) { + if (auto *TypeReq = dyn_cast(Req)) + TransReq = TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast(Req)) + TransReq = TransformExprRequirement(ExprReq); + else + TransReq = TransformNestedRequirement(cast(Req)); + if (!TransReq) + return true; + if (!TransReq->isDependent() && !TransReq->isSatisfied()) + // [expr.prim.req]p6 + // [...] The substitution and semantic constraint checking + // proceeds in lexical order and stops when a condition that + // determines the result of the requires-expression is + // encountered. [..] + SatisfcationDetermined = true; + } else + TransReq = Req; + Transformed.push_back(TransReq); + } + return false; + } + TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; @@ -991,6 +1060,11 @@ /* DeclContext *Owner */ Owner, TemplateArgs); return DeclInstantiator.SubstTemplateParams(OrigTPL); } + + TypeRequirement *TransformTypeRequirement(TypeRequirement *Req); + ExprRequirement *TransformExprRequirement(ExprRequirement *Req); + NestedRequirement *TransformNestedRequirement(NestedRequirement *Req); + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, @@ -1591,6 +1665,184 @@ return Result; } +template +static Requirement::SubstitutionDiagnostic * +createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) { + SmallString<128> Message; + SourceLocation ErrorLoc; + if (Info.hasSFINAEDiagnostic()) { + PartialDiagnosticAt PDA(SourceLocation(), + PartialDiagnostic::NullDiagnostic{}); + Info.takeSFINAEDiagnostic(PDA); + PDA.second.EmitToString(S.getDiagnostics(), Message); + ErrorLoc = PDA.first; + } else { + ErrorLoc = Info.getLocation(); + } + std::string Str; + llvm::raw_string_ostream OS(Str); + Printer(OS); + return new (S.Context) Requirement::SubstitutionDiagnostic{OS.str(), ErrorLoc, + Message.str()}; +} + +TypeRequirement * +TemplateInstantiator::TransformTypeRequirement(TypeRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getType()->getTypeLoc().getBeginLoc()); + Sema::InstantiatingTemplate TypeInst(SemaRef, + Req->getType()->getTypeLoc().getBeginLoc(), Req, Info, + Req->getType()->getTypeLoc().getSourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType(Req->getType()); + if (!TransType || Trap.hasErrorOccurred()) + return RebuildTypeRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy()); + })); + return RebuildTypeRequirement(TransType); +} + +ExprRequirement * +TemplateInstantiator::TransformExprRequirement(ExprRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getExpr()->getBeginLoc()); + + llvm::PointerUnion TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + Sema::InstantiatingTemplate ExprInst(SemaRef, Req->getExpr()->getBeginLoc(), + Req, Info, + Req->getExpr()->getSourceRange()); + if (ExprInst.isInvalid()) + return nullptr; + ExprResult TransExprRes = TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid() || Trap.hasErrorOccurred()) + TransExpr = createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + Req->getExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + }); + else + TransExpr = TransExprRes.get(); + } + + llvm::Optional TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTrailingReturnType()) { + Sema::InstantiatingTemplate TypeInst(SemaRef, SourceLocation(), Req, Info, + SourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType( + RetReq.getTrailingReturnTypeExpectedType()); + if (!TransType) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + RetReq.getTrailingReturnTypeExpectedType()->getType().print(OS, + SemaRef.getPrintingPolicy()); + })); + else + TransRetReq.emplace(SemaRef.Context, TransType); + } else if (RetReq.isConstrainedParameter()) { + TemplateParameterList *OrigTPL = + RetReq.getConstrainedParamTemplateParameterList(); + Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(), + Req, Info, OrigTPL->getSourceRange()); + if (TPLInst.isInvalid()) + return nullptr; + TemplateParameterList *TPL = + TransformTemplateParameterList(OrigTPL); + if (!TPL) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + OrigTPL->getRequiresClause()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + })); + else { + TPLInst.Clear(); + Sema::InstantiatingTemplate TypeInst(SemaRef, SourceLocation(), Req, Info, + SourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType( + RetReq.getConstrainedParamExpectedType()); + if (!TransType) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + RetReq.getConstrainedParamExpectedType()->getType() + .print(OS, SemaRef.getPrintingPolicy()); + })); + else + TransRetReq.emplace(SemaRef.Context, TPL, TransType); + } + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast()) + return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return RebuildExprRequirement( + TransExpr.get(), Req->isSimple(), + Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +NestedRequirement * +TemplateInstantiator::TransformNestedRequirement(NestedRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + Sema::InstantiatingTemplate ReqInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, + Sema::InstantiatingTemplate::ConstraintsCheck{}, + Req->getConstraintExpr()->getSourceRange()); + + ExprResult TransConstraint; + TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + { + EnterExpressionEvaluationContext ContextRAII( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Sema::SFINAETrap Trap(SemaRef); + Sema::InstantiatingTemplate ConstrInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, Info, + Req->getConstraintExpr()->getSourceRange()); + if (ConstrInst.isInvalid()) + return nullptr; + TransConstraint = TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) + return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + })); + } + return RebuildNestedRequirement(TransConstraint.get()); +} + + /// Perform substitution on the type T with a given set of template /// arguments. /// Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3429,6 +3429,12 @@ return nullptr; } +Decl * +TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(), + D->getBeginLoc()); +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2927,6 +2927,9 @@ case DeclaratorContext::PrototypeContext: Error = 0; break; + case DeclaratorContext::RequiresExprContext: + Error = 21; + break; case DeclaratorContext::LambdaExprParameterContext: // In C++14, generic lambdas allow 'auto' in their parameters. if (!SemaRef.getLangOpts().CPlusPlus14 || @@ -3152,6 +3155,7 @@ case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: case DeclaratorContext::KNRTypeListContext: + case DeclaratorContext::RequiresExprContext: // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. DiagID = diag::err_type_defined_in_param_type; @@ -4211,6 +4215,7 @@ case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TypeNameContext: case DeclaratorContext::FunctionalCastContext: + case DeclaratorContext::RequiresExprContext: // Don't infer in these contexts. break; } @@ -5143,6 +5148,7 @@ switch (D.getContext()) { case DeclaratorContext::PrototypeContext: case DeclaratorContext::LambdaExprParameterContext: + case DeclaratorContext::RequiresExprContext: // C++0x [dcl.fct]p13: // [...] When it is part of a parameter-declaration-clause, the // parameter pack is a function parameter pack (14.5.3). The type T Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -499,6 +499,12 @@ DeclarationNameInfo TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo); + bool TransformRequiresExprRequirements(ArrayRef Reqs, + llvm::SmallVectorImpl &Transformed); + TypeRequirement *TransformTypeRequirement(TypeRequirement *Req); + ExprRequirement *TransformExprRequirement(ExprRequirement *Req); + NestedRequirement *TransformNestedRequirement(NestedRequirement *Req); + /// Transform the given template name. /// /// \param SS The nested-name-specifier that qualifies the template @@ -2992,7 +2998,58 @@ return Result; } - /// \brief Build a new Objective-C boxed expression. + /// \brief Build a new requires expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc) { + ExprResult Result = getSema().CreateRequiresExpr(RequiresKWLoc, Body, + LocalParameters, + Requirements, + ClosingBraceLoc); + if (Result.isInvalid()) + return ExprError(); + return Result; + } + + TypeRequirement * + RebuildTypeRequirement(Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (SemaRef.Context) TypeRequirement(SubstDiag); + } + + TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) { + return new (SemaRef.Context) TypeRequirement(T); + } + + ExprRequirement * + RebuildExprRequirement(Requirement::SubstitutionDiagnostic *SubstDiag, + bool IsSimple, SourceLocation NoexceptLoc, + ExprRequirement::ReturnTypeRequirement Ret) { + return new (SemaRef.Context) ExprRequirement(SubstDiag, IsSimple, + NoexceptLoc, std::move(Ret)); + } + + ExprRequirement * + RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ExprRequirement::ReturnTypeRequirement Ret) { + return new (SemaRef.Context) ExprRequirement(SemaRef, E, IsSimple, + NoexceptLoc, std::move(Ret)); + } + + NestedRequirement * + RebuildNestedRequirement(Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (SemaRef.Context) NestedRequirement(SubstDiag); + } + + NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { + return new (SemaRef.Context) NestedRequirement(SemaRef, Constraint); + } + + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -10885,6 +10942,150 @@ &TransArgs); } +template +ExprResult +TreeTransform::TransformRequiresExpr(RequiresExpr *E) { + SmallVector TransParams; + SmallVector TransParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create( + getSema().Context, E->getBody()->getDeclContext(), + E->getBody()->getBeginLoc()); + + if (getDerived().TransformFunctionTypeParams(E->getRequiresKWLoc(), + E->getLocalParameters(), + /*ParamTypes=*/nullptr, + /*ParamInfos=*/nullptr, + TransParamTypes, &TransParams, + ExtParamInfos)) + return ExprError(); + + for (ParmVarDecl *Param : TransParams) + Param->setDeclContext(Body); + + SmallVector TransReqs; + if (getDerived().TransformRequiresExprRequirements(E->getRequirements(), + TransReqs)) + return ExprError(); + + for (Requirement *Req : TransReqs) + if (auto *ER = dyn_cast(Req)) + if (ER->getReturnTypeRequirement().isConstrainedParameter()) + ER->getReturnTypeRequirement() + .getConstrainedParamTemplateParameterList()->getParam(0) + ->setDeclContext(Body); + + return getDerived().RebuildRequiresExpr(E->getRequiresKWLoc(), Body, + TransParams, TransReqs, + E->getRBraceLoc()); +} + +template +bool TreeTransform::TransformRequiresExprRequirements( + ArrayRef Reqs, + SmallVectorImpl &Transformed) { + for (Requirement *Req : Reqs) { + Requirement *TransReq = nullptr; + if (auto *TypeReq = dyn_cast(Req)) + TransReq = getDerived().TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast(Req)) + TransReq = getDerived().TransformExprRequirement(ExprReq); + else + TransReq = getDerived().TransformNestedRequirement( + cast(Req)); + if (!TransReq) + return true; + Transformed.push_back(TransReq); + } + return false; +} + +template +TypeRequirement * +TreeTransform::TransformTypeRequirement(TypeRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + TypeSourceInfo *TransType = getDerived().TransformType(Req->getType()); + if (!TransType) + return nullptr; + return getDerived().RebuildTypeRequirement(TransType); +} + +template +ExprRequirement * +TreeTransform::TransformExprRequirement(ExprRequirement *Req) { + llvm::PointerUnion TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid()) + return nullptr; + TransExpr = TransExprRes.get(); + } + + llvm::Optional TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTrailingReturnType()) { + TypeSourceInfo *TransType = getDerived().TransformType( + RetReq.getTrailingReturnTypeExpectedType()); + if (!TransType) + return nullptr; + TransRetReq.emplace(getSema().Context, TransType); + } else if (RetReq.isConstrainedParameter()) { + TemplateParameterList *OrigTPL = + RetReq.getConstrainedParamTemplateParameterList(); + TemplateParameterList *TPL = + getDerived().TransformTemplateParameterList(OrigTPL); + if (!TPL) + return nullptr; + + TypeSourceInfo *TransType = getDerived().TransformType( + RetReq.getConstrainedParamExpectedType()); + if (!TransType) + return nullptr; + TransRetReq.emplace(getSema().Context, TPL, TransType); + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast()) + return getDerived().RebuildExprRequirement(E, Req->isSimple(), + Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return getDerived().RebuildExprRequirement( + TransExpr.get(), Req->isSimple(), + Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +template +NestedRequirement * +TreeTransform::TransformNestedRequirement(NestedRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + ExprResult TransConstraint = + getDerived().TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid()) + return nullptr; + return getDerived().RebuildNestedRequirement(TransConstraint.get()); +} template ExprResult Index: lib/Serialization/ASTCommon.cpp =================================================================== --- lib/Serialization/ASTCommon.cpp +++ lib/Serialization/ASTCommon.cpp @@ -396,6 +396,7 @@ case Decl::Decomposition: case Decl::Binding: case Decl::Concept: + case Decl::RequiresExprBody: return false; // These indirectly derive from Redeclarable but are not actually Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -713,6 +713,28 @@ E->setRParenLoc(ReadSourceLocation()); } +static ConstraintSatisfaction +readConstraintSatisfaction(ASTRecordReader &Record) { + ConstraintSatisfaction Satisfaction; + Satisfaction.IsSatisfied = Record.readInt(); + if (!Satisfaction.IsSatisfied) { + unsigned NumDetailRecords = Record.readInt(); + for (unsigned i = 0; i != NumDetailRecords; ++i) { + Expr *ConstraintExpr = Record.readExpr(); + if (bool IsDiagnostic = Record.readInt()) { + SourceLocation DiagLocation = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + Satisfaction.Details.emplace_back( + ConstraintExpr, new (Record.getContext()) + ConstraintSatisfaction::SubstitutionDiagnostic{ + DiagLocation, DiagMessage}); + } else + Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); + } + } + return Satisfaction; +} + void ASTStmtReader::VisitConceptSpecializationExpr( ConceptSpecializationExpr *E) { VisitExpr(E); @@ -728,24 +750,122 @@ for (unsigned I = 0; I < NumTemplateArgs; ++I) Args.push_back(Record.readTemplateArgument()); E->setTemplateArguments(ArgsAsWritten, Args); - ConstraintSatisfaction Satisfaction; - Satisfaction.IsSatisfied = Record.readInt(); - if (!Satisfaction.IsSatisfied) { - unsigned NumDetailRecords = Record.readInt(); - for (unsigned i = 0; i != NumDetailRecords; ++i) { - Expr *ConstraintExpr = Record.readExpr(); - bool IsDiagnostic = Record.readInt(); - if (IsDiagnostic) { - SourceLocation DiagLocation = Record.readSourceLocation(); - std::string DiagMessage = Record.readString(); - Satisfaction.Details.emplace_back( - ConstraintExpr, new (Record.getContext()) - ConstraintSatisfaction::SubstitutionDiagnostic{ - DiagLocation, DiagMessage}); - } else - Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); + E->setSatisfaction(readConstraintSatisfaction(Record)); +} + +static Requirement::SubstitutionDiagnostic * +readSubstitutionDiagnostic(ASTRecordReader &Record) { + std::string SubstitutedEntity = Record.readString(); + SourceLocation DiagLoc = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + return new (Record.getContext()) Requirement::SubstitutionDiagnostic{ + SubstitutedEntity, DiagLoc, DiagMessage}; +} + +void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + SourceLocation RequiresKWLoc = Record.readSourceLocation(); + E->setRequiresKWLoc(RequiresKWLoc); + auto *Body = cast(Record.readDecl()); + E->setBody(Body); + unsigned NumLocalParameters = Record.readInt(); + llvm::SmallVector LocalParameters; + for (unsigned i = 0; i < NumLocalParameters; ++i) + LocalParameters.push_back(cast(Record.readDecl())); + E->setLocalParameters(LocalParameters); + unsigned NumRequirements = Record.readInt(); + llvm::SmallVector Requirements; + for (unsigned i = 0; i < NumRequirements; ++i) { + auto RK = static_cast(Record.readInt()); + Requirement *R = nullptr; + switch (RK) { + case Requirement::RK_Type: { + auto Status = + static_cast(Record.readInt()); + if (Status == TypeRequirement::SS_SubstitutionFailure) + R = new (Record.getContext()) TypeRequirement( + readSubstitutionDiagnostic(Record)); + else + R = new (Record.getContext()) TypeRequirement( + Record.getTypeSourceInfo()); + } break; + case Requirement::RK_Simple: + case Requirement::RK_Compound: { + auto Status = + static_cast(Record.readInt()); + llvm::PointerUnion E; + if (Status == ExprRequirement::SS_ExprSubstitutionFailure) { + E = readSubstitutionDiagnostic(Record); + } else + E = Record.readExpr(); + + llvm::Optional Req; + SourceLocation NoexceptLoc; + if (RK == Requirement::RK_Simple) { + Req.emplace(); + } else { + NoexceptLoc = Record.readSourceLocation(); + switch (auto returnTypeRequirementKind = Record.readInt()) { + case 0: + // No return type requirement. + Req.emplace(); + break; + case 1: { + // trailing-return-type + TypeSourceInfo *ExpectedType = Record.getTypeSourceInfo(); + Req.emplace(Record.getContext(), ExpectedType); + } break; + case 2: { + // Constrained parameter + TypeSourceInfo *ExpectedType = Record.getTypeSourceInfo(); + TemplateParameterList *TPL = Record.readTemplateParameterList(); + ConceptSpecializationExpr *CSE = nullptr; + if (Status >= ExprRequirement::SS_ConstraintsNotSatisfied) + CSE = cast(Record.readExpr()); + if (CSE) + Req.emplace(Record.getContext(), TPL, ExpectedType, CSE); + } break; + case 3: { + // Substitution failure + Req.emplace(readSubstitutionDiagnostic(Record)); + } break; + } + } + if (Req) { + if (Expr *Ex = E.dyn_cast()) + R = new (Record.getContext()) ExprRequirement(Ex, + RK == Requirement::RK_Simple, NoexceptLoc, std::move(*Req), + Status); + else + R = new (Record.getContext()) ExprRequirement( + E.get(), + RK == Requirement::RK_Simple, NoexceptLoc, std::move(*Req)); + } + } break; + case Requirement::RK_Nested: { + if (bool IsSubstitutionDiagnostic = Record.readInt()) { + SourceLocation SubstDiagLocation = Record.readSourceLocation(); + std::string SubstDiagMessage = Record.readString(); + R = new (Record.getContext()) NestedRequirement( + readSubstitutionDiagnostic(Record)); + break; + } + Expr *E = Record.readExpr(); + if (E->isInstantiationDependent()) + R = new (Record.getContext()) NestedRequirement(E); + else + R = new (Record.getContext()) NestedRequirement(E, + readConstraintSatisfaction(Record)); + } break; } + if (!R) + continue; + Requirements.push_back(R); } + E->setRequirements(Requirements); + SourceLocation RBraceLoc = Record.readSourceLocation(); + E->setRBraceLoc(RBraceLoc); } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { @@ -3435,11 +3555,15 @@ S = new (Context) DependentCoawaitExpr(Empty); break; - case EXPR_CONCEPT_SPECIALIZATION: + case EXPR_CONCEPT_SPECIALIZATION: { unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields]; S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs); break; - + } + + case EXPR_REQUIRES: + S = new (Context) RequiresExpr(Context, Empty); + break; } // We hit a STMT_STOP, so we're done with this expression. Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include #include "clang/Serialization/ASTWriter.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" @@ -384,20 +385,9 @@ Code = serialization::EXPR_DEPENDENT_COAWAIT; } -void ASTStmtWriter::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - ArrayRef TemplateArgs = E->getTemplateArguments(); - Record.push_back(TemplateArgs.size()); - Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); - Record.AddSourceLocation(E->getTemplateKWLoc()); - Record.AddSourceLocation(E->getConceptNameLoc()); - Record.AddDeclRef(E->getFoundDecl()); - Record.AddDeclRef(E->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); - for (const TemplateArgument &Arg : TemplateArgs) - Record.AddTemplateArgument(Arg); - const ConstraintSatisfaction &Satisfaction = E->getSatisfaction(); +static void +addConstraintSatisfaction(ASTRecordWriter &Record, + const ConstraintSatisfaction &Satisfaction) { Record.push_back(Satisfaction.IsSatisfied); if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.Details.size()); @@ -414,10 +404,101 @@ Record.AddStmt(DetailRecord.second.get()); } } +} + +static void +addSubstitutionDiagnostic(ASTRecordWriter &Record, + const Requirement::SubstitutionDiagnostic *D) { + Record.AddString(D->SubstitutedEntity); + Record.AddSourceLocation(D->DiagLoc); + Record.AddString(D->DiagMessage); +} + +void ASTStmtWriter::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + ArrayRef TemplateArgs = E->getTemplateArguments(); + Record.push_back(TemplateArgs.size()); + Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); + Record.AddSourceLocation(E->getTemplateKWLoc()); + Record.AddSourceLocation(E->getConceptNameLoc()); + Record.AddDeclRef(E->getFoundDecl()); + Record.AddDeclRef(E->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); + for (const TemplateArgument &Arg : TemplateArgs) + Record.AddTemplateArgument(Arg); + addConstraintSatisfaction(Record, E->getSatisfaction()); Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } +void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getRequiresKWLoc()); + Record.AddDeclRef(E->getBody()); + Record.push_back(E->getLocalParameters().size()); + for (ParmVarDecl *P : E->getLocalParameters()) + Record.AddDeclRef(P); + Record.push_back(E->getRequirements().size()); + for (Requirement *R : E->getRequirements()) { + if (auto *TypeReq = dyn_cast(R)) { + Record.push_back(Requirement::RK_Type); + Record.push_back(TypeReq->Status); + if (TypeReq->Status == TypeRequirement::SS_SubstitutionFailure) + addSubstitutionDiagnostic(Record, TypeReq->getSubstitutionDiagnostic()); + else + Record.AddTypeSourceInfo(TypeReq->getType()); + } else if (auto *ExprReq = dyn_cast(R)) { + Record.push_back(ExprReq->getKind()); + Record.push_back(ExprReq->Status); + if (ExprReq->isExprSubstitutionFailure()) { + addSubstitutionDiagnostic(Record, + ExprReq->Value.get()); + } else + Record.AddStmt(ExprReq->Value.get()); + if (ExprReq->getKind() == Requirement::RK_Compound) { + Record.AddSourceLocation(ExprReq->NoexceptLoc); + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isSubstitutionFailure()) { + Record.push_back(3); + addSubstitutionDiagnostic(Record, RetReq.getSubstitutionDiagnostic()); + } else if (RetReq.isTrailingReturnType()) { + Record.push_back(1); + Record.AddTypeSourceInfo(RetReq.getTrailingReturnTypeExpectedType()); + } else if (RetReq.isConstrainedParameter()) { + Record.push_back(2); + Record.AddTypeSourceInfo(RetReq.getConstrainedParamExpectedType()); + Record.AddTemplateParameterList( + RetReq.getConstrainedParamTemplateParameterList()); + if (ExprReq->Status >= ExprRequirement::SS_ConstraintsNotSatisfied) + Record.AddStmt( + std::get<2>(*RetReq.Value.get())); + } else { + assert(RetReq.isEmpty()); + Record.push_back(0); + } + } + } else { + auto *NestedReq = cast(R); + Record.push_back(Requirement::RK_Nested); + + if (auto *SubstDiag = + NestedReq->Value.dyn_cast()){ + addSubstitutionDiagnostic(Record, SubstDiag); + } else { + Record.AddStmt(NestedReq->Value.get()); + if (!NestedReq->isDependent()) + addConstraintSatisfaction(Record, NestedReq->Satisfaction); + } + } + } + Record.AddSourceLocation(E->getEndLoc()); + + Code = serialization::EXPR_REQUIRES; +} + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1321,6 +1321,7 @@ case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: case Stmt::ConceptSpecializationExprClass: + case Stmt::RequiresExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/compound-requirement.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/compound-requirement.cpp @@ -0,0 +1,254 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +static_assert(requires { { 0 }; }); +static_assert(requires { { "aaaa" }; }); +static_assert(requires { { (0).da }; }); // expected-error{{member reference base type 'int' is not a structure or union}} + +void foo() {} +static_assert(requires { { foo() }; }); + +// Substitution failure in expression + +struct A {}; +struct B { + B operator+(const B &other) const { return other; } +}; +struct C { + C operator+(C &other) const { return other; } +}; + +template requires requires (T a, const T& b) { { a + b }; } // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}} +struct r1 {}; + +using r1i1 = r1; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}} +using r1i3 = r1; +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} + +struct D { void foo() {} }; + +template requires requires (T a) { { a.foo() }; } // expected-note{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} expected-note{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} expected-note{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}} +struct r2 {}; + +using r2i1 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} +using r2i2 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}} +using r2i3 = r2; +using r2i4 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} + +template requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +struct r3 {}; + +using r3i1 = r3; +using r3i2 = r3; +using r3i3 = r3; +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} + +// Non-dependent expressions + +template requires requires (T t) { { 0 }; { "a" }; { (void)'a' }; } +struct r4 {}; + +using r4i1 = r4; +using r4i2 = r4; +using r4i3 = r4; + +// Noexcept requirement +void maythrow() { } +static_assert(!requires { { maythrow() } noexcept; }); +static_assert(requires { { 1 } noexcept; }); + +struct E { void operator++(int) noexcept; }; +struct F { void operator++(int); }; + +template requires requires (T t) { { t++ } noexcept; } // expected-note{{because 't ++' may throw an exception}} +struct r5 {}; + +using r5i1 = r5; +using r5i2 = r5; +using r5i2 = r5; // expected-error{{constraints not satisfied for class template 'r5' [with T = F]}} + +template requires requires (T t) { { t.foo() } noexcept; } // expected-note{{because 't.foo()' would be invalid: no member named 'foo' in 'E'}} +struct r6 {}; + +using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with T = E]}} + +// Trailing return type requirement. + +static_assert(requires { { 0 } -> int; { 0 } -> const int; { 0 } -> long long int; }); +static_assert(!requires { { 0 } -> void; }); +static_assert(!requires { { 0 } -> char[4]; }); + +struct G { operator F() { return F(); } G(F) {} }; +struct H { explicit operator F() { return F(); } }; +struct I { + operator F() { return F(); } + explicit operator F() const { return F(); } +}; +struct J { operator int() { } operator double() { } }; + +template requires requires (T t) { { t } -> U; } // expected-note{{because expression type 'int' not implicitly convertible to required type 'void'}} expected-note{{because expression type 'H' not implicitly convertible to required type 'F'}} expected-note{{because expression type 'const I' not implicitly convertible to required type 'F'}} expected-note{{because conversion from expression type 'J' to required type 'long' is ambiguous}} +struct r7 {}; + +using r7i1 = r7; +using r7i2 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = int, U = void]}} +using r7i3 = r7; +using r7i4 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = H, U = F]}} +using r7i5 = r7; +using r7i6 = r7; +using r7i7 = r7; +using r7i8 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = const I, U = F]}} +using r7i9 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = J, U = long]}} + +struct K { void foo() { } }; +struct L { int foo() { } }; + +template requires requires (T t) { { t.foo() } -> void; } // expected-note{{because expression type 'int' not implicitly convertible to required type 'void'}} +struct r8 {}; + +using r8i1 = r8; +using r8i2 = r8; // expected-error{{constraints not satisfied for class template 'r8' [with T = L]}} + +// Substitution failure in return type requirement + +struct M { using type = M; }; + +template requires requires (T t) { { t } -> typename T::type; } // expected-note{{because 'typename T::type' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +struct r9 {}; + +using r9i1 = r9; +using r9i2 = r9; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}} + +// Constrained parameter + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +template +concept Same = is_same_v; + +template +concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}} + +template requires requires (T t) { { t } -> Large; } // expected-note{{because expression type 'short' does not satisfy 'Large':}} +struct r10 {}; + +using r10i1 = r10; +using r10i2 = r10; // expected-error{{constraints not satisfied for class template 'r10' [with T = short]}} + +template requires requires (T t) { { t } -> Same; } +struct r11 {}; + +using r11i1 = r11; +using r11i2 = r11; + +template requires requires (T t) { { t } -> const Same*; } // expected-note{{because expression type 'const int' does not match given pattern 'const *'}} +struct r12 {}; + +using r12i1 = r12; // expected-error{{constraints not satisfied for class template 'r12' [with T = const int, U = int]}} +using r12i2 = r12; +using r12i3 = r12; + +template requires requires (T t) { { t } -> const Same volatile; } +struct r13 {}; + +using r13i2 = r13; +using r13i3 = r13; +using r13i4 = r13; + +template requires requires (T t) { { t } -> Same**; } // expected-note{{because expression type 'const short *' does not match given pattern ' **'}} +struct r14 {}; + +using r14i1 = r14; +using r14i2 = r14; // expected-error{{constraints not satisfied for class template 'r14' [with T = const short *, U = const short *]}} + +// Substitution failure in constrained parameter + +template requires requires (T t) { { t } -> Same; } // expected-note{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +struct r15 {}; + +using r15i1 = r15; +using r15i2 = r15; // expected-error{{constraints not satisfied for class template 'r15' [with T = int]}} + +// Substitution failure in both expression and return type requirement + +template requires requires (T t) { { t.foo() } -> Same; } // expected-note{{because 't.foo()' would be invalid: member reference base type 'int' is not a structure or union}} +struct r16 {}; + +using r16i = r16; // expected-error{{constraints not satisfied for class template 'r16' [with T = int]}} + +template requires requires (T t) { { t.foo() } -> typename T::type; } // expected-note{{because 't.foo()' would be invalid: member reference base type 'int' is not a structure or union}} +struct r17 {}; + +using r17i = r17; // expected-error{{constraints not satisfied for class template 'r17' [with T = int]}} + +// Non-type concept in constrained parameter + +template +concept IsEven = (T % 2) == 0; + +template requires requires (T t) { { t } -> IsEven; } // expected-error{{only type concepts can be used to constrain the type of an expression ('IsEven' is a value concept)}} +struct r18 {}; + +// C++ [expr.prim.req.compound] Example +namespace std_example { + template concept C1 = + requires(T x) { + {x++}; + }; + + static_assert(C1); + static_assert(C1); + template struct C1_check {}; + using c1c1 = C1_check; + using c1c2 = C1_check; + + template concept C2 = + requires(T x) { + {*x} -> typename T::inner; // expected-note{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}} expected-note{{because expression type 'int' not implicitly convertible to required type 'typename T2::inner' (aka 'int *')}} + }; + + struct T1 { + using inner = int; + inner operator *() { return 0; } + }; + struct T2 { + using inner = int *; + int operator *() { return 0; } + }; + static_assert(C2); + template struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}} + using c2c1 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}} + using c2c2 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}} + + template concept C3 = false; // expected-note 2{{because 'false' evaluated to false}} + template concept C4 = + requires(T x) { + {*x} -> C3 const&; // expected-note{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}} expected-note {{because type constraint 'C3' was not satisfied:}} expected-note {{because type constraint 'C3' was not satisfied:}} + }; + + struct T3 { + short i; + const short &operator *() { return i; } + }; + + template struct C4_check {}; // expected-note{{because 'int' does not satisfy 'C4'}} expected-note{{because 'int *' does not satisfy 'C4'}} expected-note{{because 'std_example::T3' does not satisfy 'C4'}} + using c4c1 = C4_check; // expected-error{{constraints not satisfied for class template 'C4_check' [with T = int]}} + using c4c2 = C4_check; // expected-error{{constraints not satisfied for class template 'C4_check' [with T = int *]}} + using c4c3 = C4_check; // expected-error{{constraints not satisfied for class template 'C4_check' [with T = std_example::T3]}} + + template + void g(T t) noexcept(sizeof(T) == 1) {} + + template concept C5 = + requires(T x) { + {g(x)} noexcept; // expected-note{{because 'g(x)' may throw an exception}} + }; + + static_assert(C5); + template struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}} + using c5 = C5_check; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}} +} \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/equivalence.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/equivalence.cpp @@ -0,0 +1,162 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template constexpr bool is_same_v = false; +template constexpr bool is_same_v = true; + +template struct identity { using type = T; }; +template using identity_t = T; + +// Type requirements +template requires requires { typename identity_t; } +struct r1; +template requires requires { typename identity_t; } // expected-note{{previous template declaration is here}} +struct r1; +template requires requires { typename identity_t; } // expected-error{{associated constraints differ in template redeclaration}} +struct r1; +template requires requires { typename ::identity_t; } +struct r1; + +template requires requires { typename identity::type; } +struct r2; +template requires requires { typename identity::type; } +struct r2; +template requires requires { typename ::identity::type; } // expected-note 2{{previous template declaration is here}} +struct r2; +template requires requires { typename identity::typr; } // expected-error{{associated constraints differ in template redeclaration}} +struct r2; +namespace ns { + template struct identity { using type = T; }; +} +template requires requires { typename ns::identity::type; } // expected-error{{associated constraints differ in template redeclaration}} +struct r2; + +template requires requires { typename T::template identity::type; } +struct r3; +template requires requires { typename U::template identity::type; } // expected-note{{previous template declaration is here}} +struct r3; +template requires requires { typename T::template identitr::type; } // expected-error{{associated constraints differ in template redeclaration}} +struct r3; + +template requires requires { typename T::template temp<>; } +struct r4; +template requires requires { typename U::template temp<>; } +struct r4; + +// Expr requirements +template requires requires { 0; } // expected-note{{previous template declaration is here}} +struct r5; +template requires requires { 1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r5; + +template +concept C1 = true; + +template requires requires { sizeof(T); } +struct r6; +template requires requires { sizeof(U); } // expected-note{{previous template declaration is here}} +struct r6; +template requires requires { sizeof(U) - 1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r6; +template requires requires { { sizeof(T) }; } // expected-note 3{{previous template declaration is here}} +struct r6; +template requires requires { { sizeof(T) } -> decltype(sizeof(int)); } // expected-error{{associated constraints differ in template redeclaration}} +struct r6; +template requires requires { { sizeof(T) } noexcept; } // expected-error{{associated constraints differ in template redeclaration}} +struct r6; +template requires requires { { sizeof(T) } -> C1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r6; + +template requires requires { { sizeof(T) } -> int; } +struct r7; +template requires requires { { sizeof(U) } -> int; } // expected-note 3{{previous template declaration is here}} +struct r7; +template requires requires { { sizeof(T) } -> const int; } // expected-error{{associated constraints differ in template redeclaration}} +struct r7; +template requires requires { { sizeof(T) } noexcept -> int; } // expected-error{{associated constraints differ in template redeclaration}} +struct r7; +template requires requires { { sizeof(T) } -> T; } // expected-error{{associated constraints differ in template redeclaration}} +struct r7; + +template requires requires { { sizeof(T) } -> C1; } +struct r8; +template requires requires { { sizeof(U) } -> C1; } +struct r8; +template requires requires { { sizeof(T) } -> C1<>; } // expected-note 2{{previous template declaration is here}} +struct r8; +template requires requires { { sizeof(U) }; } // expected-error{{associated constraints differ in template redeclaration}} +struct r8; +template requires requires { { sizeof(T) } -> const C1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r8; + +template requires requires { { sizeof(T) } -> const volatile C1; } +struct r9; +template requires requires { { sizeof(U) } -> const volatile C1; } +struct r9; +template requires requires { { sizeof(T) } -> C1 const volatile; } +struct r9; +template requires requires { { sizeof(T) } -> C1 volatile const; } +struct r9; +template requires requires { { sizeof(T) } -> const C1 volatile; } +struct r9; +template requires requires { { sizeof(T) } -> volatile C1 const; } +struct r9; + +template requires requires { { sizeof(T) } -> C1[sizeof(T)]; } +struct r10; +template requires requires { { sizeof(U) } -> C1[sizeof(U)]; } // expected-note 2{{previous template declaration is here}} +struct r10; +template requires requires { { sizeof(T) } -> C1[sizeof(T) - 1]; } // expected-error{{associated constraints differ in template redeclaration}} +struct r10; +template requires requires { { sizeof(T) } -> C1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r10; + +template +concept C2 = true; + +template requires requires { { sizeof(T) } -> C2; } +struct r11; +template requires requires { { sizeof(U) } -> C2; } // expected-note{{previous template declaration is here}} +struct r11; +template requires requires { { sizeof(T) } -> C2; } // expected-error{{associated constraints differ in template redeclaration}} +struct r11; + +// Nested requirements +template requires requires { requires sizeof(T) == 0; } +struct r12; +template requires requires { requires sizeof(U) == 0; } // expected-note{{previous template declaration is here}} +struct r12; +template requires requires { requires sizeof(T) == 1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r12; + +// Parameter list +template requires requires { requires true; } +struct r13; +template requires requires() { requires true; } // expected-note{{previous template declaration is here}} +struct r13; +template requires requires(T i) { requires true; } // expected-error{{associated constraints differ in template redeclaration}} +struct r13; + +template requires requires(T i, T *j) { requires true; } // expected-note 2{{previous template declaration is here}} +struct r14; +template requires requires(T i) { requires true; } // expected-error{{associated constraints differ in template redeclaration}} +struct r14; +template requires requires(T i, T *j, T &k) { requires true; } // expected-error{{associated constraints differ in template redeclaration}} +struct r14; + +// Parameter names +template requires requires(int i) { requires sizeof(i) == 1; } +struct r15; +template requires requires(int j) { requires sizeof(j) == 1; } // expected-note 2{{previous template declaration is here}} +struct r15; +template requires requires(int k) { requires sizeof(k) == 2; } // expected-error{{associated constraints differ in template redeclaration}} +struct r15; +template requires requires(const int k) { requires sizeof(k) == 1; } // expected-error{{associated constraints differ in template redeclaration}} +struct r15; + +// Order of requirements +template requires requires { requires true; 0; } +struct r16; +template requires requires { requires true; 0; } // expected-note{{previous template declaration is here}} +struct r16; +template requires requires { 0; requires true; } // expected-error{{associated constraints differ in template redeclaration}} +struct r16; Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/nested-requirement.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +static_assert(requires { requires true; }); + +template requires requires { requires false; } // expected-note{{because 'false' evaluated to false}} +struct r1 {}; + +using r1i = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + +template requires requires { requires sizeof(T) == 0; } // expected-note{{because 'sizeof(int) == 0' (4 == 0) evaluated to false}} +struct r2 {}; + +using r2i = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} + +template requires requires (T t) { requires sizeof(t) == 0; } // expected-note{{because 'sizeof (t) == 0' (4 == 0) evaluated to false}} +struct r3 {}; + +using r3i = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = int]}} + +template +struct X { + template requires requires (U u) { requires sizeof(u) == sizeof(T); } // expected-note{{because 'sizeof (u) == sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} + struct r4 {}; +}; + +using r4i = X::r4; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}} + +// C++ [expr.prim.req.nested] Examples +namespace std_example { + template concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}} + template concept D = + requires (T t) { + requires C1; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}} + }; + + struct T1 { char operator+() { return 'a'; } }; + static_assert(D); + template struct D_check {}; // expected-note{{because 'short' does not satisfy 'D'}} + using dc1 = D_check; // expected-error{{constraints not satisfied for class template 'D_check' [with T = short]}} + + template + concept C2 = requires (T a) { // expected-note{{'a' declared here}} + requires sizeof(a) == 4; // OK + requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}} + }; +} \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/p3.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/p3.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +// Examples from standard + +template +concept R = requires (T i) { + typename T::type; + {*i} -> const typename T::type&; +}; + +template requires R struct S {}; + +struct T { + using type = int; + type i; + const type &operator*() { return i; } +}; + +using si = S; + +template +requires requires (T x) { x + x; } // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}} +T add(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}} + +int x = add(1, 2); +int y = add(T{}, T{}); // expected-error{{no matching function for call to 'add'}} + +template +concept C = requires (T x) { x + x; }; // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}} +template requires C // expected-note{{because 'T' does not satisfy 'C'}} +T add2(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}} + +int z = add2(1, 2); +int w = add2(T{}, T{}); // expected-error{{no matching function for call to 'add2'}} \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/requires-expr.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/requires-expr.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +using A = int; + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +static_assert(requires { requires true; 0; typename A; { 0 } -> int; }); +static_assert(is_same_v); + +// Check that requires expr is an unevaluated context. +struct Y { + int i; + static constexpr bool r = requires { i; }; +}; + +template requires requires (T t) { + requires false; // expected-note{{because 'false' evaluated to false}} + requires false; + requires requires { + requires false; + }; +} +struct r1 { }; + +using r1i = r1; +// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = int]}} + +template requires requires (T t) { + requires requires { + requires false; // expected-note{{because 'false' evaluated to false}} + }; +} +struct r2 { }; + +using r2i = r2; +// expected-error@-1 {{constraints not satisfied for class template 'r2' [with T = int]}} + +template requires requires (T t) { + requires requires { + requires true; + }; + requires true; + requires requires { + requires false; // expected-note{{because 'false' evaluated to false}} + }; +} +struct r3 { }; + +using r3i = r3; +// expected-error@-1 {{constraints not satisfied for class template 'r3' [with T = int]}} + +template +struct S { static const int s = T::value; }; + +template requires requires { T::value; S::s; } +// expected-note@-1 {{because 'T::value' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +struct r4 { }; + +using r4i = r4; +// expected-error@-1 {{constraints not satisfied for class template 'r4' [with T = int]}} \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/simple-requirement.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/simple-requirement.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify +#if 0 +static_assert(requires { 0; }); +static_assert(requires { "aaaa"; }); +static_assert(requires { (0).da; }); // expected-error{{member reference base type 'int' is not a structure or union}} + +struct A {}; +struct B { + B operator+(const B &other) const { return other; } +}; +struct C { + C operator+(C &other) const { return other; } +}; + +template requires requires (T a, const T& b) { a + b; } // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}} +struct r1 {}; + +using r1i1 = r1; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}} +using r1i3 = r1; +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} + +struct D { void foo() {} }; + +template requires requires (T a) { a.foo(); } // expected-note{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} expected-note{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} expected-note{{because 'a.foo()' would be invalid: member function 'foo' not viable: 'this' argument has type 'const D', but function is not marked const}} +struct r2 {}; + +using r2i1 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} +using r2i2 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}} +using r2i3 = r2; +using r2i4 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} + +template requires requires { sizeof(T); } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +struct r3 {}; + +using r3i1 = r3; +using r3i2 = r3; +using r3i3 = r3; +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} + +template requires requires (T t) { 0; "a"; (void)'a'; } +struct r4 {}; + +using r4i1 = r4; +using r4i2 = r4; +using r4i3 = r4; +#endif +template void f(T) = delete; +template requires (sizeof(T) == 1) void f(T) { } + +template requires requires(T t) { f(t); } +// expected-note@-1{{because 'f(t)' would be invalid: call to deleted function 'f'}} +struct r5 {}; + +using r5i1 = r5; +// expected-error@-1 {{constraints not satisfied for class template 'r5' [with T = int]}} +using r5i2 = r5; +#if 0 +// C++ [expr.prim.req.simple] Example +namespace std_example { + template concept C = + requires (T a, T b) { // expected-note{{because substituted constraint expression is ill-formed: argument may not have 'void' type}} + a + b; // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('int *' and 'int *')}} + }; + + static_assert(C); + template struct C_check {}; // expected-note{{because 'void' does not satisfy 'C'}} expected-note{{because 'int *' does not satisfy 'C'}} + using c1c1 = C_check; // expected-error{{constraints not satisfied for class template 'C_check' [with T = void]}} + using c1c2 = C_check; // expected-error{{constraints not satisfied for class template 'C_check' [with T = int *]}} +} +#endif \ No newline at end of file Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/type-requirement.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/type-requirement.cpp @@ -0,0 +1,184 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +using A = int; + +template using identity_t = T; // expected-note 2{{template is declared here}}} + +template struct identity { using type = T; }; + +struct C {}; + +struct D { static int type; }; // expected-note{{referenced member 'type' is declared here}} + +// Basic unqualified and global-qualified lookups + +static_assert(requires { typename A; typename ::A; }); +static_assert(requires { typename identity_t; typename ::identity_t; }); +static_assert(!requires { typename identity_t; }); // expected-error{{too many template arguments for alias template 'identity_t'}} +static_assert(!requires { typename ::identity_t; }); // expected-error{{too many template arguments for alias template 'identity_t'}} +static_assert(requires { typename identity; }); +static_assert(!requires { typename identity; }); // expected-error{{'identity' does not name a type}} +static_assert(!requires { typename ::identity; }); // expected-error{{'::identity' does not name a type}} +static_assert(!requires { typename identity_t; }); // expected-error{{'identity_t' does not name a type}} +static_assert(!requires { typename ::identity_t; }); // expected-error{{'::identity_t' does not name a type}} + +namespace ns { + using B = int; + int C = 0; + static_assert(requires { typename A; typename B; typename ::A; }); + static_assert(!requires { typename ns::A; }); // expected-error{{'ns::A' does not name a type}} + static_assert(!requires { typename ::B; }); // expected-error{{'::B' does not name a type}} + static_assert(!requires { typename C; }); // expected-error{{'C' does not name a type}} +} + +// member type lookups + +static_assert(requires { typename identity::type; typename ::identity::type; }); +static_assert(!requires { typename identity::typr; }); // expected-error{{'identity::typr' does not name a type}} +static_assert(!requires { typename ::identity::typr; }); // expected-error{{'::identity::typr' does not name a type}} + +template requires requires { typename T::type; } +// expected-note@-1 {{because 'T::type' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +// expected-note@-2 {{because 'T::type' would be invalid: no type named 'type' in 'C'}} +// expected-note@-3 {{because 'T::type' would be invalid: typename specifier refers to non-type member 'type' in 'D'}} +// expected-note@-4 {{in instantiation of template class 'invalid' requested here}} +// expected-note@-5 {{in instantiation of requirement here}} +// expected-note@-6 {{while substituting template arguments into constraint expression here}} +// expected-note@-7 {{because 'T::type' would be invalid}} +struct r1 {}; + +using r1i1 = r1>; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} +using r1i3 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = D]}} + +template struct invalid { typename T::type x; }; +// expected-error@-1 {{typename specifier refers to non-type member 'type' in 'D'}} +using r1i5 = r1>; +// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = invalid]}} +// expected-note@-2 {{while checking constraint satisfaction for template 'r1 >' required here}} + +// mismatching template arguments + +template requires requires { typename identity; } // expected-note{{because 'identity' would be invalid: too many template arguments for class template 'identity'}} +struct r2 {}; + +using r2i1 = r2; +using r2i2 = r2; +using r2i3 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = ]}} + +namespace ns2 { + template struct identity {}; + + template requires requires { typename identity; } // expected-note 2{{because 'identity' would be invalid: too few template arguments for class template 'identity'}} + struct r4 {}; + + using r4i1 = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} +} + +using r4i2 = ns2::r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} + +using E = int; +template requires requires { typename E; } // expected-error{{template arguments provided for non-template 'E'}} +struct r5v1 {}; +template requires requires { typename ::E; } // expected-error{{template arguments provided for non-template 'E'}} +struct r5v2 {}; + +template requires (sizeof(T) == 1) +struct chars_only {}; + +template requires requires { typename chars_only; } // expected-note{{because 'chars_only' would be invalid: constraints not satisfied for class template 'chars_only' [with T = int]}} +struct r6 {}; + +using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with T = int]}} + +template int F = 0; + +static_assert(!requires { typename F; }); // expected-error{{'F' refers to a variable template and not a type template}} +static_assert(!requires { typename ::F; }); // expected-error{{'::F' refers to a variable template and not a type template}} + +struct G { template static T temp; }; + +template requires requires { typename T::template temp; } +// expected-note@-1{{because 'T::temp' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +// expected-note@-2{{because 'T::temp' would be invalid: no member named 'temp' in 'D'}} +// expected-note@-3{{because 'T::temp' would be invalid: template name refers to non-type template 'G::template temp'}} +struct r7 {}; + +using r7i1 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = int]}} +using r7i2 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = D]}} +using r7i3 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = G]}} + +template struct H; + +template requires requires { typename H; } +struct r8 {}; + +using r8i = r8; + +template struct I { struct incomplete; }; // expected-note{{member is declared here}} + +static_assert(!requires { I::incomplete::inner; }); // expected-error{{implicit instantiation of undefined member 'I::incomplete'}} + +template requires requires { typename I::incomplete::inner; } // expected-note{{because 'I::incomplete::inner' would be invalid: implicit instantiation of undefined member 'I::incomplete'}} +struct r9 {}; + +using r9i = r9; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}} + +namespace ns3 { + struct X { }; // expected-note 2{{candidate found by name lookup is 'ns3::X'}} +} + +struct X { using inner = int; }; // expected-note 2{{candidate found by name lookup is 'X'}} + +using namespace ns3; +static_assert(requires { typename X; }); // expected-error{{reference to 'X' is ambiguous}} +static_assert(requires { typename X::inner; }); // expected-error{{reference to 'X' is ambiguous}} + +// naming a type template specialization in a type requirement does not require +// it to be complete and should not care about partial specializations. + +template +struct Z; + +template requires (sizeof(T) >= 1) +struct Z {}; // expected-note{{partial specialization matches [with T = int]}} + +template requires (sizeof(T) <= 4) +struct Z {}; // expected-note{{partial specialization matches [with T = int]}} + +Z x; // expected-error{{ambiguous partial specializations of 'Z'}} + +static_assert(requires { typename Z; }); + +// C++ [expr.prim.req.type] Example +namespace std_example { + template struct S; + // expected-note@-1 {{because 'S' would be invalid: no type named 'type' in 'std_example::has_inner}} + template using Ref = T&; // expected-note{{because 'Ref' would be invalid: cannot form a reference to 'void'}} + template concept C1 = + requires { + typename T::inner; + // expected-note@-1 {{because 'T::inner' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@-2 {{because 'T::inner' would be invalid: no type named 'inner' in 'std_example::has_type'}} + }; + template concept C2 = requires { typename S; }; + template concept C3 = requires { typename Ref; }; + + struct has_inner { using inner = int;}; + struct has_type { using type = int; }; + struct has_inner_and_type { using inner = int; using type = int; }; + + static_assert(C1 && C2 && C3); + template struct C1_check {}; + // expected-note@-1 {{because 'int' does not satisfy 'C1'}} + // expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}} + template struct C2_check {}; + // expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}} + template struct C3_check {}; + // expected-note@-1 {{because 'void' does not satisfy 'C3'}} + using c1 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}} + using c2 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}} + using c3 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}} + using c4 = C3_check; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}} +} \ No newline at end of file Index: test/Parser/cxx2a-concepts-requires-expr.cpp =================================================================== --- /dev/null +++ test/Parser/cxx2a-concepts-requires-expr.cpp @@ -0,0 +1,167 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +bool r1 = requires () {}; +// expected-error@-1 {{a requires expression must contain at least one requirement}} + +bool r2 = requires { requires true; }; + +bool r3 = requires (int a, ...) { requires true; }; +// expected-error@-1 {{varargs not allowed in requires expression}} + +template +bool r4 = requires (T... ts) { requires true; }; + +bool r5 = requires (bool c, int d) { c; d; }; + +bool r6 = requires (bool c, int d) { c; d; } && decltype(d){}; +// expected-error@-1 {{use of undeclared identifier 'd'}} + +bool r7 = requires (bool c) { c; (requires (int d) { c; d; }); d; } && decltype(c){} && decltype(d){}; +// expected-error@-1 2{{use of undeclared identifier 'd'}} +// expected-error@-2 {{use of undeclared identifier 'c'}} + +bool r8 = requires (bool, int) { requires true; }; + +bool r9 = requires (bool a, int a) { requires true; }; +// expected-error@-1 {{redefinition of parameter 'a'}} +// expected-note@-2 {{previous declaration is here}} + +bool r10 = requires (struct new_struct { int x; } s) { requires true; }; +// expected-error@-1 {{'new_struct' cannot be defined in a parameter type}} + +bool r11 = requires (int x(1)) { requires true; }; +// expected-error@-1 {{expected parameter declarator}} +// expected-error@-2 {{expected ')'}} +// expected-note@-3 {{to match this '('}} + +bool r12 = requires (int x = 10) { requires true; }; +// expected-error@-1 {{default arguments not allowed for parameters of a requires expression}} + +bool r13 = requires (int f(int)) { requires true; }; + +bool r14 = requires (int (*f)(int)) { requires true; }; + +bool r15 = requires (10) { requires true; }; +// expected-error@-1 {{expected parameter declarator}} + +bool r16 = requires (auto x) { requires true; }; +// expected-error@-1 {{'auto' not allowed in requires expression parameter}} + +bool r17 = requires (auto [x, y]) { requires true; }; +// expected-error@-1 {{'auto' not allowed in requires expression parameter}} +// expected-error@-2 {{use of undeclared identifier 'x'}} + +using a = int; + +bool r18 = requires { typename a; }; + +bool r19 = requires { typename ::a; }; + +template struct identity { using type = T; }; + +template using identity_t = T; + +bool r20 = requires { + typename identity::type; + typename identity; + typename ::identity_t; +}; + +struct s { bool operator==(const s&); ~s(); }; + +bool r21 = requires { typename s::operator==; }; +// expected-error@-1 {{expected identifier or template-id in type requirement}} + +bool r22 = requires { typename s::~s; }; +// expected-error@-1 {{expected identifier or template-id in type requirement}} + +template +bool r23 = requires { typename identity::temp; }; +// expected-error@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}} + +template +bool r24 = requires { + typename identity::template temp; + typename identity::template temp; // expected-error{{expected identifier or template-id in type requirement}} +}; + +bool r25 = requires { ; }; +// expected-error@-1 {{expected expression}} + +bool r26 = requires { {}; }; +// expected-error@-1 {{expected expression}} + +bool r27 = requires { { 0 } noexcept; }; + +bool r28 = requires { { 0 } noexcept noexcept; }; +// expected-error@-1 {{expected '->' before expression type requirement}} + +bool r29 = requires { { 0 } noexcept int; }; +// expected-error@-1 {{expected '->' before expression type requirement}} + +template +concept C1 = true; + +bool r30 = requires { { 0 } noexcept -> const volatile C1; }; + +bool r31 = requires { { 0 } noexcept -> const C1 volatile; }; + +bool r32 = requires { { 0 } noexcept -> C1 const volatile; }; + +bool r33 = requires { { 0 } noexcept -> const volatile C1 const volatile; }; // expected-warning {{duplicate 'const' declaration specifier}} expected-warning {{duplicate 'volatile' declaration specifier}} + +bool r34 = requires { { 0 } noexcept -> const volatile C1[1]; }; + +bool r35 = requires { { 0 } noexcept -> const C1 volatile[1]; }; + +bool r36 = requires { { 0 } noexcept -> C1 const volatile[1]; }; + +bool r37 = requires { { 0 } noexcept -> const volatile C1 const volatile[1]; }; // expected-warning {{duplicate 'const' declaration specifier}} expected-warning {{duplicate 'volatile' declaration specifier}} + +bool r38 = requires { { 0 } noexcept -> const C1 *(int); }; + +template +T i1 = 0; + +bool r39 = requires { { 0 } noexcept -> const volatile i1; }; +// expected-error@-1 {{unknown type name 'i1'}} + +bool r40 = requires { { 0 } noexcept -> const int; }; + +bool r41 = requires { { 0 } noexcept -> const volatile decltype(i1); }; + +template +concept C2 = true; + +bool r42 = requires { { 0 } noexcept -> const volatile C2; }; +// expected-error@-1 {{concept 'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} + +bool r43 = requires { { 0 } noexcept -> const volatile C1<>; }; + +bool r44 = requires { { 0 } noexcept -> const volatile C2; }; + +bool r45 = requires { requires false, 1; }; +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r46 = requires { 0 noexcept; }; +// expected-error@-1 {{'noexcept' can only be used in a compound requirements (with '{' '}' around the expression)}} + +bool r47 = requires { 0 int; }; +// expected-error@-1 {{unexpected int after expression. Did you intend to use a compound requirement? (with '{' '}' around the expression)}} + +bool r48 = requires { requires true }; +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r49 = requires (bool b) { requires sizeof(b) == 1; }; + +bool r50 = requires { { 0 } -> auto&&; }; +// expected-error@-1 {{'auto' not allowed in expression type requirement}} + +bool r51 = requires { { 0 } -> decltype(auto); }; +// expected-error@-1 {{'decltype(auto)' not allowed in expression type requirement}} + +bool r52 = requires { { 0 } -> const auto *; }; +// expected-error@-1 {{'auto' not allowed in expression type requirement}} + +void r53(bool b) requires requires { 1 } {} +// expected-error@-1 {{expected ';' at end of requirement}} \ No newline at end of file Index: test/SemaTemplate/instantiate-requires-expr.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/instantiate-requires-expr.cpp @@ -0,0 +1,279 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify -Wno-unused-value + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +// We use a hack in this file to make the compiler print out the requires +// expression after it has been instantiated - we put false_v as +// the requires clause of a class template, then instantiate the template. +// The requirement will not be satisfied, and the explaining diagnostic will +// print out false_v in its raw form (the false_v serves to +// prevent the diagnostic from elaborating on why the requires expr wasn't +// satisfied). + +template +constexpr bool false_v = false; + +template +using void_t = void; + +// Check that requires parameters are instantiated correctly. + +template requires +false_v; }> +// expected-note@-1 {{because 'false_v; }>' evaluated to false}} +// expected-note@-2 {{because 'false_v; }>' evaluated to false}} +struct r1 {}; + +using r1i1 = r1; // expected-error {{constraints not satisfied for class template 'r1' [with T = int]}} +using r1i2 = r1; // expected-error {{constraints not satisfied for class template 'r1' [with T = char]}} + +// Check that parameter packs work. + +template requires +false_v +// expected-note@-1 {{because 'false_v'}} +// expected-note@-2 {{because 'false_v' evaluated to false}} +struct r2 {}; + +using r2i1 = r2; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = ]}} +using r2i2 = r2; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = ]}} + +template requires +false_v<(requires (Ts ts) {requires sizeof(ts) != 0;} && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +// expected-note@-2 {{because 'false_v' evaluated to false}} +struct r3 {}; + +using r3i1 = r3; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = ]}} +using r3i2 = r3; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = ]}} + +template +struct identity { using type = T; }; + +namespace type_requirement { + struct A {}; + + // check that nested name specifier is instantiated correctly. + template requires false_v // expected-note{{because 'false_v::type; }>' evaluated to false}} + struct r1 {}; + + using r1i = r1>; // expected-error{{constraints not satisfied for class template 'r1' [with T = identity]}} + + // check that template argument list is instantiated correctly. + template + struct contains_template { + template requires is_same_v, U> + using temp = int; + }; + + template requires + false_v; }> + // expected-note@-1 {{because 'false_v::temp >; }>' evaluated to false}} + // expected-note@-2 {{because 'false_v::temp >; }>' evaluated to false}} + struct r2 {}; + + using r2i1 = r2>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template]}} + using r2i2 = r2>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template]}} + + // substitution error occurs, then requires expr is instantiated again + + template + struct a { + template requires (requires { typename T::a::a; }, false) + // expected-note@-1{{because 'requires { typename <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // Parameter pack inside expr + template requires + false_v<(requires { typename Ts::type; } && ...)> + // expected-note@-1 {{because 'false_v::type; } && requires { typename identity::type; } && requires { typename <>; }>' evaluated to false}} + struct r5 {}; + + using r5i = r5, identity, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = , identity, short>]}} + template requires + false_v<(requires { typename void_t; } && ...)> // expected-note{{because 'false_v; } && requires { typename void_t; }>' evaluated to false}} + struct r6 {}; + + using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = ]}} + + template requires + false_v<(requires { typename Ts::template aaa; } && ...)> + // expected-note@-1 {{because 'false_v>; } && requires { typename <>; }>' evaluated to false}} + struct r7 {}; + + using r7i = r7; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = ]}} +} + +namespace expr_requirement { + // check that compound/simple requirements are instantiated correctly. + + template requires false_v + // expected-note@-1 {{because 'false_v' evaluated to false}} + // expected-note@-2 {{because 'false_v>; { sizeof(T) }; }>' evaluated to false}} + struct r1 {}; + + using r1i1 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = void]}} + + // substitution error occurs in expr, then expr is instantiated again. + + template + struct a { + template requires (requires { sizeof(T::a); }, false) // expected-note{{because 'requires { <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // check that trailing-return-type requirement is instantiated correctly. + + template requires + false_v typename T::type; }> + // expected-note@-1 {{because 'false_v) } -> typename identity::type; }>' evaluated to false}} + struct r2 {}; + + using r2i = r2>; // expected-error{{constraints not satisfied for class template 'r2' [with T = identity]}} + + // check that constrained-parameter is instantiated correctly. + + template + concept C1 = is_same_v; + + template requires false_v C1; }> + // expected-note@-1 {{because 'false_v C1; }>' evaluated to false}} + // expected-note@-2 {{because 'false_v C1; }>' evaluated to false}} + struct r3 {}; + + using r3i1 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = int]}} + using r3i2 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = double]}} + + void foo(int, double) {} + + template requires + false_v C1(int, T); }> + // expected-note@-1 {{because 'false_v C1 (int, double); }>' evaluated to false}} + // expected-note@-2 {{because 'false_v C1 (int, int); }>' evaluated to false}} + struct r4 {}; + + using r4i1 = r4; // expected-error{{constraints not satisfied for class template 'r4' [with T = double]}} + using r4i2 = r4; // expected-error{{constraints not satisfied for class template 'r4' [with T = int]}} + + // substitution error occurs in return type requirement, then requires expr is + // instantiated again. + + template + struct b { + template requires (requires { { 0 } -> typename T::a; }, false) // expected-note{{because 'requires { { 0 } -> <>; } , false' evaluated to false}} + struct r {}; + }; + + using bri = b::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // Parameter pack inside expr + template requires + false_v<(requires { { sizeof(Ts) } noexcept -> int; } && ...)> + // expected-note@-1 {{because 'false_v int; } && requires { { sizeof(short) } noexcept -> int; }>' evaluated to false}} + struct r5 {}; + + using r5i = r5; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = ]}} + + template requires + false_v<(requires { sizeof(Ts); } && ...)> // expected-note{{because 'false_v' evaluated to false}} + struct r6 {}; + + using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = ]}} + + template requires + false_v<(requires { { 0 } noexcept -> Ts; } && ...)> + // expected-note@-1{{because 'false_v int; } && requires { { 0 } noexcept -> unsigned int; } && requires { { 0 } noexcept -> long; }>' evaluated to false}} + struct r7 {}; + + using r7i = r7; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = ]}} + + template requires + false_v<(requires { { 0 } noexcept -> const C1(Ts); } && ...)> + // expected-note@-1 {{because 'false_v const C1 (int); } && requires { { 0 } noexcept -> const C1 (unsigned int); }>' evaluated to false}} + struct r8 {}; + + using r8i = r8; // expected-error{{constraints not satisfied for class template 'r8' [with Ts = ]}} + + template + concept C2 = true; + + template requires + false_v<(requires { { 0 } noexcept -> C2[3]; } && ...)> + // expected-note@-1 {{because 'false_v C2 [3]; } && requires { { 0 } noexcept -> C2 [3]; }>' evaluated to false}} + struct r9 {}; + + using r9i = r9; // expected-error{{constraints not satisfied for class template 'r9' [with Ts = ]}} + + template requires + false_v<(requires { { sizeof(Ts) } noexcept -> C2[sizeof(Ts)]; } && ...)> + // expected-note@-1 {{because 'false_v C2 [4]; } && requires { { sizeof(long) } noexcept -> C2 [8]; }>' evaluated to false}} + struct r10 {}; + + using r10i = r10; // expected-error{{constraints not satisfied for class template 'r10' [with Ts = ]}} + + template requires + false_v<(requires { { sizeof(Ts) } noexcept -> const volatile Ts; } && ...)> + // expected-note@-1 {{because 'false_v const volatile int; } && requires { { sizeof(unsigned int) } noexcept -> const volatile unsigned int; }>' evaluated to false}} + struct r11 {}; + + using r11i = r11; // expected-error{{constraints not satisfied for class template 'r11' [with Ts = ]}} +} + +namespace nested_requirement { + // check that constraint expression is instantiated correctly + template requires false_v // expected-note{{because 'false_v' evaluated to false}} + struct r1 {}; + + using r1i = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + + // substitution error occurs in expr, then expr is instantiated again. + template + struct a { + template requires + (requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // Parameter pack inside expr + template requires + false_v<(requires { requires sizeof(Ts) == 0; } && ...)> + // expected-note@-1 {{because 'false_v' evaluated to false}} + struct r2 {}; + + using r2i = r2; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = ]}} +} + +// Parameter pack inside multiple requirements +template requires +false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +struct r4 {}; + +using r4i = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} + +template requires +false_v<(requires(Ts t) { requires sizeof(t) == 0; t++; } && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +struct r5 {}; + +using r5i = r5; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = ]}} + +template requires +false_v<(requires(T t) { T{t}; })> // T{t} creates an "UnevaluatedList" context. +// expected-note@-1 {{because 'false_v<(requires (int t) { int{t}; })>' evaluated to false}} +struct r6 {}; + +using r6i = r6; +// expected-error@-1 {{constraints not satisfied for class template 'r6' [with T = int]}} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -6265,6 +6265,7 @@ case Decl::PragmaDetectMismatch: case Decl::UsingPack: case Decl::Concept: + case Decl::RequiresExprBody: return C; // Declaration kinds that don't make any sense here, but are Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -257,6 +257,7 @@ case Stmt::TypeTraitExprClass: case Stmt::CoawaitExprClass: case Stmt::ConceptSpecializationExprClass: + case Stmt::RequiresExprClass: case Stmt::DependentCoawaitExprClass: case Stmt::CoyieldExprClass: case Stmt::CXXBindTemporaryExprClass: