-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Clang] [Sema] Allow non-local/non-variable declarations in for loop #129737
Conversation
@llvm/pr-subscribers-clang Author: None (Sirraide) ChangesCurrently, we error on non-variable or non-local variable declarations in Full diff: https://github.com/llvm/llvm-project/pull/129737.diff 4 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 97b8e32f03b57..cf205e7064aa8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -237,6 +237,8 @@ Bug Fixes in This Version
when it can affect template argument deduction (#GH122306).
- Fix crash on code completion of function calls involving partial order of function templates
(#GH125500).
+- Non-local variable and non-variable declarations in the first clause of a ``for`` loop in C are no longer erroneously
+ considered an error in C23 mode and are allowed as an extension in earlier language modes.
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d89648a8a2e83..c87ef62310cb9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10794,6 +10794,23 @@ def err_non_local_variable_decl_in_for : Error<
"declaration of non-local variable in 'for' loop">;
def err_non_variable_decl_in_for : Error<
"non-variable declaration in 'for' loop">;
+
+def ext_c23_non_local_variable_decl_in_for : Extension<
+ "declaration of non-local variable in 'for' loop is a C23 extension">,
+ InGroup<C23>;
+
+def warn_c17_non_local_variable_decl_in_for : Warning<
+ "declaration of non-local variable in 'for' loop is incompatible with C standards before C23">,
+ DefaultIgnore, InGroup<CPre23Compat>;
+
+def ext_c23_non_variable_decl_in_for : Extension<
+ "non-variable declaration in 'for' loop is a C23 extension">,
+ InGroup<C23>;
+
+def warn_c17_non_variable_decl_in_for : Warning<
+ "non-variable declaration in 'for' loop is incompatible with C standards before C23">,
+ DefaultIgnore, InGroup<CPre23Compat>;
+
def err_toomany_element_decls : Error<
"only one element declaration is allowed">;
def err_selector_element_not_lvalue : Error<
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index d0b713f074c33..0a193b5299bcc 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2269,10 +2269,11 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
for (auto *DI : DS->decls()) {
if (VarDecl *VD = dyn_cast<VarDecl>(DI)) {
VarDeclSeen = true;
- if (VD->isLocalVarDecl() && !VD->hasLocalStorage()) {
- Diag(DI->getLocation(), diag::err_non_local_variable_decl_in_for);
- DI->setInvalidDecl();
- }
+ if (VD->isLocalVarDecl() && !VD->hasLocalStorage())
+ Diag(DI->getLocation(),
+ getLangOpts().C23
+ ? diag::warn_c17_non_local_variable_decl_in_for
+ : diag::ext_c23_non_local_variable_decl_in_for);
} else if (!NonVarSeen) {
// Keep track of the first non-variable declaration we saw so that
// we can diagnose if we don't see any variable declarations. This
@@ -2284,7 +2285,9 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
// Diagnose if we saw a non-variable declaration but no variable
// declarations.
if (NonVarSeen && !VarDeclSeen)
- Diag(NonVarSeen->getLocation(), diag::err_non_variable_decl_in_for);
+ Diag(NonVarSeen->getLocation(),
+ getLangOpts().C23 ? diag::warn_c17_non_variable_decl_in_for
+ : diag::ext_c23_non_variable_decl_in_for);
}
}
diff --git a/clang/test/Sema/for.c b/clang/test/Sema/for.c
index d0c2f7f21a960..c21ef62247037 100644
--- a/clang/test/Sema/for.c
+++ b/clang/test/Sema/for.c
@@ -1,13 +1,21 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify=c11 -std=c11 -pedantic %s
+// RUN: %clang_cc1 -fsyntax-only -verify=c23 -std=c23 -Wpre-c23-compat %s
// Check C99 6.8.5p3
void b1 (void) { for (void (*f) (void);;); }
-void b2 (void) { for (void f (void);;); } // expected-error {{non-variable declaration in 'for' loop}}
-void b3 (void) { for (static int f;;); } // expected-error {{declaration of non-local variable}}
-void b4 (void) { for (typedef int f;;); } // expected-error {{non-variable declaration in 'for' loop}}
+void b2 (void) { for (void f (void);;); } /* c11-warning {{non-variable declaration in 'for' loop is a C23 extension}}
+ c23-warning {{non-variable declaration in 'for' loop is incompatible with C standards before C23}} */
+void b3 (void) { for (static int f;;); } /* c11-warning {{declaration of non-local variable in 'for' loop is a C23 extension}}
+ c23-warning {{declaration of non-local variable in 'for' loop is incompatible with C standards before C23}} */
+
+void b4 (void) { for (typedef int f;;); } /* c11-warning {{non-variable declaration in 'for' loop is a C23 extension}}
+ c23-warning {{non-variable declaration in 'for' loop is incompatible with C standards before C23}} */
void b5 (void) { for (struct { int i; } s;;); }
void b6 (void) { for (enum { zero, ten = 10 } i;;); }
-void b7 (void) { for (struct s { int i; };;); } // expected-error {{non-variable declaration in 'for' loop}}
-void b8 (void) { for (static struct { int i; } s;;); } // expected-error {{declaration of non-local variable}}
+void b7 (void) { for (struct s { int i; };;); } /* c11-warning {{non-variable declaration in 'for' loop is a C23 extension}}
+ c23-warning {{non-variable declaration in 'for' loop is incompatible with C standards before C23}} */
+void b8 (void) { for (static struct { int i; } s;;); } /* c11-warning {{declaration of non-local variable in 'for' loop is a C23 extension}}
+ c23-warning {{declaration of non-local variable in 'for' loop is incompatible with C standards before C23}} */
void b9 (void) { for (struct { int i; } (*s)(struct { int j; } o) = 0;;); }
-void b10(void) { for (typedef struct { int i; } (*s)(struct { int j; });;); } // expected-error {{non-variable declaration in 'for' loop}}
+void b10(void) { for (typedef struct { int i; } (*s)(struct { int j; });;); } /* c11-warning {{non-variable declaration in 'for' loop is a C23 extension}}
+ c23-warning {{non-variable declaration in 'for' loop is incompatible with C standards before C23}} */
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this have updated: https://clang.llvm.org/c_status.html
Also it would have been nice to mention the paper number in the release notes, I am assuming there was a paper?
Diag(DI->getLocation(), diag::err_non_local_variable_decl_in_for); | ||
DI->setInvalidDecl(); | ||
} | ||
if (VD->isLocalVarDecl() && !VD->hasLocalStorage()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a big fan of quoting the section of the standard that allows various features. This allows folks in the future to know where to look to better understand the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a C99 quote further up above on line 2264; we could add a comment that mentions that this changed at some point though. Something like ‘C23 has no such restriction’ maybe?
void b2 (void) { for (void f (void);;); } // expected-error {{non-variable declaration in 'for' loop}} | ||
void b3 (void) { for (static int f;;); } // expected-error {{declaration of non-local variable}} | ||
void b4 (void) { for (typedef int f;;); } // expected-error {{non-variable declaration in 'for' loop}} | ||
void b2 (void) { for (void f (void);;); } /* c11-warning {{non-variable declaration in 'for' loop is a C23 extension}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these test should have show the non-local variables being used within the loops as well. Otherwise the tests are not fully testing the functionality and are not fully protecting against regressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, I can reference them in the loop condition I suppose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for the drive by after commit review but I really would like to see that. Having spent some time digging into regression the past few months a main pattern is lack of testing and so I am trying to push asking for more in the testing arena.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense; I’m a bit busy at the moment but I should be able to do that next week considering that it shouldn’t take too long.
Not according to Aaron: #129737 (comment) |
Oh wow, my bad, I missed that 😬 |
Currently, we error on non-variable or non-local variable declarations in
for
loops such asfor (struct S {}; 0; ) {}
. However, this is valid in C23, so this patch changes the error to a compatibilty warning and also allows this as an extension in earlier language modes. This also matches GCC’s behaviour.