This is an archive of the discontinued LLVM Phabricator instance.

[Concepts] Function trailing requires clauses
ClosedPublic

Authored by saar.raz on Feb 15 2018, 3:03 PM.

Details

Summary

Function trailing requires clauses now parsed, supported in overload resolution and when calling, referencing and taking the address of functions or function templates.Does not directly affect code generation yet.
Depends on D41910.

Diff Detail

Event Timeline

saar.raz created this revision.Feb 15 2018, 3:03 PM
saar.raz retitled this revision from Function trailing requires clauses now parsed, supported in overload resolution and when calling, referencing and taking the address of functions or function templates. Does not directly affect code generation yet. to Function trailing requires clauses.Feb 15 2018, 3:30 PM
saar.raz edited the summary of this revision. (Show Details)
saar.raz retitled this revision from Function trailing requires clauses to [Concepts] Function trailing requires clauses.Mar 10 2018, 4:25 AM
saar.raz updated this revision to Diff 147188.May 16 2018, 2:57 PM
  • Fixed handling of empty/non-expression after trailing requires keyword.
  • Unified satisfaction check for templated/non-templated constraint exprs
saar.raz updated this revision to Diff 159349.Aug 6 2018, 12:07 PM
  • Fix bad diagnostic detection and suppression
rsmith added a comment.Aug 6 2018, 3:42 PM

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).

include/clang/AST/Decl.h
1810–1813

Please find a way to store this that doesn't make all FunctionDecls 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.

1812

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.)

include/clang/Basic/DiagnosticSemaKinds.td
2471

"virtual function cannot have a requires clause" would be more in line with how we usually word diagnostics.

2473

We generally separate a general problem and a specific problem with a colon, not a hyphen, in diagnostics.

lib/Parse/ParseDecl.cpp
6286–6353

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
6290

The ConceptsTS check here is redundant. If we see a kw_requires token, the feature is enabled.

6294–6298

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.

6297

This isn't right: a constraint-logical-or-expression is required here, not a constraint-expression.

6300–6301

Clang style puts the && on the previous line.

lib/Parse/ParseTentative.cpp
1011–1012

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).

lib/Sema/SemaDecl.cpp
7970–7973

Please parenthesize the && subexpression and run this through clang-format.

8545–8549

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.

lib/Sema/SemaExpr.cpp
2892–2908

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.

lib/Sema/SemaOverload.cpp
1272–1273

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.)

6235

The ConceptsTS check here doesn't make any sense to me: if you've already parsed a requires clause, you should always check it.

6738–6748

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).

9360–9372

According to [over.match.best], this check belongs immediately after the "more specialized template" check, not down here.

lib/Sema/SemaTemplateInstantiateDecl.cpp
1707–1716

This is wrong. Per [temp.decls]p2, requires-clauses are instantiated separately from their associated functions. That doesn't need to be handled in this change, but please at least include a FIXME.

1711

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.)

2031

(Likewise.)

lib/Sema/SemaTemplateVariadic.cpp
912–914

Please reformat.

saar.raz added inline comments.Aug 7 2018, 1:40 PM
lib/Sema/SemaDecl.cpp
8545–8549

I don't really see why A::f() should override B::f() indeed - since it is not marked virtual nor override, shouldn't it just hide B::f()? or am I missing something here?

rsmith added inline comments.Aug 7 2018, 2:08 PM
lib/Sema/SemaDecl.cpp
8545–8549

Functions override base class virtual functions if they have matching name, parameter types, cv-qualifiers and ref-qualifiers, *regardless* of whether they're declared virtual etc. Eg:

struct A { virtual void f(); };
struct B : A { void f(); };

B::f overrides A::f, and as a consequence, B::f is implicitly a virtual function. See [class.virtual]p2 and its footnote.

Note that the determination of whether the derived class function overrides the base class function doesn't care about the associated constraints on the derived class function. (After checking with the core reflector, general consensus seems to be that this is the right rule, and my previous example should indeed be ill-formed because it declares a constrained virtual function.)

Herald added a reviewer: shafik. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript
saar.raz updated this revision to Diff 195033.Apr 13 2019, 1:00 PM
  • Add diagnostic explaining the unintuitive subsumption rules when user might be relying on expression equivalence for odering
  • Fixed incorrect checking of trailing requires clause subsumption
  • Fixed missing conversion constraint checking
  • Fixed constraint checking in function templates to recognize function parameters
  • Address CR comments by rsmith
    • Move TrailingRequiresClause to ExtInfo
    • Fixed diagnostic styling
    • Correct checking of constrained virtual functions
    • Small formatting fixes
    • Parse constraint-logical-or-expressions in requires clauses
    • Parse the requires clause as part of the init-declarator, member-declarator or function-definition
    • No dirty diagnostic suppression when trying to parse switched-order trailing requires and return type
