Page MenuHomePhabricator

Parse concept definition
Needs ReviewPublic

Authored by saar.raz on Nov 22 2017, 9:37 PM.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
faisalv added inline comments.Dec 17 2017, 8:55 AM

Pls consider just lumping this case with 'Binding/VarTemplate' (which by the way also includes a comment for why we have to add the ugly IDNS_Tag hack here (personally i think we should refactor this functionality, currently it seems split between this and SemaLookup.cpp:getIDNS and would benefit from some sort of well-designed cohesion - but that's another (low-priority) patch)


I think we should diagnose qualified-name errors here much more gracefully than we do.

i.e. template<class T> concept N::C = ...

  • perhaps try and parse a scope-specifier, and if found emit a diagnostic such as concepts must be defined within their namespace ...

Rename L as NameLoc?


Perhaps add a lookup check here? (or a fixme that we are deferring to the next patch), something along these *pseudo-code* lines:

LookupResult Previous(*this, DeclarationNameInfo(DeclarationName(Name),NameLoc), LookupOrdinaryName);
LookupName(Previous, S);
if (Previous.getRepresentativeDecl())    ... if the decl is a concept - give error regarding only one definition allowed in a TU, if a different kind of decl then declared w a different symbol type of error?

Consider refactoring these checks on constraint expressions into a separate function checkConstraintExpression (that we can also call from other contexts such as requires-clauses and nested requires expressions)?

faisalv requested changes to this revision.Dec 17 2017, 8:57 AM
faisalv added a reviewer: faisalv.
saar.raz added inline comments.Dec 17 2017, 2:01 PM

This was meant as a placeholder. The next patch (D41217) replaces this function, I don't think it is that much of a big deal what happens here in this patch.


I did that in the upcoming patches, no need to do it here as well.

faisalv requested changes to this revision.Dec 17 2017, 3:17 PM
faisalv added inline comments.

This: " I don't think it is that much of a big deal what happens here in this patch." I think is poor practice in an open source project when you're not sure when the next patch will land.

I think, when features are implemented incrementally - each patch should diagnose the yet to be implemented features - and error out as gracefully as possible. So for instance replacing the body of this function with an appropriate diagnostic (that is then removed in future patches) would be the better thing to do here.


Once again - relying on a future patch (especially without a clear FIXME) to tweak the architectural/factorization issues that you have been made aware of (and especially those, such as this, that do not require too much effort), is not practice I would generally encourage.

So changyu, if you agree that these suggestions would improve the quality of the patch and leave clang in a more robust state (maintenance-wise or execution-wise), then please make the changes. If not, please share your thoughts as to why these suggestions would either not be an improvement or be inconsistent with the theme of this patch.


This revision now requires changes to proceed.Dec 17 2017, 3:17 PM
changyu marked 6 inline comments as done.Dec 20 2017, 11:59 PM
changyu added inline comments.

The condition


fails in this case

template<concept T> concept D1 = true; // expected-error {{expected template parameter}}

ParseTemplateDeclarationOrSpecialization calls ParseTemplateParameters which eventually calls ParseNonTypeTemplateParameter. ParseNonTypeTemplateParameter prints the diag and fails but does not cause ParseTemplateParameters to fail (I'm not sure why). ParseTemplateDeclarationOrSpecialization proceeds to parse the concept definition, and we get this

/home/changyu/test.cpp:1:10: error: expected template parameter
template<concept T> concept D1 = true; // expected-error {{expected template parameter}}
clang: /home/changyu/git/llvm/tools/clang/lib/Sema/SemaTemplate.cpp:7747: clang::Decl* clang::Sema::ActOnConceptDefinition(clang::Scope*, clang::MultiTemplateParamsArg, clang::IdentifierInfo*, clang::SourceLocation, clang::Expr*): Assertion `S->isTemplateParamScope() && "Not in template param scope?"' failed.

What should we do?

faisalv added inline comments.Dec 23 2017, 9:10 AM

I think this occurs because our template parameter list is incorrectly perceived as empty - i.e. once the error occurs - to continue processing errors clang assumes this case is an explicit-specialization and replaces the template parameter scope with the outer scope. I think the thing to do here - which might also address the case where folks actually write 'template<> concept' is to actually check if template-parameter-list is empty - and emit a diagnostic about concepts can not be explicit specializations or some such ...

On a somewhat related note - i think the logic for signaling, handling and propagating failures of parsing the template parameter list might be a little broken (fixing which, might avoid triggering the assertion in template<concept> but not template<>). I might submit a patch to fix that error handling-issue separately - but either way I think we should handle the explicit-specialization error?


changyu marked 27 inline comments as done.Dec 23 2017, 8:48 PM
changyu added inline comments.

I removed it here since Saar fixed it in his patch.

changyu updated this revision to Diff 128094.Dec 23 2017, 8:48 PM
changyu marked an inline comment as done.

I think this looks good enough to commit - do you have commit privileges - or do you need one of us to commit it for you?
thank you!

I don't have commit privilege. And also there's one more problem we might want to address first.

Thank you.


There's one problem here.

​I added this if in attempt to catch the following case (but it's wrong)

	​template<> concept D1 = true;  // expected-error {{expected template parameter}}

The problem is I'm not sure how to differentiate between the above situation and the following

	​template<concept T> concept D1 = true; // expected-error {{expected template parameter}}

Both have an empty template parameter list​. The latter case has diagnostic printed by ParseNonTypeTemplateParameter while the former has not (so we try to catch it here).

What should we do?

faisalv added inline comments.Dec 24 2017, 11:23 AM

I was thinking that we would just emit a (redundant in the case of a bad template parameter) message in Sema if the template-parameters are empty that explicit specializations are not allowed here. while it would be a little misleading in the invalid template parameter case - to fix this robustly would require some fine-tuning and correcting some of the handshaking/error-propagation between the parsing of the template parameters and the code that calls it, I think. I would vote for not holding up this patch for that, unless you feel strongly you'd like to fix that behavior - then we can try and work on that first?


changyu updated this revision to Diff 128123.Dec 24 2017, 8:43 PM
changyu marked 2 inline comments as done.

I moved some template param checks from Parser::ParseTemplateDeclarationOrSpecialization to Parser::ParseConceptDefinition and Sema::ActOnConceptDefinition.


Sure, let's fix that in another patch. I added a note for this in cxx2a-concept-declaration.cpp.

saar.raz added inline comments.Mar 10 2018, 3:55 AM

Perhaps add a LangOpts.ConceptsTS check here?

saar.raz added inline comments.Mar 10 2018, 6:01 AM

We could accept 'bool' here to be nice to people coming in from the old Concepts TS version of these decls - and emit a proper warning.

I discussed this briefly w Hubert - and i'm planning on modifying this patch slightly so that it flows through ParseDeclSpecifier and handles attributes and other invalid decl-specifiers such as static etc. more gracefully on a concept decl. I have this partially implemented - my hope is to get this done v soonish so feel free to ping me if you don't hear anything about this in a week or so ...

rsmith added inline comments.Aug 6 2018, 4:18 PM

This comment isn't appropriate. Please just describe what the node is. (And note that a definition is a kind of declaration, despite common parlance.)


Please remove this again. ASTDeclReader should set the ConstraintExpr field directly. The AST is intended to be immutable after creation, so should generally not have setters.


There's a bigger problem here:

TemplateDecl *TD = /*some ConceptDecl*/;
auto SR = TD->getSourceRange(); // crash

TemplateDecl has a hard assumption that it contains a TemplatedDecl. So, three options:

  1. Change TemplateDecl and all its users to remove this assumption (hard and leads to an awkward-to-use AST)
  2. Add a Decl subclass that acts as the templated declaration in a concept declaration (corresponding to the C++ grammar's concept-definition production)
  3. Make ConceptDecl not derive from a TemplateDecl at all

I think option 2 is my preference, but option 3 is also somewhat appealing (there are other ways in which a concept is not a template: for instance, it cannot be constrained, and it cannot be classified as either a type template or a non-type template, because its kind depends on the context in which it appears).

Of course, this leads to one of the Hard Problems Of Computer Science: naming things. ConceptDecl for a concept-definition and ConceptTemplateDecl for the template-head concept-definition would be consistent with the rest of the AST. It's a little unfortunate for the longer name to be the AST node that we actually interact with, but the consistency is probably worth the cost.


Hmm, concepts don't really have specializations, though, do they? (Much like alias templates.) And because they're substituted incrementally, and the result of evaluation can vary at different points in the same translation unit, it's not obvious how much we can actually cache.

I suppose I'll see this was handled in later patches in the series :)


I would expect something more general here. For an alias-declaration, we say:

"error: name defined in alias declaration must be an identifier"

This is then also appropriate for other kinds of invalid concept-names such as

template<typename T> concept operator int = true;

"must be 'bool'" doesn't make sense. Maybe "constraint expression must be of type 'bool' but is of type %0" or similar?


Do not use "may not" in this context; it's ambiguous (this could be read as "I'm not sure if this concept has associated constraints"). Use "cannot" instead.

(And generally prefer "can" over "may" or "must" in diagnostics.)


Please make this a bit less informal and a little more informative. Perhaps "sorry, unimplemented concepts feature used". For a temporary "under construction" error, it's also OK to include a %0 and pass in a string describing the feature if you like.


maybe not -> cannot


Note this comment. You need to track down the diagnostics that use this enum and update their %selects to cover the new name kind.


This comment just repeats what the comment before it already says: please remove it again.


No need. If you see a kw_concept keyword, you have the language feature enabled.


Please also check for a < after the identifier, and diagnose the attempt to form a concept partial / explicit specialization. (I'm fine with leaving that to a later patch if you prefer.)

One option would be to call ParseUnqualifiedId and then diagnose anything that's not a simple identifier. (See ParseAliasDeclarationAfterDeclarator for how we do this for alias declarations.)


Use tok::equal here, not a string literal.


This diagnostic doesn't take an argument.


This needs rebasing onto SVN trunk. (We have lambda'fied the dependent arguments check there.)


You need to check CurContext->getRedeclContext(). A concept can be defined in a LinkageSpecDecl (with C++ linkage) or an ExportDecl, for instance.


ConceptDecl::Create cannot return null. Checking this doesn't make sense, because (a) this is dead code, and (b) if this happened, you would return null without issuing a diagnostic.

saar.raz added inline comments.Apr 9 2019, 1:32 PM

The dummy decl is pretty confusing and will be a very weird decl in and of itself. I can't think of a good name right now, but your proposed naming will be pretty confusing given that a ConceptTemplateDecl does not template a concept declaration given the meaning of the phrase in the standard... Maybe ConceptBodyDecl? ConceptDummyDecl? ConceptDefinitionDecl? We need something that screams "this is not interesting" for the AST usage to be reasonable.
Option 3 feels like loads of code duplication.
I'm not entirely sure how many blind (through TemplateDecl) uses of getTemplatedDecl there are, but there might not be that many, in which case the first option might be the lesser of all these evils.

saar.raz updated this revision to Diff 194417.Apr 9 2019, 4:24 PM

Address most of rsmith's CR comments, rebase onto trunk

Herald added a project: Restricted Project. · View Herald TranscriptApr 9 2019, 4:24 PM
Herald added a subscriber: jfb. · View Herald Transcript
saar.raz commandeered this revision.Apr 9 2019, 4:28 PM
saar.raz edited reviewers, added: changyu; removed: saar.raz.
saar.raz marked 21 inline comments as done.Apr 9 2019, 4:33 PM

Only the TemplatedDecl issue remains, all other comments have been addressed and we're rebased onto master.
@rsmith please reply to the last comment, and lets finally start merging these :)


Please revert the (presumably unintended) mode changes to the ptxas executables.


This should also derive from Mergeable<ConceptDecl>, since we are permitted to merge multiple definitions of the same concept across translation units by C++20 [basic.def.odr]/12.


Faisal's comment is marked "Done" but not done.

ConceptBodyDecl or something like it seems reasonable. But I think we can consider that after landing this patch, and leave the templated declaration null for now.


Generally we don't leave blank lines between diagnostic definitions, and instead use the continuation indent to visually separate them.


The phrasing of this (particularly the "if any") is a little confusing. I think it's fine to just use the err_concept_definition_not_identifier diagnostic for this case.


I'd probably phrase this as

"ISO C++2a does not permit the 'bool' keyword after 'concept'"

(The Concepts TS isn't really the past -- TSes are more like an alternative reality -- so "no longer" is a bit odd.) I'd also be tempted to turn this into an ExtWarn so that we can accept code targeting the Concepts TS with a warning.


Drop the ", did you attempt this?". It doesn't add anything to the diagnostic, and might be read as a little snarky, frustrating the user.

182–186 ↗(On Diff #194417)

This needs rebasing; I believe the right place for this is now split between TextNodeDumper.cpp and ASTNodeTraverser.h.


This looks wrong: we'll only print the template parameters, and not the concept <name> = <expr>; part. Please add another else if for the ConceptDecl case (or similar).


This change looks redundant. Note that VisitTemplateDecl below already returned true if getTemplatedDecl() is null.


There's no 'export'[opt] in template-head.


Consider calling DiagnoseAndSkipCXX11Attributes before and after parsing the name of the concept; it seems likely that people will try adding attributes there, and we should give a good diagnostic for this until they start being permitted in one of those two locations.


This should be PushOnScopeChains(NewDecl, S); instead (though I think in practice it won't matter until/unless we start supporting block-scope concept definitions).


No need for a return here; the code after an llvm_unreachable is unreachable =)


Call mergeMergeable(D) at the end of this.

saar.raz updated this revision to Diff 196009.Apr 21 2019, 10:12 AM

Address CR comments by rsmith

saar.raz marked 13 inline comments as done.May 2 2019, 2:33 AM
saar.raz marked an inline comment as done.May 2 2019, 2:37 AM

@rsmith are we done with CR on this?

rsmith accepted this revision.May 4 2019, 6:52 PM

LGTM with a few mechanical updates.


ExtWarn diagnostics should be named ext_ not err_. (This is important because readers of the code emitting the diagnostic need to know whether they can mark things as invalid (etc) during error recovery -- which is only correct to do after emitting an error.)


Nit: please wrap the first parameter onto the next line.


Yes, you need that :)

(You should be able to check this with -ast-dump: for a concept in a namespace, its template parameters should have the namespace as their semantic DeclContext, not the translation unit. This also has some impact on merging of default argument visibility with modules.)


Please revert the addition of the unused first parameter.

saar.raz updated this revision to Diff 198200.May 5 2019, 1:46 PM
saar.raz marked 4 inline comments as done.
  • Address final CR comments by rsmith

Awesome! I do not have commit permissions though, so can you do the actual commit?

martong resigned from this revision.May 13 2019, 2:19 AM