Implement P2360R0 in C++23 mode and as an extension in older
languages mode.
Details
- Reviewers
aaron.ballman erichkeane rsmith
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Thank you for this new feature work! Adding a few more reviewers to the mix.
Can you also add a release note for the new functionality?
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
553 | This diagnostic confuses me. An init-statement can be an expression-statement, simple-declaration, or alias-declaration. So if we're in an init-statement and see a using keyword, the only think we can be parsing is an alias declaration, right? I guess I would have expected that we'd eventually wind up calling Parser::ParseAliasDeclarationAfterDeclarator() via ParseUsingDeclaration() and that would handle any parsing-related concerns with the construct. Beyond that -- users don't really know what an init statement is, I think we'd usually call this a "condition" instead of an init statement in diagnostics. | |
555 | Same concern here about "init statements", we probably should list the constructs specifically. Also, I think we're missing the "is incompatible with standards before" variant of this extension diagnostic. | |
clang/lib/Parse/ParseExprCXX.cpp | ||
1924–1928 | Okay, to verify my understanding of the logic here: you call ParseUsingDeclaration(), but that may parse a using declaration or an alias declaration, so you need to handle the case where it parsed something that's not allowed. Assuming that's correct, I think a better approach is for ParseUsingDeclaration() to pay attention to the DeclaratorContext it is given, and when it's an init statement, refuse to parse anything other than an alias declaration. Then you can get rid of err_expected_alias_after_using_in_init_statement entirely, as the user will get the typical parsing errors. If those diagnostics are low quality and we need a custom diagnostic instead, that logic can live in ParseUsingDeclaration() instead. That just about obviates the need for ParseAliasDeclarationInInitStatement() entirely, but this function still adds value because of the extension/precompat warnings so I think it's worth keeping around. |
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
553 | I think there's a more fundamental problem with this diagnostic: it's a compiler-oriented explanation of what went wrong *for us*, not a user-oriented explanation of what's wrong *with their code*. If the user wrote if (using X::A; A a = b) or if (using namespace N; ...), telling them that they could have written an alias declaration instead is not helpful -- that has no relevance to the problem they're trying to solve. It'd be better to say "using [namespace] declaration cannot appear here" and not mention alias declarations at all. | |
555 | We could use a %select to specify the context instead of saying "init statement", but actually describing the contexts seems hard to do succinctly and clearly. In this case I think it'd be OK to just say "in this context" or similar if we don't want to mention init-statements. (As far as I can tell, literally no-one outside a C++ parser's test suite has ever put a typedef in an init-statement, so we should similarly assume this feature will never be used outside our own tests. The %select is probably not worth the effort.) |
I agree with aaron, the ParseUsingDeclaration bit should be aware of the DeclaratorContext and emit the error there, rather than try to parse it as whatever and THEN error.
The problem is the user-interface at that point, which is to spend a few cycles correcting one of the other forms, just to then tell them it isn't permitted. This is a case where erroring 'early' in this case would be a much better user experience.
clang/include/clang/Parse/Parser.h | ||
---|---|---|
2402–2403 | Slight preference for reversing these conditions | |
clang/lib/Parse/ParseExprCXX.cpp | ||
2044–2049 | single line if/else don't get '{' or '}'. |
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
553 | We do but ParseUsingDeclaration can parse other things, which are not allowed there. | |
555 | This was my justification for not doing more work. I agree that "in this context" is better | |
clang/lib/Parse/ParseExprCXX.cpp | ||
1924–1928 | Yes, I considered modifying ParseUsingDeclaration but wanted to keep the change somewhat localized, and keep ParseUsingDeclaration as is on the basis that no other place in the standard only accept alias-declaration. But the solution you suggest is certainly possible. |
- Modify ParseUsingDeclaration to only parse an alias in init statememts
- Make the c++23 extebsion warning clearer
- Nitpicks.
The error is now 'expected '=', which seems good enough in that scenario.
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
552–553 | Can you also add the "incompatible with standards before C++2b" variant of this diagnostic? | |
clang/lib/Parse/ParseDeclCXX.cpp | ||
720–722 | ||
clang/lib/Parse/ParseExprCXX.cpp | ||
1927–1930 | Though I suspect this will change somewhat when you add the precompat warning. | |
2047–2050 | ||
clang/lib/Parse/ParseTentative.cpp | ||
488–489 | No else after a return, and can drop the curly braces, per coding standard. |
clang/test/Parser/cxx2b-init-statement.cpp | ||
---|---|---|
20 | Is there any way to have this elaborate more? The 'expected 'TOKEN'' is my least favorite type of error. Could we change this to be something like, 'expected '=' in type alias' or something? |
Just nits from me, but otherwise LGTM!
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
---|---|---|
556 | ||
clang/include/clang/Parse/Parser.h | ||
1982 | ||
clang/lib/Parse/ParseExprCXX.cpp | ||
1915 | ||
clang/test/Parser/cxx2b-init-statement.cpp | ||
20 | FWIW, I'm fine with the diagnostic as-is because I don't expect anyone to actually *use* this functionality, so I don't expect a ton of users to hit this. I'd say it's fine to address when a user files a bug showing some code where the diagnostic is unclear. |
clang/test/Parser/cxx2b-init-statement.cpp | ||
---|---|---|
20 | I'm sad by this, and Aaron points out it is a bit of a hassle, so I can disagree & commit here. |
clang/test/Parser/cxx2b-init-statement.cpp | ||
---|---|---|
20 | Yeah, i guess we could but i really do not expect that feature to have a lot of uses. So i tried to keep the patch minimal. |
clang/test/Parser/cxx2b-init-statement.cpp | ||
---|---|---|
20 | Aaron and I discussed it, and I'm willing to relent, so feel free to land. In Aaron's opinion right now: Effort to improve diagnostic >(greatly) Chance of someone running into it I'm willing to wait until there is proof of the RHS of that equation being of greater magnitude before being willing to ask for the effort on the LHS. |
I've commit on your behalf in ff013b61004b01043bdbebb6416c30ecb3d3d48c, thank you for the new functionality!
This diagnostic confuses me. An init-statement can be an expression-statement, simple-declaration, or alias-declaration. So if we're in an init-statement and see a using keyword, the only think we can be parsing is an alias declaration, right?
I guess I would have expected that we'd eventually wind up calling Parser::ParseAliasDeclarationAfterDeclarator() via ParseUsingDeclaration() and that would handle any parsing-related concerns with the construct.
Beyond that -- users don't really know what an init statement is, I think we'd usually call this a "condition" instead of an init statement in diagnostics.