[clang] [Clang][OpenMP] Only defer loop analysis in a dependent context (PR #226805)
Akash Manna via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 06:15:45 PDT 2026
https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/226805
>From 81af66c4082a4f1df63896bf6b764032fe31861c Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Sun, 27 Sep 2026 21:57:08 +0530
Subject: [PATCH 1/4] [Clang] Skip function template bodies that have no
dependent context
When Sema cannot form the declaration of a function template definition,
such as `template <class T, int N> &T::foo() {}` where `T::` names no
class, the parser still parses the body in a fake function scope that
leaves CurContext at the translation unit while the template parameters
stay in scope. `collapse(N)` is then dependent in a non-dependent
context and finishLinearClauses asserts. The same happens when a
template parameter list is rejected but an invalid, non-dependent
declaration is still built, e.g. `template <int N> void A<0>::f() {}`,
out of line or as an in-class friend definition.
Skip the body in those cases, consuming its tokens the way delayed
template parsing does, in ParseFunctionDefinition and when caching
in-class definitions. Valid function templates always have a dependent
context, so they are unaffected.
Fixes #219354
---
clang/docs/ReleaseNotes.md | 5 +++
clang/lib/Parse/ParseCXXInlineMethods.cpp | 10 +++++
clang/lib/Parse/Parser.cpp | 11 ++++++
clang/test/OpenMP/gh219354.cpp | 47 +++++++++++++++++++++++
4 files changed, 73 insertions(+)
create mode 100644 clang/test/OpenMP/gh219354.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f..1b4b0d9f3fbf5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -707,6 +707,11 @@ features cannot lower the translation-unit ABI level;
- Fixed merging of lambdas across modules in the case where neither lambda is
imported from an AST file. (#GH214560)
+- Fixed an assertion when the body of an invalid function template definition,
+ such as `template <class T, int N> void T::f() {}`, used an OpenMP loop
+ directive. Such bodies are now skipped instead of being analyzed as
+ non-template code. (#GH219354)
+
- Fixed a crash when a non-type template parameter of reference type is bound
to a subobject and is used in a context that requires an implicit conversion.
(#GH215900)
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 35dbec8dfb2f7..3ddc870b045d4 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -138,6 +138,16 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
return FnD;
}
+ // The template parameters are in scope but there is no dependent context.
+ if (auto *FD = dyn_cast_if_present<FunctionDecl>(FnD);
+ FD && TemplateInfo.Kind == ParsedTemplateKind::Template &&
+ FD->isInvalidDecl() && !FD->isDependentContext()) {
+ CachedTokens Toks;
+ LexTemplateFunctionForLateParsing(Toks);
+ Actions.ActOnSkippedFunctionBody(FnD);
+ return FnD;
+ }
+
// In delayed template parsing mode, if we are within a class template
// or if we are about to parse function member template then consume
// the tokens and store them for parsing at the end of the translation unit.
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index c86ed6b2ea3f9..fa0496e19d18e 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1405,6 +1405,17 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
}
+ // The template parameters are in scope but there is no dependent context.
+ if (TemplateInfo.Kind == ParsedTemplateKind::Template &&
+ (!Res ||
+ (Res->isInvalidDecl() && !Res->getAsFunction()->isDependentContext()))) {
+ CachedTokens Toks;
+ LexTemplateFunctionForLateParsing(Toks);
+ BodyScope.Exit();
+ Actions.ActOnSkippedFunctionBody(Res);
+ return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
+ }
+
return ParseFunctionBody(Res, BodyScope);
}
diff --git a/clang/test/OpenMP/gh219354.cpp b/clang/test/OpenMP/gh219354.cpp
new file mode 100644
index 0000000000000..78850d6196456
--- /dev/null
+++ b/clang/test/OpenMP/gh219354.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fopenmp -fsyntax-only -verify %s
+
+template <class T, int N> &T::foo() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
+#pragma omp simd collapse(N)
+ ;
+}
+
+struct S {
+ template <int N> void bar();
+};
+
+namespace NS {
+template <int N> void S::bar() { // expected-error {{cannot define or redeclare 'bar' here because namespace 'NS' does not enclose namespace 'S'}}
+#pragma omp simd collapse(N)
+ ;
+}
+}
+
+template <int M> class D;
+template <int M> template <int N> void D<M>::baz() { // expected-error {{out-of-line definition of 'baz' from class 'D<M>' without definition}}
+#pragma omp simd collapse(N)
+ ;
+}
+
+template <int> struct E {
+ void spec();
+};
+template <int N> void E<0>::spec() { // expected-error {{template parameter list matching the non-templated nested type 'E<0>' should be empty ('template<>')}}
+#pragma omp simd collapse(N)
+ ;
+}
+
+template <int> struct G {
+ void fr();
+};
+struct H {
+ template <int N> friend void G<0>::fr() { // expected-error {{template parameter list matching the non-templated nested type 'G<0>' should be empty ('template<>')}}
+#pragma omp simd collapse(N)
+ ;
+ }
+};
+
+#pragma omp declare simd // expected-error {{function declaration is expected after 'declare simd' directive}}
+template <class T, int N> void T::qux() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
+#pragma omp simd collapse(N)
+ ;
+}
>From f595a22eabcf31c032a42a87b9a8684bf74f1e3c Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Tue, 29 Sep 2026 01:29:17 +0530
Subject: [PATCH 2/4] [Clang][OpenMP] Handle dependent loop nests outside
dependent contexts
Revert the parser change. checkOpenMPLoop leaves the loop helper
expressions unbuilt when the collapse or ordered count or one of the
loops is dependent, but it then decides whether to build them, and
finishLinearClauses and the loop directives assert that they were built,
by checking CurContext->isDependentContext(). Those disagree whenever
template parameters are in scope outside a dependent context, e.g. in a
block inside a variable template or in the body of an invalid function
template definition.
Record the dependence of the loop nest on the DSA stack when it is found
and check that instead.
Fixes #219354
---
clang/docs/ReleaseNotes.md | 9 ++--
clang/lib/Parse/ParseCXXInlineMethods.cpp | 10 ----
clang/lib/Parse/Parser.cpp | 11 ----
clang/lib/Sema/SemaOpenMP.cpp | 56 +++++++++++++--------
clang/test/OpenMP/gh219354.cpp | 61 ++++++++++-------------
5 files changed, 65 insertions(+), 82 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 1b4b0d9f3fbf5..8b836a4dd5a35 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -707,11 +707,6 @@ features cannot lower the translation-unit ABI level;
- Fixed merging of lambdas across modules in the case where neither lambda is
imported from an AST file. (#GH214560)
-- Fixed an assertion when the body of an invalid function template definition,
- such as `template <class T, int N> void T::f() {}`, used an OpenMP loop
- directive. Such bodies are now skipped instead of being analyzed as
- non-template code. (#GH219354)
-
- Fixed a crash when a non-type template parameter of reference type is bound
to a subobject and is used in a context that requires an implicit conversion.
(#GH215900)
@@ -1016,6 +1011,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
`thread_limit` and `num_threads` clauses for OpenMP 6.1 or later.
- Map-type-modifying modifiers applied to a list item with a user-defined mapper
are now propagated onto the maps the mapper expands to.
+- Fixed a crash or a bogus error on an OpenMP loop directive whose loop nest
+ depends on a template parameter outside of a dependent context, e.g. in a
+ block within a variable template or in the body of an invalid function
+ template definition. (#GH219354)
- Mapping of expressions with base-pointers through a user-defined mapper (e.g.
`map(s.p[0:n])`) now conforms to OpenMP's conditional pointer-attachment,
matching the behavior of such maps outside a mapper.
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 3ddc870b045d4..35dbec8dfb2f7 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -138,16 +138,6 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
return FnD;
}
- // The template parameters are in scope but there is no dependent context.
- if (auto *FD = dyn_cast_if_present<FunctionDecl>(FnD);
- FD && TemplateInfo.Kind == ParsedTemplateKind::Template &&
- FD->isInvalidDecl() && !FD->isDependentContext()) {
- CachedTokens Toks;
- LexTemplateFunctionForLateParsing(Toks);
- Actions.ActOnSkippedFunctionBody(FnD);
- return FnD;
- }
-
// In delayed template parsing mode, if we are within a class template
// or if we are about to parse function member template then consume
// the tokens and store them for parsing at the end of the translation unit.
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index fa0496e19d18e..c86ed6b2ea3f9 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1405,17 +1405,6 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
}
- // The template parameters are in scope but there is no dependent context.
- if (TemplateInfo.Kind == ParsedTemplateKind::Template &&
- (!Res ||
- (Res->isInvalidDecl() && !Res->getAsFunction()->isDependentContext()))) {
- CachedTokens Toks;
- LexTemplateFunctionForLateParsing(Toks);
- BodyScope.Exit();
- Actions.ActOnSkippedFunctionBody(Res);
- return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
- }
-
return ParseFunctionBody(Res, BodyScope);
}
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 41cfc1dcd3649..e9890388450d0 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -205,6 +205,7 @@ class DSAStackTy {
bool RegionHasOrderConcurrent = false;
unsigned AssociatedLoops = 1;
bool HasMutipleLoops = false;
+ bool DependentLoopNest = false;
const Decl *PossiblyLoopCounter = nullptr;
bool NowaitRegion = false;
bool UntiedRegion = false;
@@ -1008,6 +1009,13 @@ class DSAStackTy {
const SharingMapTy *Top = getTopOfStackOrNull();
return Top ? Top->HasMutipleLoops : false;
}
+ /// The loop nest can be dependent even if the context is not.
+ void setDependentLoopNest() { getTopOfStack().DependentLoopNest = true; }
+ bool isDependentLoopNest() const {
+ const SharingMapTy *Top = getTopOfStackOrNull();
+ return SemaRef.CurContext->isDependentContext() ||
+ (Top && Top->DependentLoopNest);
+ }
/// Marks current target region as one with closely nested teams
/// region.
@@ -3136,10 +3144,10 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
static bool finishLinearClauses(Sema &SemaRef, ArrayRef<OMPClause *> Clauses,
OMPLoopBasedDirective::HelperExprs &B,
DSAStackTy *Stack) {
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((Stack->isDependentLoopNest() || B.builtAll()) &&
"loop exprs were not built");
- if (SemaRef.CurContext->isDependentContext())
+ if (Stack->isDependentLoopNest())
return false;
// Finalize the clauses that need pre-built expressions for CodeGen.
@@ -10106,8 +10114,10 @@ static bool checkOpenMPIterationSpace(
}
assert(((For && For->getBody()) || (CXXFor && CXXFor->getBody())) &&
"No loop body.");
- // Postpone analysis in dependent contexts for ranged for loops.
- if (CXXFor && SemaRef.CurContext->isDependentContext())
+ // Postpone analysis of dependent ranged for loops.
+ if (CXXFor && !CXXFor->getBeginStmt())
+ DSA.setDependentLoopNest();
+ if (CXXFor && DSA.isDependentLoopNest())
return false;
OpenMPIterationSpaceChecker ISC(SemaRef, SupportsNonRectangular, DSA,
@@ -10165,7 +10175,9 @@ static bool checkOpenMPIterationSpace(
HasErrors |= ISC.checkAndSetInc(For ? For->getInc() : CXXFor->getInc());
}
- if (ISC.dependent() || SemaRef.CurContext->isDependentContext() || HasErrors)
+ if (ISC.dependent())
+ DSA.setDependentLoopNest();
+ if (DSA.isDependentLoopNest() || HasErrors)
return HasErrors;
// Build the loop's iteration space representation.
@@ -10613,6 +10625,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
ForVarDeclFinder FVDF{CollapsedLoopVarDecls};
FVDF.TraverseStmt(AStmt);
} else {
+ DSA.setDependentLoopNest();
Built.clear(/*Size=*/1);
return 1;
}
@@ -10635,6 +10648,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
}
OrderedLoopCount = Result.getLimitedValue();
} else {
+ DSA.setDependentLoopNest();
Built.clear(/*Size=*/1);
return 1;
}
@@ -10696,7 +10710,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
Built.clear(/*size=*/NestedLoopCount);
- if (SemaRef.CurContext->isDependentContext())
+ if (DSA.isDependentLoopNest())
return NestedLoopCount;
// An example of what is generated for the following code:
@@ -11618,7 +11632,7 @@ StmtResult SemaOpenMP::ActOnOpenMPGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPGenericLoopDirective::Create(getASTContext(), StartLoc, EndLoc,
@@ -11750,7 +11764,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -11794,7 +11808,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetTeamsGenericLoopDirective::Create(
@@ -11827,7 +11841,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPParallelGenericLoopDirective::Create(
@@ -11869,7 +11883,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetParallelGenericLoopDirective::Create(
@@ -14447,7 +14461,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14526,7 +14540,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14564,7 +14578,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14686,7 +14700,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14725,7 +14739,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14842,7 +14856,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
SemaRef.setFunctionHasBranchProtectedScope();
@@ -14870,7 +14884,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
return OMPDistributeParallelForDirective::Create(
@@ -15038,7 +15052,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp teams distribute loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15149,7 +15163,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp for loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15210,7 +15224,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
"omp target teams distribute loop exprs were not built");
return OMPTargetTeamsDistributeDirective::Create(
diff --git a/clang/test/OpenMP/gh219354.cpp b/clang/test/OpenMP/gh219354.cpp
index 78850d6196456..a55681d9cd918 100644
--- a/clang/test/OpenMP/gh219354.cpp
+++ b/clang/test/OpenMP/gh219354.cpp
@@ -1,47 +1,38 @@
-// RUN: %clang_cc1 -fopenmp -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fopenmp -fblocks -fsyntax-only -verify %s
template <class T, int N> &T::foo() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
#pragma omp simd collapse(N)
;
}
-struct S {
- template <int N> void bar();
-};
-
-namespace NS {
-template <int N> void S::bar() { // expected-error {{cannot define or redeclare 'bar' here because namespace 'NS' does not enclose namespace 'S'}}
-#pragma omp simd collapse(N)
- ;
-}
-}
-
-template <int M> class D;
-template <int M> template <int N> void D<M>::baz() { // expected-error {{out-of-line definition of 'baz' from class 'D<M>' without definition}}
-#pragma omp simd collapse(N)
- ;
-}
-
-template <int> struct E {
- void spec();
-};
-template <int N> void E<0>::spec() { // expected-error {{template parameter list matching the non-templated nested type 'E<0>' should be empty ('template<>')}}
-#pragma omp simd collapse(N)
- ;
+template <class T, int N> void T::bar() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
+#pragma omp simd
+ for (int i = 0; i < N; ++i)
+ ;
+#pragma omp simd
+ for (auto x : T())
+ ;
+#pragma omp taskloop collapse(N)
+ for (int j = 0; j < 10; ++j)
+ for (int k = 0; k < 10; ++k)
+ ;
+#pragma omp for ordered(N)
+ for (int l = 0; l < 10; ++l)
+ ;
}
-template <int> struct G {
- void fr();
-};
-struct H {
- template <int N> friend void G<0>::fr() { // expected-error {{template parameter list matching the non-templated nested type 'G<0>' should be empty ('template<>')}}
+template <int N> void (^Block)() = ^{
+ int a[N];
+#pragma omp simd
+ for (int x : a)
+ ;
#pragma omp simd collapse(N)
+ for (int i = 0; i < 10; ++i)
+ for (int j = 0; j < 10; ++j)
+ ;
+#pragma omp parallel for
+ for (int i = 0; i < N; ++i)
;
- }
};
-#pragma omp declare simd // expected-error {{function declaration is expected after 'declare simd' directive}}
-template <class T, int N> void T::qux() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
-#pragma omp simd collapse(N)
- ;
-}
+void use() { Block<2>(); }
>From 8d730e9eeec17f35ac822ae0d5994896130e2981 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Tue, 29 Sep 2026 08:23:58 +0530
Subject: [PATCH 3/4] [Clang][OpenMP] Only defer loop analysis in a dependent
context
Drop the DependentLoopNest flag again. Outside of a dependent context a
loop count that cannot be evaluated, or a dependent loop, can only come
from error recovery (here: the body of a template whose declaration
failed). Deferring the analysis there left the loop helper expressions
unbuilt in a non-dependent context, so give up on the directive instead,
like checkOpenMPLoop already does for loop counts that contain errors.
Fixes #219354
---
clang/docs/ReleaseNotes.md | 7 ++--
clang/lib/Sema/SemaOpenMP.cpp | 64 +++++++++++++++-------------------
clang/test/OpenMP/gh219354.cpp | 22 ++++--------
3 files changed, 38 insertions(+), 55 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 8b836a4dd5a35..ca9f4ab330637 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1011,10 +1011,9 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
`thread_limit` and `num_threads` clauses for OpenMP 6.1 or later.
- Map-type-modifying modifiers applied to a list item with a user-defined mapper
are now propagated onto the maps the mapper expands to.
-- Fixed a crash or a bogus error on an OpenMP loop directive whose loop nest
- depends on a template parameter outside of a dependent context, e.g. in a
- block within a variable template or in the body of an invalid function
- template definition. (#GH219354)
+- Fixed a crash on an OpenMP loop directive in the body of an invalid function
+ template definition when its `collapse` or `ordered` count or a loop bound
+ depends on a template parameter. (#GH219354)
- Mapping of expressions with base-pointers through a user-defined mapper (e.g.
`map(s.p[0:n])`) now conforms to OpenMP's conditional pointer-attachment,
matching the behavior of such maps outside a mapper.
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index e9890388450d0..71b5ef6f7b423 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -205,7 +205,6 @@ class DSAStackTy {
bool RegionHasOrderConcurrent = false;
unsigned AssociatedLoops = 1;
bool HasMutipleLoops = false;
- bool DependentLoopNest = false;
const Decl *PossiblyLoopCounter = nullptr;
bool NowaitRegion = false;
bool UntiedRegion = false;
@@ -1009,13 +1008,6 @@ class DSAStackTy {
const SharingMapTy *Top = getTopOfStackOrNull();
return Top ? Top->HasMutipleLoops : false;
}
- /// The loop nest can be dependent even if the context is not.
- void setDependentLoopNest() { getTopOfStack().DependentLoopNest = true; }
- bool isDependentLoopNest() const {
- const SharingMapTy *Top = getTopOfStackOrNull();
- return SemaRef.CurContext->isDependentContext() ||
- (Top && Top->DependentLoopNest);
- }
/// Marks current target region as one with closely nested teams
/// region.
@@ -3144,10 +3136,10 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
static bool finishLinearClauses(Sema &SemaRef, ArrayRef<OMPClause *> Clauses,
OMPLoopBasedDirective::HelperExprs &B,
DSAStackTy *Stack) {
- assert((Stack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"loop exprs were not built");
- if (Stack->isDependentLoopNest())
+ if (SemaRef.CurContext->isDependentContext())
return false;
// Finalize the clauses that need pre-built expressions for CodeGen.
@@ -10114,10 +10106,8 @@ static bool checkOpenMPIterationSpace(
}
assert(((For && For->getBody()) || (CXXFor && CXXFor->getBody())) &&
"No loop body.");
- // Postpone analysis of dependent ranged for loops.
- if (CXXFor && !CXXFor->getBeginStmt())
- DSA.setDependentLoopNest();
- if (CXXFor && DSA.isDependentLoopNest())
+ // Postpone analysis in dependent contexts for ranged for loops.
+ if (CXXFor && SemaRef.CurContext->isDependentContext())
return false;
OpenMPIterationSpaceChecker ISC(SemaRef, SupportsNonRectangular, DSA,
@@ -10175,10 +10165,11 @@ static bool checkOpenMPIterationSpace(
HasErrors |= ISC.checkAndSetInc(For ? For->getInc() : CXXFor->getInc());
}
- if (ISC.dependent())
- DSA.setDependentLoopNest();
- if (DSA.isDependentLoopNest() || HasErrors)
+ if (SemaRef.CurContext->isDependentContext() || HasErrors)
return HasErrors;
+ // Outside of a dependent context this only happens during error recovery.
+ if (ISC.dependent())
+ return true;
// Build the loop's iteration space representation.
//
@@ -10625,7 +10616,9 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
ForVarDeclFinder FVDF{CollapsedLoopVarDecls};
FVDF.TraverseStmt(AStmt);
} else {
- DSA.setDependentLoopNest();
+ // Outside of a dependent context this only happens during error recovery.
+ if (!SemaRef.CurContext->isDependentContext())
+ return 0;
Built.clear(/*Size=*/1);
return 1;
}
@@ -10648,7 +10641,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
}
OrderedLoopCount = Result.getLimitedValue();
} else {
- DSA.setDependentLoopNest();
+ if (!SemaRef.CurContext->isDependentContext())
+ return 0;
Built.clear(/*Size=*/1);
return 1;
}
@@ -10710,7 +10704,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
Built.clear(/*size=*/NestedLoopCount);
- if (DSA.isDependentLoopNest())
+ if (SemaRef.CurContext->isDependentContext())
return NestedLoopCount;
// An example of what is generated for the following code:
@@ -11632,7 +11626,7 @@ StmtResult SemaOpenMP::ActOnOpenMPGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPGenericLoopDirective::Create(getASTContext(), StartLoc, EndLoc,
@@ -11764,7 +11758,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -11808,7 +11802,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetTeamsGenericLoopDirective::Create(
@@ -11841,7 +11835,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPParallelGenericLoopDirective::Create(
@@ -11883,7 +11877,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetParallelGenericLoopDirective::Create(
@@ -14461,7 +14455,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14540,7 +14534,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14578,7 +14572,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14700,7 +14694,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14739,7 +14733,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14856,7 +14850,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
SemaRef.setFunctionHasBranchProtectedScope();
@@ -14884,7 +14878,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
return OMPDistributeParallelForDirective::Create(
@@ -15052,7 +15046,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp teams distribute loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15163,7 +15157,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp for loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15224,7 +15218,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((DSAStack->isDependentLoopNest() || B.builtAll()) &&
+ assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
"omp target teams distribute loop exprs were not built");
return OMPTargetTeamsDistributeDirective::Create(
diff --git a/clang/test/OpenMP/gh219354.cpp b/clang/test/OpenMP/gh219354.cpp
index a55681d9cd918..ebf6498c4c2d6 100644
--- a/clang/test/OpenMP/gh219354.cpp
+++ b/clang/test/OpenMP/gh219354.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fopenmp -fblocks -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fopenmp -fsyntax-only -verify %s
template <class T, int N> &T::foo() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
#pragma omp simd collapse(N)
@@ -9,9 +9,6 @@ template <class T, int N> void T::bar() { // expected-error {{nested name specif
#pragma omp simd
for (int i = 0; i < N; ++i)
;
-#pragma omp simd
- for (auto x : T())
- ;
#pragma omp taskloop collapse(N)
for (int j = 0; j < 10; ++j)
for (int k = 0; k < 10; ++k)
@@ -21,18 +18,11 @@ template <class T, int N> void T::bar() { // expected-error {{nested name specif
;
}
-template <int N> void (^Block)() = ^{
- int a[N];
-#pragma omp simd
- for (int x : a)
- ;
+template <int> struct E {
+ void f();
+};
+template <int N> void E<0>::f() { // expected-error {{template parameter list matching the non-templated nested type 'E<0>' should be empty ('template<>')}}
#pragma omp simd collapse(N)
for (int i = 0; i < 10; ++i)
- for (int j = 0; j < 10; ++j)
- ;
-#pragma omp parallel for
- for (int i = 0; i < N; ++i)
;
-};
-
-void use() { Block<2>(); }
+}
>From ec5bc39fc708ed0c0d602db0b5c995c9c881c23b Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Thu, 1 Oct 2026 18:44:19 +0530
Subject: [PATCH 4/4] [Clang][OpenMP] Track deferred loop analysis instead of
checking the context
---
clang/docs/ReleaseNotes.md | 7 ++-
clang/lib/Sema/SemaOpenMP.cpp | 62 +++++++++++--------
clang/test/OpenMP/gh219354.cpp | 42 ++++++++++++-
.../simd_lambda_default_arg_codegen.cpp | 17 +++++
4 files changed, 97 insertions(+), 31 deletions(-)
create mode 100644 clang/test/OpenMP/simd_lambda_default_arg_codegen.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ca9f4ab330637..64b82a8ac96b3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1011,9 +1011,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
`thread_limit` and `num_threads` clauses for OpenMP 6.1 or later.
- Map-type-modifying modifiers applied to a list item with a user-defined mapper
are now propagated onto the maps the mapper expands to.
-- Fixed a crash on an OpenMP loop directive in the body of an invalid function
- template definition when its `collapse` or `ordered` count or a loop bound
- depends on a template parameter. (#GH219354)
+- Fixed a crash on an OpenMP loop directive whose `collapse` or `ordered` count
+ or a loop bound depends on a template parameter outside of a dependent
+ context, e.g. in the body of an invalid function template definition.
+ (#GH219354)
- Mapping of expressions with base-pointers through a user-defined mapper (e.g.
`map(s.p[0:n])`) now conforms to OpenMP's conditional pointer-attachment,
matching the behavior of such maps outside a mapper.
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 71b5ef6f7b423..61e41def8d6e0 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -205,6 +205,7 @@ class DSAStackTy {
bool RegionHasOrderConcurrent = false;
unsigned AssociatedLoops = 1;
bool HasMutipleLoops = false;
+ bool LoopAnalysisDeferred = false;
const Decl *PossiblyLoopCounter = nullptr;
bool NowaitRegion = false;
bool UntiedRegion = false;
@@ -1008,6 +1009,15 @@ class DSAStackTy {
const SharingMapTy *Top = getTopOfStackOrNull();
return Top ? Top->HasMutipleLoops : false;
}
+ /// The loop helper expressions are built on instantiation instead.
+ void setLoopAnalysisDeferred() {
+ getTopOfStack().LoopAnalysisDeferred = true;
+ }
+ bool isLoopAnalysisDeferred() const {
+ const SharingMapTy *Top = getTopOfStackOrNull();
+ return SemaRef.CurContext->isDependentContext() ||
+ (Top && Top->LoopAnalysisDeferred);
+ }
/// Marks current target region as one with closely nested teams
/// region.
@@ -3136,10 +3146,10 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
static bool finishLinearClauses(Sema &SemaRef, ArrayRef<OMPClause *> Clauses,
OMPLoopBasedDirective::HelperExprs &B,
DSAStackTy *Stack) {
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((Stack->isLoopAnalysisDeferred() || B.builtAll()) &&
"loop exprs were not built");
- if (SemaRef.CurContext->isDependentContext())
+ if (Stack->isLoopAnalysisDeferred())
return false;
// Finalize the clauses that need pre-built expressions for CodeGen.
@@ -10165,11 +10175,10 @@ static bool checkOpenMPIterationSpace(
HasErrors |= ISC.checkAndSetInc(For ? For->getInc() : CXXFor->getInc());
}
- if (SemaRef.CurContext->isDependentContext() || HasErrors)
- return HasErrors;
- // Outside of a dependent context this only happens during error recovery.
if (ISC.dependent())
- return true;
+ DSA.setLoopAnalysisDeferred();
+ if (DSA.isLoopAnalysisDeferred() || HasErrors)
+ return HasErrors;
// Build the loop's iteration space representation.
//
@@ -10616,9 +10625,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
ForVarDeclFinder FVDF{CollapsedLoopVarDecls};
FVDF.TraverseStmt(AStmt);
} else {
- // Outside of a dependent context this only happens during error recovery.
- if (!SemaRef.CurContext->isDependentContext())
- return 0;
+ if (CollapseLoopCountExpr->isInstantiationDependent())
+ DSA.setLoopAnalysisDeferred();
Built.clear(/*Size=*/1);
return 1;
}
@@ -10641,8 +10649,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
}
OrderedLoopCount = Result.getLimitedValue();
} else {
- if (!SemaRef.CurContext->isDependentContext())
- return 0;
+ if (OrderedLoopCountExpr->isInstantiationDependent())
+ DSA.setLoopAnalysisDeferred();
Built.clear(/*Size=*/1);
return 1;
}
@@ -10704,7 +10712,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
Built.clear(/*size=*/NestedLoopCount);
- if (SemaRef.CurContext->isDependentContext())
+ if (DSA.isLoopAnalysisDeferred())
return NestedLoopCount;
// An example of what is generated for the following code:
@@ -11626,7 +11634,7 @@ StmtResult SemaOpenMP::ActOnOpenMPGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPGenericLoopDirective::Create(getASTContext(), StartLoc, EndLoc,
@@ -11758,7 +11766,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -11802,7 +11810,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetTeamsGenericLoopDirective::Create(
@@ -11835,7 +11843,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPParallelGenericLoopDirective::Create(
@@ -11877,7 +11885,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelGenericLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp loop exprs were not built");
return OMPTargetParallelGenericLoopDirective::Create(
@@ -14455,7 +14463,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14534,7 +14542,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14572,7 +14580,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14694,7 +14702,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMasterTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14733,7 +14741,7 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMaskedTaskLoopDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
// OpenMP, [2.9.2 taskloop Construct, Restrictions]
@@ -14850,7 +14858,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
SemaRef.setFunctionHasBranchProtectedScope();
@@ -14878,7 +14886,7 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
return OMPDistributeParallelForDirective::Create(
@@ -15046,7 +15054,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp teams distribute loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15157,7 +15165,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp for loop exprs were not built");
DSAStack->setParentTeamsRegionLoc(StartLoc);
@@ -15218,7 +15226,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
if (NestedLoopCount == 0)
return StmtError();
- assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+ assert((DSAStack->isLoopAnalysisDeferred() || B.builtAll()) &&
"omp target teams distribute loop exprs were not built");
return OMPTargetTeamsDistributeDirective::Create(
diff --git a/clang/test/OpenMP/gh219354.cpp b/clang/test/OpenMP/gh219354.cpp
index ebf6498c4c2d6..4dd5df43737be 100644
--- a/clang/test/OpenMP/gh219354.cpp
+++ b/clang/test/OpenMP/gh219354.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fopenmp -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fopenmp -fblocks -fsyntax-only -verify %s
template <class T, int N> &T::foo() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
#pragma omp simd collapse(N)
@@ -6,6 +6,7 @@ template <class T, int N> &T::foo() { // expected-error {{nested name specifier
}
template <class T, int N> void T::bar() { // expected-error {{nested name specifier 'T' for declaration does not refer into a class, class template or class template partial specialization}}
+ int lin = 0;
#pragma omp simd
for (int i = 0; i < N; ++i)
;
@@ -16,6 +17,9 @@ template <class T, int N> void T::bar() { // expected-error {{nested name specif
#pragma omp for ordered(N)
for (int l = 0; l < 10; ++l)
;
+#pragma omp simd collapse(N) linear(lin)
+ for (int m = 0; m < 10; ++m)
+ ;
}
template <int> struct E {
@@ -26,3 +30,39 @@ template <int N> void E<0>::f() { // expected-error {{template parameter list ma
for (int i = 0; i < 10; ++i)
;
}
+
+int lambda(auto x, int (*f)() = [] {
+ int k = 0;
+#pragma omp simd collapse(sizeof(x) / sizeof(x))
+ for (int i = 0; i < 10; ++i)
+ k++;
+#pragma omp simd
+ for (int i = 0; i < (int)sizeof(x); ++i)
+ k++;
+ return k;
+}) {
+ return f();
+}
+int useLambda() { return lambda(1); }
+
+int lambdaErr(auto x, int (*f)() = [] { // expected-note {{while substituting into a lambda expression here}}
+ int k = 0;
+#pragma omp simd collapse(sizeof(x) / sizeof(x) + 1) // expected-note {{as specified in 'collapse' clause}}
+ for (int i = 0; i < 10; ++i)
+ k++; // expected-error {{expected 2 for loops after '#pragma omp simd', but found only 1}}
+ return k;
+}) {
+ return f();
+}
+int useLambdaErr() { return lambdaErr(1); } // expected-note {{in instantiation of default function argument expression for 'lambdaErr<int>' required here}}
+
+template <int N> void (^Block)() = ^{
+#pragma omp simd collapse(N)
+ for (int i = 0; i < 10; ++i)
+ for (int j = 0; j < 10; ++j)
+ ;
+#pragma omp parallel for
+ for (int i = 0; i < N; ++i)
+ ;
+};
+void useBlock() { Block<2>(); }
diff --git a/clang/test/OpenMP/simd_lambda_default_arg_codegen.cpp b/clang/test/OpenMP/simd_lambda_default_arg_codegen.cpp
new file mode 100644
index 0000000000000..4538d65424054
--- /dev/null
+++ b/clang/test/OpenMP/simd_lambda_default_arg_codegen.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fopenmp -emit-llvm -o - %s | FileCheck %s
+
+int foo(auto x, int (*f)() = [] {
+ int k = 0;
+#pragma omp simd collapse(sizeof(x) / sizeof(x))
+ for (int i = 0; i < 10; ++i)
+ k++;
+ return k;
+}) {
+ return f();
+}
+int bar() { return foo(1); }
+
+// CHECK-LABEL: define internal noundef i32 @"{{.+}}clEv"(
+// CHECK: store i32 %{{.+}}, ptr %{{.+}}, align 4, !llvm.access.group
+// CHECK: br label %{{.+}}, !llvm.loop
+// CHECK: !{!"llvm.loop.vectorize.enable"}
More information about the cfe-commits
mailing list