[clang] C23 auto (PR #224019)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 06:50:16 PDT 2026
https://github.com/zahiraam created https://github.com/llvm/llvm-project/pull/224019
None
>From b925b2c3fa0f15b20898bd6456329eddfdb027ea Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Wed, 20 May 2026 05:48:13 -0700
Subject: [PATCH 1/2] [OpenMP] Prevent parser infinite loop on unimplemented
clauses
---
clang/lib/Basic/OpenMPKinds.cpp | 1 +
clang/lib/Parse/ParseOpenMP.cpp | 9 ++
clang/lib/Sema/SemaOpenMP.cpp | 4 +
.../OpenMP/unimplemented_clause_messages.cpp | 93 +++++++++++++++++++
4 files changed, 107 insertions(+)
create mode 100644 clang/test/OpenMP/unimplemented_clause_messages.cpp
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 287eb217ba458..675d86349c933 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -965,6 +965,7 @@ void clang::getOpenMPCaptureRegions(
case OMPD_simd:
case OMPD_single:
case OMPD_target_data:
+ case OMPD_taskgraph:
case OMPD_taskgroup:
case OMPD_stripe:
// These directives (when standalone) use OMPD_unknown as the region,
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 45a47ec797f01..ba3d3113700ff 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2388,6 +2388,8 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
ImplicitClauseAllowed = false;
Actions.OpenMP().StartOpenMPClause(CKind);
HasImplicitClause = false;
+ SourceLocation ClauseLoc = Tok.getLocation();
+
OMPClause *Clause =
ParseOpenMPClause(DKind, CKind, !SeenClauses[unsigned(CKind)]);
SeenClauses[unsigned(CKind)] = true;
@@ -2398,6 +2400,13 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
if (Tok.is(tok::comma))
ConsumeToken();
Actions.OpenMP().EndOpenMPClause();
+
+ // If ParseOpenMPClause returned without consuming any tokens, skip
+ // to end to avoid an infinite loop.
+ if (Tok.getLocation() == ClauseLoc) {
+ skipUntilPragmaOpenMPEnd(DKind);
+ break;
+ }
}
// End location of the directive.
EndLoc = Tok.getLocation();
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index d6f6bc919a31b..76b40a5039180 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6778,6 +6778,10 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
case OMPD_begin_declare_variant:
case OMPD_end_declare_variant:
llvm_unreachable("OpenMP Directive is not allowed");
+ case OMPD_taskgraph:
+ Diag(StartLoc, diag::err_omp_unexpected_directive)
+ << 1 << getOpenMPDirectiveName(OMPD_taskgraph);
+ return StmtError();
case OMPD_unknown:
default:
llvm_unreachable("Unknown OpenMP directive");
diff --git a/clang/test/OpenMP/unimplemented_clause_messages.cpp b/clang/test/OpenMP/unimplemented_clause_messages.cpp
new file mode 100644
index 0000000000000..172203ea5d040
--- /dev/null
+++ b/clang/test/OpenMP/unimplemented_clause_messages.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -verify=expected,omp60 -fopenmp -fopenmp-version=60 %s
+// RUN: %clang_cc1 -verify=expected,omp51 -fopenmp -fopenmp-version=51 %s
+// RUN: %clang_cc1 -verify=expected,omp60 -fopenmp-simd -fopenmp-version=60 %s
+// RUN: %clang_cc1 -verify=expected,omp51 -fopenmp-simd -fopenmp-version=51 %s
+
+
+void test_induction_basic() {
+ int i;
+ // omp60-warning at +4{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+ // omp60-error at +3{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+ // omp51-warning at +2{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+ // omp51-error at +1{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+#pragma omp parallel for induction(i)
+ for (i = 0; i < 10; ++i)
+ ;
+}
+
+void test_apply() {
+ // omp60-warning at +4{{extra tokens at the end of '#pragma omp tile' are ignored}}
+ // omp60-error at +3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp51-error at +2{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp51-warning at +1{{extra tokens at the end of '#pragma omp tile' are ignored}}
+#pragma omp tile sizes(10) apply(intratile: unroll)
+ for (int i = 0; i < 10; ++i)
+ ;
+}
+
+void test_empty_apply() {
+ // omp60-warning at +4{{extra tokens at the end of '#pragma omp tile' are ignored}}
+ // omp60-error at +3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp51-error at +2{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp51-warning at +1{{extra tokens at the end of '#pragma omp tile' are ignored}}
+#pragma omp tile sizes(10) apply()
+ for (int i = 0; i < 10; ++i)
+ ;
+}
+
+void test_nested_apply()
+{
+ // omp60-error at +5{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp60-warning at +4{{extra tokens at the end of '#pragma omp tile' are ignored}}
+ //omp51-error at +3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}}
+ // omp51-warning at +2{{extra tokens at the end of '#pragma omp tile' are ignored}}
+#pragma omp tile sizes(10) \
+ apply(intratile: unroll partial(2) apply(reverse))
+ for (int i = 0; i < 100; ++i)
+ ;
+}
+
+void test_induction_with_following_clause() {
+ int i;
+ // omp60-warning at +4{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+ // omp60-error at +3{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+ // omp51-error at +2{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+ // omp51-warning at +1{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+#pragma omp parallel for induction(i) num_threads(4)
+ for (i = 0; i < 10; ++i)
+ ;
+}
+
+class Point {
+ float x, y, m;
+ char color;
+
+};
+
+void processPointsInLine() {
+ float separation;
+ // omp60-error at +4{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+ // omp60-warning at +3{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+ // omp51-error at +2{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}}
+ // omp51-warning at +1{{extra tokens at the end of '#pragma omp parallel for' are ignored}}
+#pragma omp parallel for induction(step(Separation))
+ for (int i = 0; i < 10; ++i) {
+ ;
+ }
+}
+
+// Make sure test doesn't crash.
+void test_tasgraph()
+{
+ // omp60-error at +2{{unexpected OpenMP directive '#pragma omp taskgraph'}}
+ // omp51-error at +1{{unexpected OpenMP directive '#pragma omp taskgraph'}}
+#pragma omp taskgraph
+ for (int i = 0; i < 10; ++i)
+ ;
+}
+
+void test_implemented_clause() {
+#pragma omp tile sizes(10)
+ for (int i = 0; i < 10; ++i)
+ ;
+}
>From a17d08bb350951da18541f0106e35bc726e6de39 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <Zahira.Ammarguellat at intel.com>
Date: Wed, 16 Sep 2026 06:49:13 -0700
Subject: [PATCH 2/2] [Clang][C23] Fix typedef-name after 'auto' as
storage-class use
---
clang/lib/Parse/ParseDecl.cpp | 31 +++++++++++++++++++++++++++++++
clang/test/C/C23/n3007.c | 18 ++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9fa3b96527c08..b1160063161b9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4147,6 +4147,27 @@ void Parser::ParseDeclarationSpecifiers(
break;
case tok::kw_auto:
if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
+ // FIXME: In C++, `auto` as a storage-class specifier is a
+ // deprecated extension. This lookahead runs for C only; teaching it
+ // to also recognize typedef-names in C++ would require broader
+ // design discussion around `ext_auto_storage_class`. See
+ // https://github.com/llvm/llvm-project/issues/164930.
+ auto IsTypedefName = [&](const Token &T) {
+ if (!T.is(tok::identifier))
+ return false;
+ IdentifierInfo *II = T.getIdentifierInfo();
+ if (!II)
+ return false;
+ // Use a raw suppressed lookup (rather than Sema::getTypeName) to
+ // avoid emitting deprecation/availability diagnostics on the
+ // typedef during this speculative peek — the real parse will look
+ // the name up again and emit them at the right time.
+ LookupResult R(Actions, II, T.getLocation(), Sema::LookupOrdinaryName);
+ Actions.LookupName(R, getCurScope(),
+ /*AllowBuiltinCreation=*/false);
+ R.suppressDiagnostics();
+ return R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
+ };
auto MayBeTypeSpecifier = [&]() {
// In pre-C23 C, auto can be used as a storage-class specifier.
// C23 removes auto from the storage-class specifiers and repurposes
@@ -4161,6 +4182,16 @@ void Parser::ParseDeclarationSpecifiers(
if (isKnownToBeTypeSpecifier(T))
return true;
+ // C23: a bare identifier that names a typedef is a type
+ // specifier here, so `auto typedefName varName;` should be
+ // parsed with `auto` as the storage-class specifier — not as
+ // type inference. Without this check the parser would consume
+ // `auto` as type-inference and then error on the missing
+ // initializer for what it thinks is `typedefName` (issue
+ // #164930).
+ if (getLangOpts().C23 && IsTypedefName(T))
+ return true;
+
if (getLangOpts().C23 && isTypeSpecifierQualifier(T))
++I;
else
diff --git a/clang/test/C/C23/n3007.c b/clang/test/C/C23/n3007.c
index a881b89443462..143a2fcc6796e 100644
--- a/clang/test/C/C23/n3007.c
+++ b/clang/test/C/C23/n3007.c
@@ -209,3 +209,21 @@ void test_macros(int in_int) {
_Static_assert(_Generic(c, int : 1));
_Static_assert(_Generic(result, int : 1));
}
+
+// Regression test for #164930: `auto <typedef-name> <var>;` should parse as a
+// declaration of <var> with type <typedef-name> (auto used as storage-class in
+// C23 with an explicit type-name), not as inferred type deduction on the
+// typedef.
+void test_auto_typedef(void) {
+ typedef int T;
+ {
+ auto T at_local;
+ at_local = 42;
+ _Static_assert(_Generic(at_local, int : 1));
+ }
+ {
+ // Also works with qualifiers.
+ const auto T at_const = 1;
+ _Static_assert(_Generic(&at_const, const int * : 1));
+ }
+}
More information about the cfe-commits
mailing list