martong requested changes to this revision.Apr 16 2019, 1:37 AM
martong added inline comments.
lib/AST/ASTImporter.cpp
3053

First you have to import the trailing requires clause and then pass the imported Expr here.
I think you can reuse the importSeq call above.

This revision now requires changes to proceed.Apr 16 2019, 1:37 AM
saar.raz updated this revision to Diff 195978.Apr 20 2019, 12:50 PM
  • Remove #if from test
saar.raz updated this revision to Diff 196157.Apr 22 2019, 4:19 PM
  • Address CR comment
  • Adjust to new getAssociatedConstraints interface
  • Add destructor and conversion operator tests
martong accepted this revision.Apr 23 2019, 7:32 AM

From the ASTImporter point of view it looks good, thanks for the changes!

This revision is now accepted and ready to land.Apr 23 2019, 7:32 AM
martong resigned from this revision.Apr 23 2019, 7:33 AM
This revision now requires review to proceed.Apr 23 2019, 7:33 AM
saar.raz updated this revision to Diff 203897.Jun 10 2019, 2:06 PM

Introduce function parameters into scope when parsing function requires clause

saar.raz updated this revision to Diff 204172.Jun 11 2019, 2:51 PM

Allow accessing 'this' in requires clause when appropriate.

saar.raz updated this revision to Diff 204953.Jun 16 2019, 9:32 AM

Add support for lambda expression trailing requires clauses

saar.raz marked 20 inline comments as done.Oct 14 2019, 2:35 PM
saar.raz updated this revision to Diff 224911.Oct 14 2019, 2:56 PM

Addressed remaining comments by rsmith

