Per p0734r0.
Details
Diff Detail
Event Timeline
Also add:
In ASTDumper
+ void VisitConceptTemplateDecl(const ConceptTemplateDecl *D);
Implementation:
void ASTDumper::VisitConceptTemplateDecl(const ConceptTemplateDecl *D) { dumpName(D); dumpTemplateParameters(D->getTemplateParameters()); VisitExpr(D->getConstraintExpression()); }
Address the other issues I've mentioned in the inline comments and we're good! Great job :)
include/clang/AST/DeclTemplate.h | ||
---|---|---|
3023 | Add CreateDeserialized. | |
3027 | Add setConstraintExpr (for use when calling CreateDeserialized, see ASTDeclReader) | |
include/clang/Parse/Parser.h | ||
2787 | C++2a | |
lib/Parse/ParseTemplate.cpp | ||
374 | Add a check to ParseConstraintExpression that the type is either dependent or bool, and add an apropriate diagnostic. if (!ConstraintExpr->isTypeDependent() && ConstraintExpr->getType() != Context.BoolTy) { Diag(Init->getSourceRange().getBegin(), diag::err_concept_initialized_with_non_bool_type) << Init->getType(); Concept->setInvalidDecl(); return; } | |
lib/Sema/SemaTemplate.cpp | ||
2935 | Add || isa<ConceptDecl> | |
3936 | We're gonna want to check !TemplateSpecializationType::anyDependentTemplateArguments(*TemplateArgs, InstantiationDependent) here as well - so that we can instantiate a ConceptSpecializationExpr later when we have it (we're not gonna want to instantiate a ConceptSpecializationExpr with dependent arguments. | |
7698 | Check that !DC->isFileContext() (Concepts may only be defined at namespace scope) | |
7700 | I think it would be better to just check that TemplateParameterLists.size() != 1 - it can't happen in a valid situation anyway, you'd want a diagnostic there. | |
7702 | Check that c->getAssociatedConstraints() == nullptr ([temp.concept]p4 A concept shall not have associated constraints). | |
lib/Sema/SemaTemplateInstantiateDecl.cpp | ||
3099 | Yes - llvm_unreachable("Concept definitions cannot reside inside a template"); | |
lib/Serialization/ASTReaderDecl.cpp | ||
1975 | Add here: D->setConstraintExpression(Record.readExpr()); And in ASTDeclWriter, a method VisitConceptDecl: void ASTDeclWriter::VisitConceptTemplateDecl(ConceptTemplateDecl *D) { VisitTemplateDecl(D); Record.AddStmt(D->getConstraintExpression()); Code = serialization::DECL_CONCEPT; } And call it in the appropriate places. | |
3503 | Add a case: DECL_CONCEPT, also create that enum member. |
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
161 | No braces here please. | |
168 | No else needed here. | |
382 | Do we really need ThisDecl? | |
lib/Sema/SemaTemplate.cpp | ||
3936 | No braces here please. | |
3938 | clang-format please :) | |
7693 | Did you run this through clang-format? | |
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp | ||
2 | There is no -fconcepts-ts flag AFAIK. Also, why -fcxx-exceptions, it's not needed here. The -x c++ part too. It should also be -std=c++2a. |
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp | ||
---|---|---|
2 | There is a -fconcepts-ts flag (clang -Xclang -fconcepts-ts), I believe now that there is no more Concepts TS the flag should be removed as well and all concepts-related code be put under C++2a ifs. |
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp | ||
---|---|---|
2 | This was discussed with Richard, Faisal, and Aaron Ballman before. We would like to use -Xclang -fconcepts until there is enough of an implementation. When we are ready, then we switch to having it directly under C++2a. There is no reason why dealing with the option cannot be done as a separate patch, so I don't think we need to hold up this one for that work. |
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp | ||
---|---|---|
2 | OK, wasn't aware this was discussed before. |
include/clang/Sema/Sema.h | ||
---|---|---|
6194 | Indentation issue here too. | |
lib/Parse/ParseTemplate.cpp | ||
57 | C++2a Concepts | |
374 | I think that would still need a TODO to instead walk the constraint expression for atomic constraints and diagnose those. template <typename T> concept C = 1 || T::value; // error | |
lib/Sema/SemaTemplate.cpp | ||
3903 | Add more comments here. It looks like this allows us to get id-expressions naming concepts defined with non-dependent bool constraint expressions to "work" for now? | |
test/Parser/cxx-concept-declaration.cpp | ||
0 | Should add tests to prevent redeclaring concept names as either a "normal" (e.g., variable) or tag name. Should add tests to prevent multiple definitions of the same concept. Should add tests to prevent defining concepts at class scope. |
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
374 | I'm guessing you meant for this to be in class Sema so I added this to Sema::ActOnConceptDefinition. Also what is Init here? | |
374 | Why is that an error? [temp.constr.normal] in p0734r0 seems to say it's valid? | |
lib/Sema/SemaTemplate.cpp | ||
3903 | That's pretty much it. | |
3936 | I'm following the style of the surrounding code here. I can remove the braces if you insist though. | |
7693 | No, when I run the file through clang-format (with no arguments except the file), it reformats the whole file. How should I be running clang-format? |
include/clang/Sema/Sema.h | ||
---|---|---|
6194 | That last line is 79 characters long. |
include/clang/Sema/Sema.h | ||
---|---|---|
6194 | clang-format is happy to give: ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, ConceptDecl *Template, SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs); I'm no fan of blindly using clang-format, but its output is sometimes useful. | |
lib/Parse/ParseTemplate.cpp | ||
374 | From N4700 subclause 17.4.1.2 [temp.constr.atomic] paragraph 3: A search of "bool" in P0734R0 seems to indicate that is also the basis for the diagnostic Saar is requesting. Although that wording only applies clearly when determining the satisfaction of C<T> for some T, it would be good to catch it early. I believe that the particular case I presented falls under the "no valid specialization" wording in [temp.res]. I think there is a gap between the wording and the intent if overloaded binary logical operators, detectable without substitution, are not sufficiently wrong on the part of the user that a compiler may refuse to translate the program. | |
lib/Sema/SemaTemplate.cpp | ||
7693 | One workflow that works is to clang-format the base file, clang-format with your changes, grab a patch and then apply it to the original base file (probably needs some manual work). |
lib/Sema/SemaTemplate.cpp | ||
---|---|---|
7693 | I always copy the code I want to format, then pipe it into clang-format, and the paste it back with some manual adjustments to indentation. |
Do the requested clang-formatting as shown by hubert, other than that looks good to me :)
- Fixed indent issues.
- Moved TODO on boolean atomic constraints to ActOnConceptDefinition where we are currently checking for boolean constraint expression
Thanks for working on this! :)
include/clang/AST/DeclTemplate.h | ||
---|---|---|
3035 | why not just fix it now? return SourceRange(getTemplateParameters()->getTemplateLoc(), ConstraintExpr->getLocEnd(); ? | |
include/clang/AST/RecursiveASTVisitor.h | ||
1725 | Perhaps something along these lines? DEF_TRAVERSE_DECL(ConceptDecl, { TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); TRY_TO(TraverseStmt(D->getConstraintExpr())); // FIXME: Traverse all the concept specializations (one we implement forming template-ids with them). }) | |
include/clang/Sema/Sema.h | ||
6196 | Have you considered just deferring formation of concept template-ids to the next patch (especially since it might require introduction of another AST node ConceptSpecializationDecl. We could just focus on handling concept declarations/definitions in this one (and emit an unimplemented error if someone tries to form a template-id with a concept within BuildTemplateId etc.) - but perhaps consider making sure we handle name clashes/redeclarations with any other parsed names in the same namespace (within this patch)? | |
lib/AST/DeclBase.cpp | ||
773 | 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) | |
lib/CodeGen/CGDecl.cpp | ||
108 | You would need to add this to EmitTopLevelDecl too to handle global-namespace concept declarations. | |
lib/Parse/ParseTemplate.cpp | ||
368 | I think we should diagnose qualified-name errors here much more gracefully than we do. i.e. template<class T> concept N::C = ...
| |
lib/Sema/SemaTemplate.cpp | ||
3899 | I don't think we want to just return the constraint expr? I think we would need to create another conceptspecializationDecl node and nest it within a DeclRefExpr? But once again, I think we should defer this to another patch - no? | |
3923 | const bool, no? | |
7692 | Rename L as NameLoc? | |
7706 | 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? | |
7713 | 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)? | |
7735 | Why not use 'PushOnScopeChains' onto the enclosing scope? assert(S->isTemplateParamScope() && S->getParent() && !S->getParent()->isTemplateParamScope() && "..."); PushOnScopeChains(NewDecl, S->getParent(),/*AddToContext*/true); | |
test/Parser/cxx-concept-declaration.cpp | ||
0 | Name this file starting with cxx2a- please. |
lib/Sema/SemaTemplate.cpp | ||
---|---|---|
3899 | 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. | |
7713 | 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. Thanks! |
lib/Sema/SemaTemplate.cpp | ||
---|---|---|
7735 | The condition S->isTemplateParamScope() 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? |
lib/Sema/SemaTemplate.cpp | ||
---|---|---|
7735 | 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? Thoughts? |
lib/Sema/SemaTemplate.cpp | ||
---|---|---|
7713 | I removed it here since Saar fixed it in his patch. |
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.
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
181 | 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? |
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
181 | 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? Thoughts? |
I moved some template param checks from Parser::ParseTemplateDeclarationOrSpecialization to Parser::ParseConceptDefinition and Sema::ActOnConceptDefinition.
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
181 | Sure, let's fix that in another patch. I added a note for this in cxx2a-concept-declaration.cpp. |
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
161 | Perhaps add a LangOpts.ConceptsTS check here? |
lib/Parse/ParseTemplate.cpp | ||
---|---|---|
383 | 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 ...
include/clang/AST/DeclTemplate.h | ||
---|---|---|
3007 | 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.) | |
3027 | 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. | |
3035 | 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:
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. | |
include/clang/AST/RecursiveASTVisitor.h | ||
1728 | 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 :) | |
include/clang/Basic/DiagnosticParseKinds.td | ||
1134 | 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; | |
include/clang/Basic/DiagnosticSemaKinds.td | ||
2390–2391 | "must be 'bool'" doesn't make sense. Maybe "constraint expression must be of type 'bool' but is of type %0" or similar? | |
2394–2395 | 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.) | |
2396–2397 | 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. | |
2398–2399 | maybe not -> cannot | |
include/clang/Basic/TemplateKinds.h | ||
20 | Note this comment. You need to track down the diagnostics that use this enum and update their %selects to cover the new name kind. | |
lib/AST/DeclBase.cpp | ||
719–722 | This comment just repeats what the comment before it already says: please remove it again. | |
lib/Parse/ParseTemplate.cpp | ||
161 | No need. If you see a kw_concept keyword, you have the language feature enabled. | |
383–386 | 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.) | |
392 | Use tok::equal here, not a string literal. | |
399 | This diagnostic doesn't take an argument. | |
lib/Sema/SemaTemplate.cpp | ||
3922–3925 | This needs rebasing onto SVN trunk. (We have lambda'fied the dependent arguments check there.) | |
7709 | You need to check CurContext->getRedeclContext(). A concept can be defined in a LinkageSpecDecl (with C++ linkage) or an ExportDecl, for instance. | |
7733–7734 | 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. |
include/clang/AST/DeclTemplate.h | ||
---|---|---|
3035 | 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. |
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 :)
Thanks!
Please revert the (presumably unintended) mode changes to the ptxas executables.
include/clang/AST/DeclTemplate.h | ||
---|---|---|
3016 | 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. | |
3035 | 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. | |
include/clang/Basic/DiagnosticParseKinds.td | ||
1260–1270 | Generally we don't leave blank lines between diagnostic definitions, and instead use the continuation indent to visually separate them. | |
1261–1263 | 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. | |
1269 | 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. | |
include/clang/Basic/DiagnosticSemaKinds.td | ||
2443 | 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. | |
lib/AST/ASTDumper.cpp | ||
182–186 ↗ | (On Diff #194417) | This needs rebasing; I believe the right place for this is now split between TextNodeDumper.cpp and ASTNodeTraverser.h. |
lib/AST/DeclPrinter.cpp | ||
1084–1085 | 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). | |
lib/Index/IndexDecl.cpp | ||
658–662 | This change looks redundant. Note that VisitTemplateDecl below already returned true if getTemplatedDecl() is null. | |
lib/Parse/ParseTemplate.cpp | ||
58 | There's no 'export'[opt] in template-head. | |
387 | 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. | |
lib/Sema/SemaTemplate.cpp | ||
7928 | 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). | |
lib/Sema/SemaTemplateInstantiateDecl.cpp | ||
3365 | No need for a return here; the code after an llvm_unreachable is unreachable =) | |
lib/Serialization/ASTReaderDecl.cpp | ||
2082 | Call mergeMergeable(D) at the end of this. |
LGTM with a few mechanical updates.
include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
1262 | 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.) | |
include/clang/Sema/Sema.h | ||
6676 | Nit: please wrap the first parameter onto the next line. | |
lib/AST/DeclTemplate.cpp | ||
833–834 | 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.) | |
lib/Index/IndexDecl.cpp | ||
677 | Please revert the addition of the unused first parameter. |
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