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
  • [PATCH] D43357: [... Saar Raz via Phabricator via cfe-commits
    • [PATCH] D433... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D433... Saar Raz via Phabricator via cfe-commits
    • [PATCH] D433... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to