[Clang][C2y] Add support for if declarations (N3356 paper)#198244
Conversation
|
@llvm/pr-subscribers-clang Author: Muhammad Bassiouni (bassiounix) ChangesAdd support for if declarations in No API change to the names except a signature change to I noticed that the usage of The only new changes to the code is narrowing the code path to It should be noted that the first clause in the standard paper can only be declaration. If I understand correctly this means we can't allow expression statement in the first clause of the condition, and that's the goal of the new warnings. Sources:
Resourses:
TBR: There might still be C++-specific code paths that I didn't guard against in the code, so please address them if you notice one. I'm still not familiar with Clang codebase :) Full diff: https://github.com/llvm/llvm-project/pull/198244.diff 7 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..9c4527764226b 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -220,6 +220,8 @@ def ext_c2y_case_range : Extension<
"case ranges are a C2y extension">, InGroup<C2y>;
def err_c2y_labeled_break_continue : Error<
"named %select{'break'|'continue'}0 is only supported in C2y">;
+def err_c2y_first_condition_clause_is_not_declaration : Error<
+ "first clause in condition must be a declaration">;
// Generic errors.
def err_expected_expression : Error<"expected expression">;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index dc3dc8a4ae0e9..5182e2849f8d7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5039,12 +5039,10 @@ class Parser : public CodeCompletionHandler {
/// appropriate moment for a 'for' loop.
///
/// \returns The parsed condition.
- Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
- SourceLocation Loc,
- Sema::ConditionKind CK,
- bool MissingOK,
- ForRangeInfo *FRI = nullptr,
- bool EnterForConditionScope = false);
+ Sema::ConditionResult ParseCXXCondition(
+ StmtResult *InitStmt, SourceLocation Loc, Sema::ConditionKind CK,
+ bool MissingOK, ForRangeInfo *FRI = nullptr,
+ bool EnterForConditionScope = false, bool isSecondCallForIfCond = false);
DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
ParsedAttributes &Attrs);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 75ad821c245a5..0635508cd0713 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2162,10 +2162,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
ParsedAttributes LocalAttrs(AttrFactory);
LocalAttrs.takeAllPrependingFrom(Attrs);
ParsingDeclarator D(*this, DS, LocalAttrs, Context);
- if (TemplateInfo.TemplateParams)
+ if (!getLangOpts().C2y && TemplateInfo.TemplateParams)
D.setTemplateParameterLists(*TemplateInfo.TemplateParams);
bool IsTemplateSpecOrInst =
+ !getLangOpts().C2y &&
(TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation ||
TemplateInfo.Kind == ParsedTemplateKind::ExplicitSpecialization);
SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
@@ -2185,7 +2186,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
while (MaybeParseHLSLAnnotations(D))
;
- if (Tok.is(tok::kw_requires))
+ if (!getLangOpts().C2y && Tok.is(tok::kw_requires))
ParseTrailingRequiresClauseWithScope(D);
// Save late-parsed attributes for now; they need to be parsed in the
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 39c61f4b5bf5c..85782255a1b97 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1868,7 +1868,8 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
Sema::ConditionResult
Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
Sema::ConditionKind CK, bool MissingOK,
- ForRangeInfo *FRI, bool EnterForConditionScope) {
+ ForRangeInfo *FRI, bool EnterForConditionScope,
+ bool isSecondCallForIfCond) {
// Helper to ensure we always enter a continue/break scope if requested.
struct ForConditionScopeRAII {
Scope *S;
@@ -1883,6 +1884,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
S->setIsConditionVarScope(false);
}
} ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
+ bool parsingIfOrSwitchCondition = !FRI && !EnterForConditionScope;
ParenBraceBracketBalancer BalancerRAIIObj(*this);
PreferredType.enterCondition(Actions, Tok.getLocation());
@@ -1898,10 +1900,11 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
MaybeParseCXX11Attributes(attrs);
const auto WarnOnInit = [this, &CK] {
- Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
- ? diag::warn_cxx14_compat_init_statement
- : diag::ext_init_statement)
- << (CK == Sema::ConditionKind::Switch);
+ if (!getLangOpts().C2y)
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_init_statement
+ : diag::ext_init_statement)
+ << (CK == Sema::ConditionKind::Switch);
};
// Determine what kind of thing we have.
@@ -1924,7 +1927,9 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
}
ConsumeToken();
*InitStmt = Actions.ActOnNullStmt(SemiLoc);
- return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+ EnterForConditionScope,
+ parsingIfOrSwitchCondition);
}
EnterExpressionEvaluationContext Eval(
@@ -1939,10 +1944,22 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
return Sema::ConditionError();
if (InitStmt && Tok.is(tok::semi)) {
+ if (getLangOpts().C2y && parsingIfOrSwitchCondition &&
+ !isSecondCallForIfCond)
+ // C2y only permits declaration in the first clause of an if condition,
+ // so it makes sense to error out in other condition. We can stop
+ // parsing here and just report an error but we chose to continue to
+ // generate an error about the second clause of the condition since
+ // there's a ';' found.
+ Diag(Tok.getLocation(),
+ diag::err_c2y_first_condition_clause_is_not_declaration);
+
WarnOnInit();
*InitStmt = Actions.ActOnExprStmt(Expr.get());
ConsumeToken();
- return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+ EnterForConditionScope,
+ parsingIfOrSwitchCondition);
}
return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -1953,7 +1970,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
WarnOnInit();
DeclGroupPtrTy DG;
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
- if (Tok.is(tok::kw_using))
+ if (!getLangOpts().C2y && Tok.is(tok::kw_using))
DG = ParseAliasDeclarationInInitStatement(
DeclaratorContext::SelectionInit, attrs);
else {
@@ -1962,7 +1979,9 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
attrs, DeclSpecAttrs, /*RequireSemi=*/true);
}
*InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
- return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+ EnterForConditionScope,
+ parsingIfOrSwitchCondition);
}
case ConditionOrInitStatement::ForRangeDecl: {
@@ -1978,7 +1997,12 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
return Sema::ConditionResult();
}
- case ConditionOrInitStatement::ConditionDecl:
+ case ConditionOrInitStatement::ConditionDecl: {
+ if (getLangOpts().C2y && isSecondCallForIfCond) {
+ Diag(Tok.getLocation(), diag::err_expected_expression);
+ return Sema::ConditionError();
+ }
+ } break;
case ConditionOrInitStatement::Error:
break;
}
@@ -2007,7 +2031,8 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
}
// If attributes are present, parse them.
- MaybeParseGNUAttributes(DeclaratorInfo);
+ if (!getLangOpts().C2y)
+ MaybeParseGNUAttributes(DeclaratorInfo);
// Type-check the declaration itself.
DeclResult Dcl = Actions.ActOnCXXConditionDeclaration(getCurScope(),
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1a45ed66950be..301898fb3955f 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1264,7 +1264,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
T.consumeOpen();
SourceLocation Start = Tok.getLocation();
- if (getLangOpts().CPlusPlus) {
+ if (getLangOpts().CPlusPlus || getLangOpts().C2y) {
Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
} else {
ExprResult CondExpr = ParseExpression();
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index f77b1001332fe..8c4b328fe538f 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -453,7 +453,7 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
CanBeForRangeDecl);
- if (CanBeInitStatement && Tok.is(tok::kw_using))
+ if (!getLangOpts().C2y && CanBeInitStatement && Tok.is(tok::kw_using))
return ConditionOrInitStatement::InitStmtDecl;
if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
return State.result();
@@ -1065,7 +1065,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
// Check for need to substitute AltiVec __vector keyword
// for "vector" identifier.
- if (TryAltiVecVectorToken())
+ if (!getLangOpts().C2y && TryAltiVecVectorToken())
return TPResult::True;
const Token &Next = NextToken();
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
new file mode 100644
index 0000000000000..df3dbc7561ff2
--- /dev/null
+++ b/clang/test/C/C2y/n3267.c
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=c2y -verify %s
+
+bool test_if() {
+ if (true) {}
+ if (bool x = true; x) {}
+ if (bool x = false) return x;
+ if ([[maybe_unused]] bool x = true) {}
+ if (bool x [[maybe_unused]] = true) {}
+ if ([[maybe_unused]] int x = 3; x > 0) {}
+ return false;
+}
+
+int test_switch() {
+ int y = 1;
+ switch (y) {}
+
+ switch (int x = 1; x) {
+ default:
+ y += x;
+ }
+
+ switch (int x [[maybe_unused]] = 1) {}
+ switch ([[maybe_unused]] int x = 1) {}
+
+ switch (int x = 1) {
+ default:
+ return y + x;
+ }
+}
+
+bool negative_test_if() {
+ if (true; true) {} /* expected-error {{first clause in condition must be a declaration}}
+ expected-warning {{expression result unused}}*/
+ if (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
+ expected-error {{expected expression}}
+ expected-warning {{expression result unused}} */
+ if (bool x = true; bool y = x) return y; /* expected-error {{expected expression}}
+ expected-error {{use of undeclared identifier 'y'}} */
+ if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
+ expected-error {{expected expression}}
+ expected-error {{use of undeclared identifier 'y'}}
+ expected-warning {{expression result unused}}*/
+ return false;
+}
+
+int negative_test_switch() {
+ switch (true; 1) { /* expected-error {{first clause in condition must be a declaration}}
+ expected-warning {{expression result unused}} */
+ default:
+ break;
+ }
+ switch (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
+ expected-error {{expected expression}}
+ expected-warning {{expression result unused}} */
+ switch (int x = 1; int y = x) { // expected-error {{expected expression}}
+ default:
+ return y; // expected-error {{use of undeclared identifier 'y'}}
+ }
+}
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
Sirraide
left a comment
There was a problem hiding this comment.
Ok, this is looking much better now; seems like doing the checks after calling ParseCXXCondition() was the right approach
| ParsedAttributes attrs(AttrFactory); | ||
| MaybeParseCXX11Attributes(attrs); | ||
| bool ParsedAttrs = MaybeParseCXX11Attributes(attrs); | ||
| if (!getLangOpts().CPlusPlus) |
There was a problem hiding this comment.
Why is this only necessary for C?
There was a problem hiding this comment.
I did that because I don't know whether we should support GNU extensions in this case for C++ or not.
The original implementation did not, so I assumed that it's only needed for C2y only.
If that's not the case please let me know and I'll change that.
There was a problem hiding this comment.
I would expect that we'd accept GNU and square bracket attributes but I'm a bit confused because GNU-style attributes are working in C++ already without this: https://godbolt.org/z/ob636jsh7 (also, note that GCC supports these attributes in C mode too, which is another reason we should).
There was a problem hiding this comment.
Ok after investigating the behavior of the current code, we do want to parse this in C mode only because in C++ this code will raise an error and a warning in case we used [[]] or any attributes only in the init stmt.
if ([[]]; true) {}
if (__attribute__((assume(1 > 0))); true) {}
if (__attribute__(()); true) {}But we do handle [[]]; in C mode since we parse CXX11 attributes and check them down with the existence of ;
// Handle 'if (; true)' and 'if ([[...]]; true)'.
if (Tok.is(tok::semi)) {Yes GNU attributes are handled for ConditionOrInitStatement::InitStmtDecl, but since it has C++ specific code it raises error/warnings on empty declarations, which is not desired in C mode.
We can go ahead and suppress these reports but it will cause a parsing behavior that's completely wrong rejecting valid code like __attribute__((...)) __attribute__((...)) int x;. If we wanted to handle that we should have a state somewhere to know what to accept and what to reject, which will alter functions' signature and becomes a messy edit.
The best way to go around it in my opinion is to catch that case before even entering ConditionOrInitStatement::InitStmtDecl and handle it in if (Tok.is(tok::semi)) { like we already do with [[]] by MaybeParseCXX11Attributes(attrs);.
And since this is only a valid case in C mode, we don't want to do that in C++ mode and let the compiler report empty init stmt like desired.
There was a problem hiding this comment.
Yes GNU attributes are handled for ConditionOrInitStatement::InitStmtDecl, but since it has C++ specific code it raises error/warnings on empty declarations, which is not desired in C mode.
You have example of these warnings?
There was a problem hiding this comment.
Yes GNU attributes are handled for ConditionOrInitStatement::InitStmtDecl, but since it has C++ specific code it raises error/warnings on empty declarations, which is not desired in C mode.
You have an example?
There was a problem hiding this comment.
There was a problem hiding this comment.
Why is this only necessary for C?
TL;DR because of the special case in C2y, C++ handles it properly. (__attribute__((...)); expr) specifically.
Part of the reason why it's C2y only is this #198244 (comment)
There was a problem hiding this comment.
I think there's a GCC bug in the C implementation for [[]] because [[]]; is a declaration (an attribute-declaration, specifically). And that makes me wonder if rejecting __attribute__ in that same position is also a bug: https://godbolt.org/z/G1Ks5f5Yr
Another oddity that makes me think this could be a bug is that it is accepted in C++ mode, while [[]]; is not: https://godbolt.org/z/nrx8nYxxd
But then again, GCC is consistent about expecting an actual declaration as opposed to "it was easier for the standards committee to specify the grammar if this is a declaration" things: https://godbolt.org/z/E1a1xjrc1 so perhaps this is intentional?
CC @pinskia
There was a problem hiding this comment.
I think there's a GCC bug in the C implementation for
[[]]because[[]];is a declaration (an attribute-declaration, specifically). And that makes me wonder if rejecting__attribute__in that same position is also a bug: https://godbolt.org/z/G1Ks5f5YrAnother oddity that makes me think this could be a bug is that it is accepted in C++ mode, while
[[]];is not: https://godbolt.org/z/nrx8nYxxdBut then again, GCC is consistent about expecting an actual declaration as opposed to "it was easier for the standards committee to specify the grammar if this is a declaration" things: https://godbolt.org/z/E1a1xjrc1 so perhaps this is intentional?
CC @pinskia
How does that have any relevance to this PR or us? Are you going at that we should do the same as GCC and not the standard?
| // The Declarator's DeclarationAttrs only accepts [[]] and keyword attributes; | ||
| // move any GNU attributes onto the DeclSpec instead. | ||
| if (!getLangOpts().CPlusPlus) |
There was a problem hiding this comment.
This seems pretty suspicious to me, can you explain why it's needed a bit more? CC @erichkeane
There was a problem hiding this comment.
The declarator in the case of a simple declaration refuses to take GNU attributes or act on them, IIRC it causes a crash (I can't remember precisely), so I figured if the values are transformed from the GNU form to the CXX11 form then it can be handled fine.
I don't know exactly why this is the case but this is a workaround.
There was a problem hiding this comment.
Hrm, it seems like we may be working around a deeper bug then, I think more investigation is needed here.
There was a problem hiding this comment.
The reason for the crash is this assert and the intended use of Declarator
/// `DS` and `DeclarationAttrs` must outlive the `Declarator`. In particular,
/// take care not to pass temporary objects for these parameters.
///
/// `DeclarationAttrs` contains [[]] attributes from the
/// attribute-specifier-seq at the beginning of a declaration, which appertain
/// to the declared entity itself. Attributes with other syntax (e.g. GNU)
/// should not be placed in this attribute list; if they occur at the
/// beginning of a declaration, they apply to the `DeclSpec` and should be
/// attached to that instead.
///
/// Here is an example of an attribute associated with a declaration:
///
/// [[deprecated]] int x, y;
///
/// This attribute appertains to all of the entities declared in the
/// declaration, i.e. `x` and `y` in this case.
Declarator(const DeclSpec &DS, const ParsedAttributesView &DeclarationAttrs,
DeclaratorContext C)
: ... {
assert(llvm::all_of(DeclarationAttrs,
[](const ParsedAttr &AL) {
return (AL.isStandardAttributeSyntax() ||
AL.isRegularKeywordAttribute());
}) &&
"DeclarationAttrs may only contain [[]] and keyword attributes");
}Specifically
/// ... Attributes with other syntax (e.g. GNU)
/// should not be placed in this attribute list; if they occur at the
/// beginning of a declaration, they apply to theDeclSpecand should be
/// attached to that instead.
So by doing this
if (!getLangOpts().CPlusPlus)
for (ParsedAttr &AL : attrs)
if (AL.isGNUAttribute())
DS.getAttributes().takeOneFrom(attrs, &AL);I'm effectively applying the GNU attributes to the DeclSpec and attaching to that instead.
I don't think altering the predicate of all_of is the solution here, but what I did.
There was a problem hiding this comment.
This seems reasonable to me. Though, I question what C++ does here? Why is this not shared code with C++?
There was a problem hiding this comment.
This seems reasonable to me. Though, I question what C++ does here? Why is this not shared code with C++?
C before this proposal didn't have a notion of declarators in conditions, so this class was intended to be used with C++ only originally (I guess).
There was a problem hiding this comment.
OK so C++ does handle all the cases properly: https://godbolt.org/z/KGfnjdsdf
The reason for not sharing it with C++ is we parse the GNU attribute as part of a declspec to handle the special case for (__attribute__((...)); expr) in C2y
// Handle '(; expr)', '(__attribute__((...)); expr)' and '([[...]]; expr)'.
if (Tok.is(tok::semi)) {
StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
if (ParsedAttrs) {
WarnOnInit();
*InitStmt = Actions.ActOnAttributedStmt(attrs, Null.get());
} else
Diag(Null.get()->getBeginLoc(),
diag::err_c2y_first_condition_clause_is_not_declaration);
return ParseCondition(nullptr, Loc, CK, MissingOK);
}This is not valid C++ case, it's C2y specific case.
So it makes sense to convert it from declspec attribute to become an attribute for the declarator itself
Refer back to this in clang/lib/Parse/ParseDecl.cpp Parser::ParseDeclGroup
// Accept attributes in an init-declarator. In the first declarator in a
// declaration, these would be part of the declspec. In subsequent
// declarators, they become part of the declarator itself, so that they
// don't apply to declarators after *this* one. Examples:
// short __attribute__((common)) var; -> declspec
// short var __attribute__((common)); -> declarator
// short x, __attribute__((common)) var; -> declarator
MaybeParseGNUAttributes(D);So I'm effectively taking it from this form short __attribute__((common)) var; as declspec to this form short var __attribute__((common)); to be part of the declarator itself when it's a ConditionDecl.
I don't believe that there's any bugs here, let alone a deeper one. It's working as intended and handles the special case for C2y case.
There was a problem hiding this comment.
I still don't see that as a reason to have a different parse in C vs C++.
This is valid c++ https://godbolt.org/z/WM4P6vYTc (per https://eel.is/c++draft/stmt.pre#nt:init-statement)
And we can (and do, afaict) support gnu attributes in there (ie that's outside of the scope of the standards)
if([[]] int x; true) is the only thing we should not support in C++ (and I'm not convinced that's not an omission in the wording frankly)
There was a problem hiding this comment.
This is valid c++ https://godbolt.org/z/WM4P6vYTc
Yes and it's not a valid C per the paper wording, even GCC doesn't support it: https://godbolt.org/z/YTqvPjh36
I have to specialize here to C only to reject this code.
I still don't see that as a reason to have a different parse in C vs C++.
There has to be a differentiation between C and C++ because they don't parse the same rules, and the reason I did parse the GNU attribute at the beginning is to handle the case of __attribute__((...)); expr. And because I did that at the beginning, it got parsed as a declaration specifier, so when it comes the time to parse (__attribute__((...)) int x = 1) the declarator cannot accept a declaration specifier attribute (it's a single declarator, we can't have (int x = 1, y = 2)), so it gets converted to a declarator attribute.
C++ already handles all types of cases where you would put the attribute in different places, it doesn't need this code. https://godbolt.org/z/KGfnjdsdf
The reason this code is special and C only is the case of attribute declaration or when you have __attribute__((...)); expr and I don't want to interfere with already working C++ path. I only modify/specialize for C paths.
if([[]] int x; true) is the only thing we should not support in C++ (and I'm not convinced that's not an omission in the wording frankly)
Well it is valid in C and C++ per wording of the paper, so I don't think there's a concern here related to this patch.
There was a problem hiding this comment.
In any case, I don't want to make you all feel that I'm pushing towards the wrong direction. I could be wrong with my reasoning here.
If that's the case, please just let me know and tell me to enable it for other modes not just C and I'll happily do that :)
| def warn_c2y_compat_generic_with_type_arg : Warning< | ||
| "passing a type argument as the first operand to '_Generic' is incompatible " | ||
| "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore; | ||
| def warn_c2y_compat_decl_statement : Warning< |
There was a problem hiding this comment.
Drive-by comment, nothing to be done for this PR: we have CXXCompat for defining these in a pair, but we don't have CCompat; we probably should generalize that at some point.
| ParsedAttributes attrs(AttrFactory); | ||
| MaybeParseCXX11Attributes(attrs); | ||
| bool ParsedAttrs = MaybeParseCXX11Attributes(attrs); | ||
| if (!getLangOpts().CPlusPlus) |
There was a problem hiding this comment.
I think there's a GCC bug in the C implementation for [[]] because [[]]; is a declaration (an attribute-declaration, specifically). And that makes me wonder if rejecting __attribute__ in that same position is also a bug: https://godbolt.org/z/G1Ks5f5Yr
Another oddity that makes me think this could be a bug is that it is accepted in C++ mode, while [[]]; is not: https://godbolt.org/z/nrx8nYxxd
But then again, GCC is consistent about expecting an actual declaration as opposed to "it was easier for the standards committee to specify the grammar if this is a declaration" things: https://godbolt.org/z/E1a1xjrc1 so perhaps this is intentional?
CC @pinskia
… GCC to finalize it
|
I am wondering if we can do something to incrementally move forward. That is, is there some conservative subset of this pull request that can land and have the more focused discussions under smaller, incremental additions? That would allow @bassiounix to declare some level of progress and success as part of his work. |
We talked about this PR at my office hours yesterday. The plan is to do another round of reviews, we'll likely add FIXME comments to various places for things where there are open questions about handling However, I think @bassiounix can declare progress and success on the work already today -- he's been actively engaged with the community on addressing feedback and each time we get a bit closer to it being ready to land. |
AaronBallman
left a comment
There was a problem hiding this comment.
I expect there will be some follow-up work and we may discover some surprises once users throw real world code at us, but I think this is now ready to go. LGTM with a couple of minor nits addressed.
| def warn_c2y_compat_generic_with_type_arg : Warning< | ||
| "passing a type argument as the first operand to '_Generic' is incompatible " | ||
| "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore; | ||
| def warn_c2y_compat_decl_statement : Warning< |
There was a problem hiding this comment.
Follow-up work to switch to this whenever it lands: #209241
Co-authored-by: Aaron Ballman <aaron@aaronballman.com>
|
I'm merging this later today if there isn't any more comments. |
993ddea to
0ea1906
Compare
Add support for _if declarations_ in `C2y` mode by exploiting existing C++ code. The only new changes to the code is narrowing the code path to `C` mode and introduce new errors and warnings for some pitfalls with the syntax and what is expected from the standard and old language modes. It should be noted that the first clause in the standard paper can only be declaration. This means we can't allow expression statement in the first clause of the condition, and that's the goal of the new warnings/errors. Also this is different from C++ grammar.
Add support for if declarations in
C2ymode by exploiting existing C++ code.No API change to the names.
The only new changes to the code is narrowing the code path to
Cmode and introduce new errors and warnings for some pitfalls with the syntax and what is expected from the standard and old language modes.It should be noted that the first clause in the standard paper can only be declaration. This means we can't allow expression statement in the first clause of the condition, and that's the goal of the new warnings/errors.
Sources:
Resourses:
C2yhttps://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf