[clang] [Clang] Skip function template bodies that have no dependent context (PR #226805)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 27 10:13:02 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

Fixes #<!-- -->219354

For `template <class T, int N> &T::foo() { ... }`, `T::` doesn't name a class, so Sema diagnoses the declarator and never creates a declaration. The parser still parses the body, but in a fake function scope that leaves `CurContext` at the translation unit while `T` and `N` are still visible. `collapse(N)` is dependent, so OpenMP holds off on building the loop expressions, and `finishLinearClauses` then asserts because the context isn't dependent. The same happens when Sema rejects a template parameter list but still builds an invalid, non-dependent declaration, like `template <int N> void A<0>::f() {}`, out of line or as an in-class friend. OpenMP is just the first thing to trip over it; this goes back to at least clang 9.

Now the body is skipped whenever template parameters are in scope but there's no dependent context to parse it in, i.e. no declaration at all or only an invalid, non-dependent one. The tokens are consumed the same way delayed template parsing already does, in `ParseFunctionDefinition` and where in-class definitions are cached. A valid function template always has a dependent context, so correct code isn't affected, and the declaration error is still reported.


---
Full diff: https://github.com/llvm/llvm-project/pull/226805.diff


4 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+5) 
- (modified) clang/lib/Parse/ParseCXXInlineMethods.cpp (+10) 
- (modified) clang/lib/Parse/Parser.cpp (+11) 
- (added) clang/test/OpenMP/gh219354.cpp (+47) 


``````````diff
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)
+  ;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/226805


More information about the cfe-commits mailing list