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 <[email protected]> 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@+4{{extra tokens at the end of '#pragma omp parallel for' are ignored}} + // omp60-error@+3{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}} + // omp51-warning@+2{{extra tokens at the end of '#pragma omp parallel for' are ignored}} + // omp51-error@+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@+4{{extra tokens at the end of '#pragma omp tile' are ignored}} + // omp60-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp51-error@+2{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp51-warning@+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@+4{{extra tokens at the end of '#pragma omp tile' are ignored}} + // omp60-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp51-error@+2{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp51-warning@+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@+5{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp60-warning@+4{{extra tokens at the end of '#pragma omp tile' are ignored}} + //omp51-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma omp tile'}} + // omp51-warning@+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@+4{{extra tokens at the end of '#pragma omp parallel for' are ignored}} + // omp60-error@+3{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}} + // omp51-error@+2{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}} + // omp51-warning@+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@+4{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}} + // omp60-warning@+3{{extra tokens at the end of '#pragma omp parallel for' are ignored}} + // omp51-error@+2{{unexpected OpenMP clause 'induction' in directive '#pragma omp parallel for'}} + // omp51-warning@+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@+2{{unexpected OpenMP directive '#pragma omp taskgraph'}} + // omp51-error@+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 <[email protected]> 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)); + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
