[clang] [Clang][OpenMP] Fix crash on failed type substitution in declare reduction/mapper (PR #230059)
Akash Manna via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 03:55:48 PDT 2026
https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/230059
Fixes #223366
`VisitOMPDeclareReductionDecl` passed the substituted reduction type to `ActOnOpenMPDeclareReductionType` without checking it, so a failed substitution (null type) hit `assert(ParsedType.isUsable())`. `-fms-compatibility` is not needed: `typename T::type` with `T = int` crashes too. `declare mapper` had the same bug.
Both visitors now return early when the substituted type is null. The error is already reported by `SubstType`.
I used AI tools for assistance!
>From b642d5eacdb063e45c956d7c2975eb412884e7b8 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Tue, 6 Oct 2026 11:03:36 +0530
Subject: [PATCH] [Clang][OpenMP] Fix crash on failed type substitution in
declare reduction/mapper
Fixes #223366
When a template containing `#pragma omp declare reduction` is
instantiated, `VisitOMPDeclareReductionDecl` substitutes the reduction
type and hands the result to `ActOnOpenMPDeclareReductionType`. It never
checked whether the substitution worked. If it failed, `SubstType` had
already reported the error and returned a null type, which then tripped
`assert(ParsedType.isUsable())`. `-fms-compatibility` is incidental in
the report: it only makes the undeclared `baz` a dependent name that is
looked up at instantiation time, and plain `typename T::type` with
`T = int` crashes the same way. `VisitOMPDeclareMapperDecl` had the same
code, and there a build without assertions dereferenced the null type.
Both visitors now check the substituted type and return before calling
into `SemaOpenMP` when it is null. The error has already been emitted,
so no new diagnostic is added, and the declaration is dropped the same
way as when the instantiated type is rejected, for example for being a
reference type.
---
clang/docs/ReleaseNotes.md | 2 ++
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 16 +++++++-----
clang/test/OpenMP/declare_mapper_messages.cpp | 20 +++++++++++++++
.../OpenMP/declare_reduction_messages.cpp | 16 ++++++++++++
.../OpenMP/declare_reduction_ms_compat.cpp | 25 +++++++++++++++++++
5 files changed, 73 insertions(+), 6 deletions(-)
create mode 100644 clang/test/OpenMP/declare_reduction_ms_compat.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7ec126a065ae5..a7683d7532475 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -588,6 +588,8 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when an OpenMP `copyprivate` clause names a non-static data member that is not private in the
enclosing context, which is now diagnosed, a data member inside a member function template, or a structured
binding. (#GH217893)
+- Fixed a crash when template argument substitution fails in the type of an OpenMP `declare reduction` or
+ `declare mapper` directive. (#GH223366)
- Fixed an assertion failure when a variable implicitly mapped by an OpenMP `target` directive has a class type
(such as `std::map`) whose mapper lookup instantiates a class template specialization. (#GH154704)
- Fixed a bug where repeated #imports of modular headers in non-modular compilation were translated to #pragma clang module import. (#GH216924)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5e72f05c863a0..eaa82623eb6b6 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4569,10 +4569,12 @@ Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl(
D->getType()->containsUnexpandedParameterPack();
QualType SubstReductionType;
if (RequiresInstantiation) {
+ QualType T = SemaRef.SubstType(D->getType(), TemplateArgs, D->getLocation(),
+ DeclarationName());
+ if (T.isNull())
+ return nullptr;
SubstReductionType = SemaRef.OpenMP().ActOnOpenMPDeclareReductionType(
- D->getLocation(),
- ParsedType::make(SemaRef.SubstType(
- D->getType(), TemplateArgs, D->getLocation(), DeclarationName())));
+ D->getLocation(), ParsedType::make(T));
} else {
SubstReductionType = D->getType();
}
@@ -4661,10 +4663,12 @@ TemplateDeclInstantiator::VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D) {
QualType SubstMapperTy;
DeclarationName VN = D->getVarName();
if (RequiresInstantiation) {
+ QualType T =
+ SemaRef.SubstType(D->getType(), TemplateArgs, D->getLocation(), VN);
+ if (T.isNull())
+ return nullptr;
SubstMapperTy = SemaRef.OpenMP().ActOnOpenMPDeclareMapperType(
- D->getLocation(),
- ParsedType::make(SemaRef.SubstType(D->getType(), TemplateArgs,
- D->getLocation(), VN)));
+ D->getLocation(), ParsedType::make(T));
} else {
SubstMapperTy = D->getType();
}
diff --git a/clang/test/OpenMP/declare_mapper_messages.cpp b/clang/test/OpenMP/declare_mapper_messages.cpp
index f2101786f6ce0..cf9c339bc44ae 100644
--- a/clang/test/OpenMP/declare_mapper_messages.cpp
+++ b/clang/test/OpenMP/declare_mapper_messages.cpp
@@ -121,3 +121,23 @@ int fun(int arg) {
}
return arg;
}
+
+#if __cplusplus >= 201103L
+namespace GH223366 {
+struct HasType {
+ typedef int type;
+};
+
+template <typename T> using Vec = vec;
+
+template <typename T>
+void f() {
+#pragma omp declare mapper(id : Vec<typename T::type> v) map(v.len) // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
+}
+
+void g() {
+ f<HasType>();
+ f<int>(); // expected-note {{in instantiation of function template specialization 'GH223366::f<int>' requested here}}
+}
+}
+#endif
diff --git a/clang/test/OpenMP/declare_reduction_messages.cpp b/clang/test/OpenMP/declare_reduction_messages.cpp
index f91d952dfa14f..3d49a5d57cdda 100644
--- a/clang/test/OpenMP/declare_reduction_messages.cpp
+++ b/clang/test/OpenMP/declare_reduction_messages.cpp
@@ -167,3 +167,19 @@ struct S
#pragma omp declare reduction (bar : S<1> : omp_out.foo(omp_in))
#endif
+
+namespace GH223366 {
+struct HasType {
+ typedef int type;
+};
+
+template <typename T>
+void f() {
+#pragma omp declare reduction(red : typename T::type : omp_out += omp_in) // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
+}
+
+void g() {
+ f<HasType>();
+ f<int>(); // expected-note {{in instantiation of function template specialization 'GH223366::f<int>' requested here}}
+}
+}
diff --git a/clang/test/OpenMP/declare_reduction_ms_compat.cpp b/clang/test/OpenMP/declare_reduction_ms_compat.cpp
new file mode 100644
index 0000000000000..96bacfa3f7aec
--- /dev/null
+++ b/clang/test/OpenMP/declare_reduction_ms_compat.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -verify -fopenmp -fms-compatibility %s
+// RUN: %clang_cc1 -verify -fopenmp-simd -fms-compatibility %s
+
+struct foo {};
+struct has_baz {
+ typedef int baz;
+};
+
+template <typename T> struct S : T {
+ virtual void bar() {
+#pragma omp declare reduction(foo : baz : omp_out)
+ // expected-warning at -1 {{use of undeclared identifier 'baz'; unqualified lookup into dependent bases of class template 'S' is a Microsoft extension}}
+ // expected-error at -2 {{no type named 'baz' in 'S<foo>'}}
+ }
+};
+
+template <typename T> struct NotInstantiated : T {
+ virtual void bar() {
+#pragma omp declare reduction(foo : baz : omp_out)
+ // expected-warning at -1 {{use of undeclared identifier 'baz'; unqualified lookup into dependent bases of class template 'NotInstantiated' is a Microsoft extension}}
+ }
+};
+
+S<foo> s; // expected-note {{in instantiation of member function 'S<foo>::bar' requested here}}
+S<has_baz> t;
More information about the cfe-commits
mailing list