rsmith added inline comments.Dec 14 2019, 4:30 PM
clang/include/clang/Basic/DiagnosticParseKinds.td
179–180 ↗(On Diff #224911)

I think the "did you forget parentheses?" part here may be irritating to users who didn't know they needed parentheses at all -- it may be read as patronizing by some. Can we rephrase as "parentheses are required around a top-level unary %0 expression in a requires clause" or something like that?

clang/include/clang/Basic/DiagnosticSemaKinds.td
2515 ↗(On Diff #224911)

Don't use a colon to indicate a location. Diagnostics are formatted in many different ways and this won't always make sense. Also, where possible, don't pretty-print expressions in diagnostics; underline the source range instead.

2516 ↗(On Diff #224911)

Similarly, this note presentation won't always make sense. Can we somehow rephrase these notes so they're more presenting the facts and less telling the user what to do?

3886–3895 ↗(On Diff #224911)

Don't repeat this %select; use %sub{select_ovl_candidate_kind} instead. (The enum and the appropriate %select have already changed at least twice since this was copy-pasted...)

clang/include/clang/Parse/Parser.h
1652 ↗(On Diff #224911)

Don't repeat the name of the entity in the doc comment. (This is an old style that we're phasing out; new code shouldn't do it.)

1653 ↗(On Diff #224911)

We would usually call this sort of enumeration SomethingKind not SomethingOption.

1658 ↗(On Diff #224911)

Please don't use Type as the name of something that's not a language-level type.

2700 ↗(On Diff #224911)

Line-wrapped parameters should start on the same column as the first parameter.

clang/lib/Parse/ParseDecl.cpp
2068–2069 ↗(On Diff #224911)

We should check with GCC to see which side of the requires-clause they expect this.

For the second and subsequent declarations, we expect the asm label before the requires clause; here we parse the asm label after the requires clause.

2257–2258 ↗(On Diff #224911)

We already parsed this for the first declarator in the group. Do we accidentally permit two requires clauses on the first declarator?

clang/lib/Parse/ParseDeclCXX.cpp
3799–3810 ↗(On Diff #224911)

This scope-building code should be in Sema, not in the parser. (Consider adding an ActOnStartTrailingRequiresClause / ActOnFinishTrailingRequiresClause pair.)

3801 ↗(On Diff #224911)

Add braces for this multi-line if.

3825 ↗(On Diff #224911)

Also tok::colon for constructors.

clang/lib/Parse/ParseExpr.cpp
349–350 ↗(On Diff #224911)

The parser shouldn't be encoding this kind of semantic knowledge. Please move this to Sema. (This is also not really a very general way to detect a function-like name: checking for an expression whose type is a function type or OverloadType would catch more cases.)

354 ↗(On Diff #224911)

I don't think this is true. Consider:

struct A {
  template<typename T> requires X<T>
  (A)() {}
};

For a trailing requires clause, we can be much more aggressive with our error detection here, though, and in that case perhaps we can unconditionally treat a trailing ( as a function call.

901 ↗(On Diff #224911)

Do not hardcode English strings into diagnostic arguments. Use a %select or a different diagnostic instead.

saar.raz updated this revision to Diff 234992.Dec 20 2019, 4:59 PM
saar.raz marked 18 inline comments as done.

Address CR comments by rsmith.
Also, add Sema normalization API (useful on its own right) and a normalization cache, for two reasons:

  • Performance (not having to re-normalize the constraints for every template or concept on every use).
  • Preventing normalization diagnostics from popping up multiple times (when the causing constraint is normalized again).
saar.raz added inline comments.Dec 20 2019, 5:01 PM
clang/lib/Parse/ParseDecl.cpp
2068–2069 ↗(On Diff #224911)

They expect the requires clause first.

clang/lib/Parse/ParseDeclCXX.cpp
3799–3810 ↗(On Diff #224911)

Is there anything that needs to be in ActOnFinishTrailingRequiresClause?

clang/lib/Parse/ParseExpr.cpp
349–350 ↗(On Diff #224911)

Function types and overload types would not reach here (would be eliminated in the previous check for 'bool' types). Only dependent types and bools get here, and checking for a function type or OverloadType would not catch those cases.

Anyway, moved this to Sema

354 ↗(On Diff #224911)

If the cases where this is possible are only very remote - could we maybe turn this into a warning instead and keep it here?

Also, if X<T> is a function type or OverloadType, then this is ill-formed anyway, right? since the constraint expression can't be a bool.

saar.raz updated this revision to Diff 235200.Dec 23 2019, 10:11 PM

Fix memory handling of NormalizedConstraint and AtomicConstraint
Fix some more failed tests and rebase onto trunk

nridge added a subscriber: nridge.Dec 26 2019, 9:17 PM
rsmith marked an inline comment as done.Jan 7 2020, 3:48 PM
rsmith added inline comments.
clang/include/clang/AST/ASTNodeTraverser.h
391–392 ↗(On Diff #235200)

It would be better to do this before we visit the inits, so that we visit the parts of a constructor declaration in lexical order. Eg:

struct X {
  X() requires true : x(0) {}
  int x;
};

should dump true before 0.

clang/include/clang/AST/DeclCXX.h
2368 ↗(On Diff #235200)

It's better to not add the default argument here. We would want any new caller of this (private) constructor to think about what value to pass in here. (Likewise for the other private constructors below.)

clang/include/clang/Basic/DiagnosticParseKinds.td
187 ↗(On Diff #235200)

Perhaps "functional cast expression" (or "explicit type conversion") would be a more familiar term than "initialization expression"?

326 ↗(On Diff #235200)

"appear" is a better word than "come" in this context.

clang/include/clang/Sema/Sema.h
6181 ↗(On Diff #235200)

emplate -> template

6189–6191 ↗(On Diff #235200)

Copy-paste error here; this is the comment for the previous map.

6196–6198 ↗(On Diff #235200)

I'm worried about this. DenseMap is not a node-based container, so if you return a pointer into the NormalizationCache here, it can be invalidated by any subsequent call to the same function. (So for example, you can't reliably get normalized constraints for two declarations at once.) Maybe NormalizationCache should store NormalizedConstraint*s instead, or maybe you should return Optional<NormalizedConstraint>s here.

6212 ↗(On Diff #235200)

Don't use \brief. (We enable the doxygen "autobrief" setting so it's unnecessary.)

clang/include/clang/Sema/SemaConcept.h
25 ↗(On Diff #235200)

The SmallVector here is probably costing us space instead of saving space; we're probably better off directly allocating the correct number of parameter mappings rather than over-allocating in the (probably very common) case of one or two parameter mappings. (Plus SmallVector has size overhead to support resizing and capacity>size, which we should never need.)

86 ↗(On Diff #235200)

All the allocation here should be allocated on the ASTContext. We're going to cache these for the lifetime of the AST anyway; there's no need to do manual heap management here. (And ASTContext allocation is cheaper than the regular heap anyway.)

clang/lib/AST/ASTImporter.cpp
3317 ↗(On Diff #235200)

Please add a FIXME to properly import the InheritedConstructor information.

clang/lib/AST/Decl.cpp
1839–1848 ↗(On Diff #235200)

There's really no point doing any of this; Deallocate is a no-op. You can just delete this block and unconditionally store to getExtInfo()->QualifierLoc.

clang/lib/Parse/ParseDecl.cpp
2045–2046 ↗(On Diff #235200)

This should be done earlier, before we parse GNU attributes. For example, GCC accepts:

template<typename T> struct X {
  void f() requires true __attribute__((noreturn)),
       g() requires true __attribute__((noreturn));
};

... and rejects if either requires-clause is the other side of the attributes. (We already get this right for the second and subsequent declarators.)

6044–6045 ↗(On Diff #235200)

Combine simplify this by grouping these two conditions together as } else if (Tok.is(tok::kw_requires) && D.hasGroupingParens()) {.

clang/lib/Parse/ParseDeclCXX.cpp
2319–2320 ↗(On Diff #235200)

Please add the (redundant) braces here to make the local style a little more consistent.

3800 ↗(On Diff #235200)

Please don't repeat the function name in the documentation comment.

3828 ↗(On Diff #235200)

Hm, should we stop at an =? We can't have an initializer after a requires-clause, but we can't have a top-level = in a constraint-expression either. I'm inclined to think that we should skip past the =, but happy either way.

3799–3810 ↗(On Diff #224911)

I would put the call to CorrectDelayedTyposInExpr there, but not a big deal either way.

clang/lib/Parse/ParseExpr.cpp
354 ↗(On Diff #224911)

Yes: I think this is correct if either (a) we've checked that the preceding expression is a function or overload set (or of non-dependent non-bool type), or (b) we're parsing a trailing-requires-clause.

321 ↗(On Diff #235200)

I'd expect you can recover from errors better by putting this check in ParseConstraintLogicalAndExpression after parsing each individual primary expression. (That is: check for this *before* forming an && or || binary operator.) That'd make it a lot easier to recover by calling ParsePostfixExpressionSuffix or similar.

330–341 ↗(On Diff #235200)

It's very unusual for the parser to attach a note to a diagnostic produced by Sema. Also, the parser ideally should not be considering the type of Culprit for itself (that's not a parsing concern), and should generally try to treat Exprs as being opaque. Please consider passing Tok.getKind() into CheckConstraintExpression instead and have it produce a suitable note for itself.

344 ↗(On Diff #235200)

I think skipping to the ) should only be done in the case where we found a (. Otherwise, there's no reason to expect there to be a ) at all, and we'll skip the entire templated declaration (in the non-trailing requires clause case). As noted above, we could recover better by calling ParsePostfixExpressionSuffix in the case where we find an element of the requires-clause that is clearly not just a primary-expression.

357 ↗(On Diff #235200)

he -> they

922 ↗(On Diff #235200)

parent -> paren

Yes, I know this is pre-existing, but this comment was confusing me when reading your patch :)

1262–1272 ↗(On Diff #235200)

These all have function call syntax, so should be treated as postfix-expressions not as primary-expressions.

1279 ↗(On Diff #235200)

Do we need to bail out here and below? Continuing and parsing the postfix-expression anyway (in the case where it can't possibly be anything else) would result in better error recovery.

1399 ↗(On Diff #235200)

This is also not a primary-expression.

1407 ↗(On Diff #235200)

This should be treated as a postfix-expression, not a primary-expression, like typeid

1620–1626 ↗(On Diff #235200)

These are all not primary-expressions.

1630 ↗(On Diff #235200)

This may or may not be a primary-expression. In principle, we need to pass the ParseKind into here so that it knows whether to call ParsePostfixExpressionSuffix. But in practice, we can disallow all of these: they will never be of type bool.

1651 ↗(On Diff #235200)

Hmm. Should an Objective-C++20 message send be considered a primary-expression or a postfix-expression? I'm inclined to say that for now we shouldn't extend the set of things that C++20 allows at the top level in a requires-clause: we should allow literals, parenthesized expressions, id-expressions, requires-expressions, and nothing else.

clang/lib/Parse/ParseTemplate.cpp
259–262 ↗(On Diff #235200)

These are in the wrong order: the requires-clause goes before GNU attributes.

clang/lib/Sema/SemaConcept.cpp
711 ↗(On Diff #235200)

Should we skip this entirely when we're in a SFINAE context (or more precisely, if our notes will just be discarded and never shown to the user)?

747–759 ↗(On Diff #235200)

Can you avoid rebuilding the CNF and DNF versions of the normalized constraints here? (Probably not a big deal if this only happens on the path to a hard error.)

clang/lib/Sema/SemaDecl.cpp
8335–8340 ↗(On Diff #235200)

We should diagnose trailing requires clauses on deduction guides. (I've started a conversation in the C++ committee regarding whether we should allow them, and so far the suggestion is "yes, but only in C++23".)

10585–10589 ↗(On Diff #235200)

No need to check this: all overridden methods must be virtual, and virtual functions can't have trailing requires clauses (or if they do, we already diagnosed that when processing them).

clang/lib/Sema/SemaOverload.cpp
6298 ↗(On Diff #235200)

No need to check the LangOpts here; if we have parsed a trailing requires clause, we must be in a suitable language mode, and we should check that requires clause.

6340–6347 ↗(On Diff #235200)

(Huh, this looks wrong: we should be checking this before we form parameter conversion sequences, shouldn't we?)

6824–6833 ↗(On Diff #235200)

Formally, I think we should be doing this before we perform object argument initialization, but ... I think it doesn't actually matter, because object argument initialization can only fail with a hard error by triggering completion of the object argument type, which must have already happened by this point. Subtle, but I think this is OK.

9555 ↗(On Diff #235200)

If they're equally-constrained, we should continue and check the later tie-breakers.

10019–10024 ↗(On Diff #235200)

Checking this for all pairs of functions could be very expensive if there are many overload candidates with constraints. Perhaps for now we should only do this if there are exactly two candidates that are tied for being the best viable candidate?

10033 ↗(On Diff #235200)

he -> they

10062 ↗(On Diff #235200)

I think generally we should not do this when displaying an arbitrary candidate set, but only when displaying a set of ambiguous candidates (that is, in OCD_AmbiguousCandidates mode) -- and ideally only when we know the ambiguity would be resolved if the constraints were ordered.

We get here (for example) when complaining that there were no viable functions, and in that case it makes no sense to talk about ambiguous constraints.

11325 ↗(On Diff #235200)

This would be a good place to call MaybeDiagnoseAmbiguousConstraints, but only in the case where OCD == OCD_AmbiguousCandidates (that is, when we're explaining an ambiguity).

11376–11384 ↗(On Diff #235200)

We should not do this here, because we might be reporting all candidates here, not only the best ones after overload resolution -- this is too low-level to determine whether these notes make sense.

11490–11500 ↗(On Diff #235200)

This is reporting no match between a declared template specialization and the known template declarations. Associated constraint mismatches between those templates are irrelevant here.

12019–12042 ↗(On Diff #235200)

I don't believe this approach is correct. In particular, consider:

Function 1 and 2 are ambiguous. Function 3 is more constrained than function 1. You end up picking function 3 regardless of whether it's more constrained than function 2 (it might not be).

You need to at least check your end result against all the functions you skipped due to ambiguity. (That should be sufficient if we can assume that "more constrained than" is transitive, which it should be.)

12037 ↗(On Diff #235200)

Please add some kind of punctuation after "constrained".

clang/lib/Sema/SemaTemplateDeduction.cpp
3440–3447 ↗(On Diff #235200)

Huh. Doing this down here does indeed seem to be correct, but that seems like a rather bad rule...

clang/lib/Serialization/ASTReaderDecl.cpp
825 ↗(On Diff #235200)

You don't need this flag; AddStmt and readStmt can cope fine with null pointers.

clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp
2 ↗(On Diff #235200)

Are the #if 0s (here and below) intentional?

rsmith added inline comments.Jan 7 2020, 7:48 PM
clang/lib/Sema/SemaTemplateDeduction.cpp
3440–3447 ↗(On Diff #235200)

This is going to get moved back up to where we had it before by CWG issue 2369 =)

saar.raz updated this revision to Diff 236914.Jan 8 2020, 2:53 PM
saar.raz marked 52 inline comments as done.

Address CR comments by rsmith.

saar.raz marked an inline comment as done.Jan 8 2020, 2:54 PM
saar.raz added inline comments.
clang/lib/Sema/SemaOverload.cpp
6340–6347 ↗(On Diff #235200)

Should I fix this in this patch?

saar.raz updated this revision to Diff 236918.Jan 8 2020, 3:11 PM

Use InclusiveOr precedence when recovering from non-primary constraint expr

rsmith accepted this revision.Jan 8 2020, 4:03 PM

Thanks, I'm happy to iterate on this further after you commit (though feel free to ask for another round of review if you prefer). The changes to recovery from missing parens in a trailing requires clause can be done in a separate change after this one if you like.

clang/include/clang/Basic/DiagnosticParseKinds.td
186–188 ↗(On Diff #236914)

This diagnostic is now unused; please remove it.

clang/include/clang/Sema/SemaConcept.h
25 ↗(On Diff #235200)

This will leak, since we're never going to actually run the destructor for AtomicConstraint. (That's not a problem for compilation, but can be a problem for use of Clang as a library.) Perhaps we could store this as an ArrayRef to an array allocated on the ASTContext allocator? Alternatively, can we compute the number of parameter mappings when we create the AtomicConstraint object, and tail-allocate the parameter mappings?

clang/lib/Parse/ParseDecl.cpp
6061–6063 ↗(On Diff #236914)

I would just drop this comment; I don't think it's all that useful, since it's just really describing how the grammar works.

clang/lib/Parse/ParseExpr.cpp
280 ↗(On Diff #236914)

Please return false here (unless E.isInvalid()): we should recovering as described by the fixit hints, so we shouldn't bail out of outer parsing steps. (I suppose you should also set NotPrimaryExpression back to false for subsequent parses, to handle things like template<typename T> requires a == b && c (X(T));, for which we want to diagnose the missing parens around a == b but not assume that the c (X(T)) is a function call.)

283 ↗(On Diff #236914)

I think we can catch and diagnose even more cases here. If the next token is a binary operator with higher precedence than && or a trailing postfix operator other than ( (that is, ., ->, ++, --, or a [ not followed by [), that's always an error and we can parse the remaining tokens as a non-primary expression to recover. (For a trailing requires clause, the only thing that can come next is a function body, and that never starts with any of those tokens -- it can start with =, but that has lower precedence than &&. For a non-trailing requires clause, none of those tokens can appear next -- ( can, and CheckConstraintExpression checks for that case.)

295–299 ↗(On Diff #236914)

It'd be nice to move the call to ParseCastExpression into CheckExpr rather than duplicating it.

363–381 ↗(On Diff #236914)

Do we still need this here? CheckConstraintExpression already sets PossibleNonPrimary to true in cases very much like this one -- could we make it handle these remaining cases too?

1647 ↗(On Diff #236914)

These are not primary-expressions.

clang/lib/Sema/SemaConcept.cpp
52–81 ↗(On Diff #236914)

I think it'd be better to not produce a diagnostic here in the case where you set PossibleNonPrimary to true, and leave it to the caller to do that instead. That way we can properly recover in the parser and parse the rest of the expression (and produce a diagnostic showing where the parens should be inserted).

clang/lib/Sema/SemaDeclCXX.cpp
3887 ↗(On Diff #236914)

We need to return the result of this back to the caller; we want to use the typo-corrected constraint expression in any further processing.

clang/lib/Sema/SemaOverload.cpp
6297–6298 ↗(On Diff #236914)

Nit: combine these into one line (if (Expr *RequiresClause = ...) to minimize the scope of RequiresClause.

6824–6825 ↗(On Diff #236914)

Nit: as above, combine these to a single line.

10051 ↗(On Diff #236914)

You can break after this }. Consider flattening / simplifying the two nested loops here into a single loop (walk the candidates and collect those ones that have associated constraints, and make sure you find exactly two).

clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
2 ↗(On Diff #236914)

Are the #if 0s here intentional?

clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp
61–62 ↗(On Diff #236914)

It's useful to include a positive test case (requires C2<T> && C2<U> maybe?) in addition to the negative test case.

This revision is now accepted and ready to land.Jan 8 2020, 4:03 PM
saar.raz updated this revision to Diff 237004.Jan 9 2020, 3:19 AM

Address final CR comments by rsmith

This revision was automatically updated to reflect the committed changes.
thakis added a subscriber: thakis.Jan 9 2020, 5:50 AM

This breaks tests on Windows: http://45.33.8.238/win/5368/step_7.txt

Please take a look and revert if it takes a while to investigate.

test/CXX/concepts-ts/dcl/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp