rsmith added a comment. Looking promising, but this patch will need some rework: you need to track the trailing requires clause on the `Declarator` itself, not on the `DeclChunk`, because it's not part of the `DeclChunk` (and may appear in contexts where there is no function chunk).
================ Comment at: include/clang/AST/Decl.h:1793-1796 + /// \brief The constraint-expression introduced by the trailing + /// requires-clause provided in the function declaration, if any. + Expr *TrailingRequiresClause; + ---------------- Please find a way to store this that doesn't make all `FunctionDecl`s 8 bytes larger. At the very least, you can move this to `CXXMethodDecl`, but perhaps it'd be better to include this as an optional component in the `DeclInfo`. ================ Comment at: include/clang/AST/Decl.h:1795 + /// requires-clause provided in the function declaration, if any. + Expr *TrailingRequiresClause; + ---------------- Not necessarily for this patch, but you'll need to think about how to represent a not-yet-instantiated trailing requires clause. (A requires clause isn't instantiated as part of instantiating the function it's attached to; it's instantiated later, when needed, like an exception specification or default argument.) ================ Comment at: include/clang/Basic/DiagnosticSemaKinds.td:2433 +def err_constrained_virtual_method : Error< + "a virtual function must not have a requires clause">; +def err_reference_to_function_with_unsatisfied_constraints : Error< ---------------- "virtual function cannot have a requires clause" would be more in line with how we usually word diagnostics. ================ Comment at: include/clang/Basic/DiagnosticSemaKinds.td:2435 +def err_reference_to_function_with_unsatisfied_constraints : Error< + "invalid reference to function %0 - constraints not satisfied">; ---------------- We generally separate a general problem and a specific problem with a colon, not a hyphen, in diagnostics. ================ Comment at: lib/Parse/ParseDecl.cpp:6098-6165 + if (getLangOpts().CPlusPlus11 && Tok.is(tok::arrow)) { + ParseTrailingReturn(); + } + // Parse trailing requires-clause[opt]. + if (getLangOpts().ConceptsTS && Tok.is(tok::kw_requires)) { + LocalEndLoc = Tok.getLocation(); + ConsumeToken(); ---------------- This is the wrong place to parse a //requires-clause//: a //requires-clause// is a trailing part of the overall //init-declarator// or //member-declarator//, not part of the function declarator chunk. For example: ``` using T = void (); T x requires true; // ok, but you reject void (f() requires true); // ill-formed but you accept void (f()) requires true; // ok but you reject ``` ================ Comment at: lib/Parse/ParseDecl.cpp:6102 + // Parse trailing requires-clause[opt]. + if (getLangOpts().ConceptsTS && Tok.is(tok::kw_requires)) { + LocalEndLoc = Tok.getLocation(); ---------------- The `ConceptsTS` check here is redundant. If we see a `kw_requires` token, the feature is enabled. ================ Comment at: lib/Parse/ParseDecl.cpp:6106-6110 + TentativeParsingAction TPA(*this); + DiagnosticErrorTrap Trap(Diags); + Diags.setSuppressAllDiagnostics(true); + TrailingRequiresClause = ParseConstraintExpression(); + Diags.setSuppressAllDiagnostics(false); ---------------- This is not a reasonable way to deal with parse errors. Parsing an expression can have non-local effects, and suppressing diagnostics like this is not a safe way to provide error recovery. You're also suppressing all warnings within the expression, which is also inappropriate. Instead, you should just parse the //requires-clause//. If you parse it correctly (not as a //constraint-expression//) the parse will stop before a possible `->` token anyway, because a top-level `->` expression is not permitted in a //constraint-logical-or-expression//. ================ Comment at: lib/Parse/ParseDecl.cpp:6109 + Diags.setSuppressAllDiagnostics(true); + TrailingRequiresClause = ParseConstraintExpression(); + Diags.setSuppressAllDiagnostics(false); ---------------- This isn't right: a //constraint-logical-or-expression// is required here, not a //constraint-expression//. ================ Comment at: lib/Parse/ParseDecl.cpp:6112-6113 + + if (!Trap.hasErrorOccurred() && TrailingRequiresClause.isUsable() + && !TrailingRequiresClause.isInvalid()) { + TPA.Commit(); ---------------- Clang style puts the `&&` on the previous line. ================ Comment at: lib/Parse/ParseTentative.cpp:955-956 + // A requires clause indicates a function declaration. + if (ParenCount) { + SkipUntil(tok::l_paren); + } else { ---------------- This is wrong.`ParenCount` isn't just *your* parens, it's all enclosing ones. And I don't think you need this check at all: the `requires` is either part of a suitable declarator or it's ill-formed, so we can just disambiguate as a declarator (that way we'll correctly handle all valid programs and correctly reject all ill-formed ones, with a diagnostic saying that the nested declarator can't have a requires-clause). ================ Comment at: lib/Sema/SemaDecl.cpp:7848-7851 + Expr *TrailingRequiresClause = D.isFunctionDeclarator() + && D.hasTrailingRequiresClause() ? + D.getFunctionTypeInfo().getTrailingRequiresClause() + : nullptr; ---------------- Please parenthesize the `&&` subexpression and run this through clang-format. ================ Comment at: lib/Sema/SemaDecl.cpp:8377-8381 + } else if (D.hasTrailingRequiresClause()) { + // C++2a [class.virtual]p6 + // A virtual method shall not have a requires-clause. + Diag(NewFD->getTrailingRequiresClause()->getLocStart(), + diag::err_constrained_virtual_method); ---------------- This is the wrong place for this check. We don't yet know whether the function is virtual here in general. A function can become virtual due to template instantiation: ``` template<typename T> struct A : T { void f() requires true; }; struct B { virtual void f(); }; template struct A<B>; // error, A<B>::f is constrained and virtual ``` This is perhaps a wording defect: it's not clear that `A::f()` really should override `B::f()`, but that is the consequence of the current rules. I've posted a question to the core reflector. ================ Comment at: lib/Sema/SemaExpr.cpp:2716-2732 + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (Expr *RC = FD->getTrailingRequiresClause()) { + ConstraintSatisfaction Satisfaction; + bool Failed = S.CheckConstraintSatisfaction(RC, Satisfaction); + if (Failed) + // A diagnostic will have already been generated (non-constant + // constraint expression, for example) ---------------- Why do we ever fail this check? Overload resolution should never select a function with unsatisfied constraints, and likewise for taking the address of / binding a reference to such a function. ================ Comment at: lib/Sema/SemaOverload.cpp:1208-1221 + Expr *NewRC = New->getTrailingRequiresClause(), + *OldRC = Old->getTrailingRequiresClause(); + if ((NewRC != nullptr) != (OldRC != nullptr)) + // RC are most certainly different - these are overloads. + return true; + + if (NewRC) { ---------------- This check is too late. We've already passed some `return false;`s at this point. (Consider changing the CUDA checks to only early-return on failure rather than moving this check earlier.) ================ Comment at: lib/Sema/SemaOverload.cpp:6106 + Expr *RequiresClause = Function->getTrailingRequiresClause(); + if (LangOpts.ConceptsTS && RequiresClause) { + ConstraintSatisfaction Satisfaction; ---------------- The `ConceptsTS` check here doesn't make any sense to me: if you've already parsed a requires clause, you should always check it. ================ Comment at: lib/Sema/SemaOverload.cpp:6619-6629 + + Expr *RequiresClause = Method->getTrailingRequiresClause(); + if (LangOpts.ConceptsTS && RequiresClause) { + ConstraintSatisfaction Satisfaction; + if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + !Satisfaction.IsSatisfied) { + Candidate.Viable = false; ---------------- According to [over.match.viable]p3, constraint satisfaction should be checked //before// we attempt to form implicit conversion sequences. This can affect the validity of code, if the associated constraints disable the function in a case where implicit conversion sequence formation would result in the program being ill-formed due to triggering a bad template instantiation (with an error outside an immediate context). ================ Comment at: lib/Sema/SemaOverload.cpp:9123-9135 + if (Cand1.Function && Cand2.Function) { + Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); + Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); + if (RC1 && RC2) { + bool MoreConstrained1 = S.IsMoreConstrained(Cand1.Function, RC1, + Cand2.Function, RC2); + bool MoreConstrained2 = S.IsMoreConstrained(Cand2.Function, RC2, ---------------- According to [over.match.best], this check belongs immediately after the "more specialized template" check, not down here. ================ Comment at: lib/Sema/SemaTemplateInstantiateDecl.cpp:1637-1646 + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + if (TrailingRequiresClause) { + ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, + TemplateArgs); + if (!SubstRC.isUsable() || SubstRC.isInvalid()) + return nullptr; + TrailingRequiresClause = SubstRC.get(); ---------------- This is wrong. Per [temp.decls]p2, //requires-clause//s are instantiated separately from their associated functions. That doesn't need to be handled in this change, but please at least include a FIXME. ================ Comment at: lib/Sema/SemaTemplateInstantiateDecl.cpp:1641 + TemplateArgs); + if (!SubstRC.isUsable() || SubstRC.isInvalid()) + return nullptr; ---------------- Checking `!isUsable()` here is wrong. (1: `SubstExpr` should never produce a valid-but-null `Expr*`, 2: even if it did, that means it succeeded, which means it's wrong for you to return `nullptr` without producing a diagnostic.) ================ Comment at: lib/Sema/SemaTemplateInstantiateDecl.cpp:1965 + TemplateArgs); + if (!SubstRC.isUsable() || SubstRC.isInvalid()) + return nullptr; ---------------- (Likewise.) ================ Comment at: lib/Sema/SemaTemplateVariadic.cpp:884-886 + if (Chunk.Fun.hasTrailingRequiresClause() + &&Chunk.Fun.TrailingRequiresClause->containsUnexpandedParameterPack()) + return true; ---------------- Please reformat. Repository: rC Clang https://reviews.llvm.org/D43357 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits