[flang-commits] [flang] [llvm] [Flang][OpenMP] Fix crash with character types in declare_reduction (PR #178038)
Krish Gupta via flang-commits
flang-commits at lists.llvm.org
Mon Jan 26 12:03:22 PST 2026
https://github.com/KrxGu created https://github.com/llvm/llvm-project/pull/178038
Fixes #177501
This PR fixes a compilation crash when using character types in OpenMP REDUCTION clauses with declare_reduction directives.
The problem was that character types weren't being handled properly during OpenMP lowering. Specifically:
- Missing character length parameters in hlfir.declare operations
- Incorrect type wrapping for by-ref reductions
- Missing special case handling for boxed/unboxed character types
The fix ensures character types are treated similarly to derived types throughout the reduction pipeline, since fir::isa_trivial() excludes them.
Added a regression test to verify the fix works for both allocatable and non-allocatable character reductions.
<img width="654" height="47" alt="image" src="https://github.com/user-attachments/assets/cc962f01-3432-44ce-befb-324644767c8b" />
>From 6a8024f2c495c2cdd02a8ec2e60cf4d92cdc50cd Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Wed, 26 Nov 2025 21:43:27 +0530
Subject: [PATCH 01/12] [flang][OpenMP] Support mappers in target update
to/from
Fixes #168701
---
flang/lib/Semantics/resolve-names.cpp | 60 +++++++++++++++++++
.../Lower/OpenMP/target-update-mapper.f90 | 52 ++++++++++++++++
.../Semantics/OpenMP/target-update-mapper.f90 | 54 +++++++++++++++++
3 files changed, 166 insertions(+)
create mode 100644 flang/test/Lower/OpenMP/target-update-mapper.f90
create mode 100644 flang/test/Semantics/OpenMP/target-update-mapper.f90
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 527be8645ff81..d57d6c9ebb901 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1634,6 +1634,8 @@ class OmpVisitor : public virtual DeclarationVisitor {
return true;
}
bool Pre(const parser::OmpMapClause &);
+ bool Pre(const parser::OmpClause::To &);
+ bool Pre(const parser::OmpClause::From &);
bool Pre(const parser::OpenMPSectionsConstruct &) {
PushScope(Scope::Kind::OtherConstruct, nullptr);
@@ -1879,6 +1881,64 @@ bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
return true;
}
+bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
+ // Resolve mapper names in "to" clauses (e.g., for target update)
+ auto &mods{OmpGetModifiers(x.v)};
+ if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
+ if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
+ auto &ultimate{symbol->GetUltimate()};
+ auto *misc{ultimate.detailsIf<MiscDetails>()};
+ auto *md{ultimate.detailsIf<MapperDetails>()};
+ if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
+ context().Say(mapper->v.source,
+ "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
+ else
+ mapper->v.symbol = symbol;
+ } else {
+ // Allow the special 'default' mapper identifier without prior
+ // declaration so lowering can recognize and handle it. Emit an
+ // error for any other missing mapper identifier.
+ if (mapper->v.source.ToString() == "default") {
+ mapper->v.symbol = &MakeSymbol(
+ mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
+ } else {
+ context().Say(
+ mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
+ }
+ }
+ }
+ return true;
+}
+
+bool OmpVisitor::Pre(const parser::OmpClause::From &x) {
+ // Resolve mapper names in "from" clauses (e.g., for target update)
+ auto &mods{OmpGetModifiers(x.v)};
+ if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
+ if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
+ auto &ultimate{symbol->GetUltimate()};
+ auto *misc{ultimate.detailsIf<MiscDetails>()};
+ auto *md{ultimate.detailsIf<MapperDetails>()};
+ if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
+ context().Say(mapper->v.source,
+ "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
+ else
+ mapper->v.symbol = symbol;
+ } else {
+ // Allow the special 'default' mapper identifier without prior
+ // declaration so lowering can recognize and handle it. Emit an
+ // error for any other missing mapper identifier.
+ if (mapper->v.source.ToString() == "default") {
+ mapper->v.symbol = &MakeSymbol(
+ mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
+ } else {
+ context().Say(
+ mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
+ }
+ }
+ }
+ return true;
+}
+
void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
const parser::OmpClauseList &clauses) {
// This "manually" walks the tree of the construct, because we need
diff --git a/flang/test/Lower/OpenMP/target-update-mapper.f90 b/flang/test/Lower/OpenMP/target-update-mapper.f90
new file mode 100644
index 0000000000000..d49ad51343679
--- /dev/null
+++ b/flang/test/Lower/OpenMP/target-update-mapper.f90
@@ -0,0 +1,52 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
+
+! Test mapper usage in target update to/from clauses
+
+program target_update_mapper
+ implicit none
+
+ integer, parameter :: n = 4
+
+ type :: typ
+ integer, allocatable :: a(:)
+ integer, allocatable :: b(:)
+ end type typ
+
+ !$omp declare mapper(custom: typ :: t) map(t%a)
+
+ ! CHECK-LABEL: omp.declare_mapper @_QQFcustom : !fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>
+
+ type(typ) :: t
+
+ allocate(t%a(n), source=1)
+ allocate(t%b(n), source=2)
+
+ !$omp target enter data map(alloc: t)
+
+ ! Test target update to with custom mapper
+ ! CHECK: %[[T_VAR:.*]] = fir.declare %{{.*}} {uniq_name = "_QFtarget_update_mapperEt"} : (!fir.ref<!fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>) -> !fir.ref<!fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>
+ ! CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%[[T_VAR]] : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
+ ! CHECK: omp.target_update motion_entries(%[[MAP_INFO]] : {{.*}})
+ t%a = 42
+ !$omp target update to(mapper(custom): t)
+
+ !$omp target
+ t%a(:) = t%a(:) / 2
+ t%b(:) = -1
+ !$omp end target
+
+ ! Test target update from with custom mapper
+ ! CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
+ ! CHECK: omp.target_update motion_entries(%[[MAP_INFO2]] : {{.*}})
+ !$omp target update from(mapper(custom): t)
+
+ ! Test target update to with default mapper
+ ! CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtyp_omp_default_mapper) -> {{.*}}
+ ! CHECK: omp.target_update motion_entries(%[[MAP_INFO3]] : {{.*}})
+ !$omp target update to(mapper(default): t)
+
+ !$omp target exit data map(delete: t)
+ deallocate(t%a)
+ deallocate(t%b)
+
+end program target_update_mapper
diff --git a/flang/test/Semantics/OpenMP/target-update-mapper.f90 b/flang/test/Semantics/OpenMP/target-update-mapper.f90
new file mode 100644
index 0000000000000..f03496e155568
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/target-update-mapper.f90
@@ -0,0 +1,54 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+
+! Test mapper usage in target update to/from clauses
+
+program target_update_mapper
+ implicit none
+
+ integer, parameter :: n = 4
+
+ type :: typ
+ integer, allocatable :: a(:)
+ integer, allocatable :: b(:)
+ end type typ
+
+ !$omp declare mapper(custom: typ :: t) map(t%a)
+
+ type(typ) :: t
+ integer :: not_a_mapper
+ allocate(t%a(n), source=1)
+ allocate(t%b(n), source=2)
+
+ !$omp target enter data map(alloc: t)
+
+ ! Valid: using custom mapper with target update to
+ t%a = 42
+ !$omp target update to(mapper(custom): t)
+
+ !$omp target
+ t%a(:) = t%a(:) / 2
+ t%b(:) = -1
+ !$omp end target
+
+ ! Valid: using custom mapper with target update from
+ !$omp target update from(mapper(custom): t)
+
+ ! Valid: using default mapper explicitly
+ !$omp target update to(mapper(default): t)
+
+ print*, t%a
+ print*, t%b
+
+ !$omp target exit data map(delete: t)
+ deallocate(t%a)
+ deallocate(t%b)
+
+ ! Test error case: undefined mapper
+ !ERROR: 'undefined_mapper' not declared
+ !$omp target update to(mapper(undefined_mapper): t)
+
+ ! Test error case: wrong kind of symbol
+ !ERROR: Name 'not_a_mapper' should be a mapper name
+ !$omp target update from(mapper(not_a_mapper): t)
+
+end program target_update_mapper
>From 1822144899828403edbc2b641028226c4438428f Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Wed, 26 Nov 2025 22:37:45 +0530
Subject: [PATCH 02/12] [flang][OpenMP] Implement mapper lowering for target
update
Support mapper modifier in processMotionClauses for to/from clauses
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 29 ++++++++++++++-----
.../Lower/OpenMP/target-update-mapper.f90 | 9 +++---
2 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index b923e415231d6..d515c8e5ebf23 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1551,21 +1551,34 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
mlir::Location clauseLocation = converter.genLocation(source);
const auto &[expectation, mapper, iterator, objects] = clause.t;
- // TODO Support motion modifiers: mapper, iterator.
- if (mapper) {
- TODO(clauseLocation, "Mapper modifier is not supported yet");
- } else if (iterator) {
- TODO(clauseLocation, "Iterator modifier is not supported yet");
- }
-
mlir::omp::ClauseMapFlags mapTypeBits =
std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To>
? mlir::omp::ClauseMapFlags::to
: mlir::omp::ClauseMapFlags::from;
if (expectation && *expectation == omp::clause::To::Expectation::Present)
mapTypeBits |= mlir::omp::ClauseMapFlags::present;
+
+ // Support motion modifiers: mapper, iterator.
+ std::string mapperIdName = "__implicit_mapper";
+ if (mapper) {
+ // Only one mapper is allowed by the parser here.
+ assert(mapper->size() == 1 && "more than one mapper");
+ const semantics::Symbol *mapperSym = mapper->front().v.id().symbol;
+ mapperIdName = mapperSym->name().ToString();
+ if (mapperIdName != "default") {
+ // Mangle with the ultimate owner so that use-associated mapper
+ // identifiers resolve to the same symbol as their defining scope.
+ const semantics::Symbol &ultimate = mapperSym->GetUltimate();
+ mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
+ }
+ }
+ if (iterator) {
+ TODO(clauseLocation, "Iterator modifier is not supported yet");
+ }
+
processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
- parentMemberIndices, result.mapVars, mapSymbols);
+ parentMemberIndices, result.mapVars, mapSymbols,
+ mapperIdName);
};
bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn);
diff --git a/flang/test/Lower/OpenMP/target-update-mapper.f90 b/flang/test/Lower/OpenMP/target-update-mapper.f90
index d49ad51343679..df23ea79c08f2 100644
--- a/flang/test/Lower/OpenMP/target-update-mapper.f90
+++ b/flang/test/Lower/OpenMP/target-update-mapper.f90
@@ -24,9 +24,8 @@ program target_update_mapper
!$omp target enter data map(alloc: t)
! Test target update to with custom mapper
- ! CHECK: %[[T_VAR:.*]] = fir.declare %{{.*}} {uniq_name = "_QFtarget_update_mapperEt"} : (!fir.ref<!fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>) -> !fir.ref<!fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>
- ! CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%[[T_VAR]] : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
- ! CHECK: omp.target_update motion_entries(%[[MAP_INFO]] : {{.*}})
+ ! CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
+ ! CHECK: omp.target_update map_entries(%[[MAP_INFO]] : {{.*}})
t%a = 42
!$omp target update to(mapper(custom): t)
@@ -37,12 +36,12 @@ program target_update_mapper
! Test target update from with custom mapper
! CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
- ! CHECK: omp.target_update motion_entries(%[[MAP_INFO2]] : {{.*}})
+ ! CHECK: omp.target_update map_entries(%[[MAP_INFO2]] : {{.*}})
!$omp target update from(mapper(custom): t)
! Test target update to with default mapper
! CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtyp_omp_default_mapper) -> {{.*}}
- ! CHECK: omp.target_update motion_entries(%[[MAP_INFO3]] : {{.*}})
+ ! CHECK: omp.target_update map_entries(%[[MAP_INFO3]] : {{.*}})
!$omp target update to(mapper(default): t)
!$omp target exit data map(delete: t)
>From 36d928415bcd2e5fbce5ee1273e17aedcf4c0df2 Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Fri, 28 Nov 2025 00:32:22 +0530
Subject: [PATCH 03/12] [flang][OpenMP] Refactor mapper resolution per review
Extract ResolveMapperModifier helper and simplify tests
---
flang/lib/Semantics/resolve-names.cpp | 94 ++++++-------------
flang/test/Lower/OpenMP/declare-mapper.f90 | 34 +++++++
.../Lower/OpenMP/target-update-mapper.f90 | 51 ----------
.../Semantics/OpenMP/target-update-mapper.f90 | 36 ++-----
4 files changed, 67 insertions(+), 148 deletions(-)
delete mode 100644 flang/test/Lower/OpenMP/target-update-mapper.f90
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index d57d6c9ebb901..453bf0d965bcd 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1782,6 +1782,7 @@ class OmpVisitor : public virtual DeclarationVisitor {
}
private:
+ void ResolveMapperModifier(parser::OmpMapper &mapper);
void ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
const parser::OmpClauseList &clauses);
void ProcessReductionSpecifier(const parser::OmpReductionSpecifier &spec,
@@ -1853,90 +1854,49 @@ void OmpVisitor::Post(const parser::OmpStylizedInstance &x) { //
bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
auto &mods{OmpGetModifiers(x)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
- // TODO: Do we need a specific flag or type here, to distinghuish against
- // other ConstructName things? Leaving this for the full implementation
- // of mapper lowering.
- auto &ultimate{symbol->GetUltimate()};
- auto *misc{ultimate.detailsIf<MiscDetails>()};
- auto *md{ultimate.detailsIf<MapperDetails>()};
- if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
- context().Say(mapper->v.source,
- "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
- else
- mapper->v.symbol = symbol;
- } else {
- // Allow the special 'default' mapper identifier without prior
- // declaration so lowering can recognize and handle it. Emit an
- // error for any other missing mapper identifier.
- if (mapper->v.source.ToString() == "default") {
- mapper->v.symbol = &MakeSymbol(
- mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
- } else {
- context().Say(
- mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
- }
- }
+ ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
}
return true;
}
bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
- // Resolve mapper names in "to" clauses (e.g., for target update)
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
- auto &ultimate{symbol->GetUltimate()};
- auto *misc{ultimate.detailsIf<MiscDetails>()};
- auto *md{ultimate.detailsIf<MapperDetails>()};
- if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
- context().Say(mapper->v.source,
- "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
- else
- mapper->v.symbol = symbol;
- } else {
- // Allow the special 'default' mapper identifier without prior
- // declaration so lowering can recognize and handle it. Emit an
- // error for any other missing mapper identifier.
- if (mapper->v.source.ToString() == "default") {
- mapper->v.symbol = &MakeSymbol(
- mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
- } else {
- context().Say(
- mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
- }
- }
+ ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
}
return true;
}
bool OmpVisitor::Pre(const parser::OmpClause::From &x) {
- // Resolve mapper names in "from" clauses (e.g., for target update)
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
- auto &ultimate{symbol->GetUltimate()};
- auto *misc{ultimate.detailsIf<MiscDetails>()};
- auto *md{ultimate.detailsIf<MapperDetails>()};
- if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
- context().Say(mapper->v.source,
- "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
- else
- mapper->v.symbol = symbol;
+ ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
+ }
+ return true;
+}
+
+void OmpVisitor::ResolveMapperModifier(parser::OmpMapper &mapper) {
+ if (auto *symbol{FindSymbol(currScope(), mapper.v)}) {
+ auto &ultimate{symbol->GetUltimate()};
+ auto *misc{ultimate.detailsIf<MiscDetails>()};
+ auto *md{ultimate.detailsIf<MapperDetails>()};
+ if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
+ context().Say(mapper.v.source,
+ "Name '%s' should be a mapper name"_err_en_US, mapper.v.source);
+ else
+ mapper.v.symbol = symbol;
+ } else {
+ // Allow the special 'default' mapper identifier without prior
+ // declaration so lowering can recognize and handle it. Emit an
+ // error for any other missing mapper identifier.
+ if (mapper.v.source.ToString() == "default") {
+ mapper.v.symbol = &MakeSymbol(
+ mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
- // Allow the special 'default' mapper identifier without prior
- // declaration so lowering can recognize and handle it. Emit an
- // error for any other missing mapper identifier.
- if (mapper->v.source.ToString() == "default") {
- mapper->v.symbol = &MakeSymbol(
- mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
- } else {
- context().Say(
- mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
- }
+ context().Say(
+ mapper.v.source, "'%s' not declared"_err_en_US, mapper.v.source);
}
}
- return true;
}
void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
diff --git a/flang/test/Lower/OpenMP/declare-mapper.f90 b/flang/test/Lower/OpenMP/declare-mapper.f90
index 7eda1a4c497be..a2f228811e8f9 100644
--- a/flang/test/Lower/OpenMP/declare-mapper.f90
+++ b/flang/test/Lower/OpenMP/declare-mapper.f90
@@ -11,6 +11,7 @@
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -J %t %t/omp-declare-mapper-7.use.f90 -o - | FileCheck %t/omp-declare-mapper-7.use.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -module-dir %t %t/omp-declare-mapper-8.mod.f90 -o - >/dev/null
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -J %t %t/omp-declare-mapper-8.use.f90 -o - | FileCheck %t/omp-declare-mapper-8.use.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %t/omp-declare-mapper-9.f90 -o - | FileCheck %t/omp-declare-mapper-9.f90
!--- omp-declare-mapper-1.f90
subroutine declare_mapper_1
@@ -360,3 +361,36 @@ program use_module_default_mapper
a%x = 8
!$omp end target
end program use_module_default_mapper
+
+!--- omp-declare-mapper-9.f90
+! Test mapper usage in target update to/from clauses
+program target_update_mapper
+ type :: typ
+ integer :: a
+ integer :: b
+ end type typ
+
+ !CHECK: omp.declare_mapper @_QQFtarget_update_mapperFtyp_omp_default_mapper : !fir.type<_QFtarget_update_mapperTtyp{a:i32,b:i32}>
+ !CHECK: omp.declare_mapper @_QQFtarget_update_mapperFcustom : !fir.type<_QFtarget_update_mapperTtyp{a:i32,b:i32}>
+
+ !$omp declare mapper(typ :: t) map(t%a, t%b)
+ !$omp declare mapper(custom: typ :: t) map(t%a)
+
+ type(typ) :: t
+
+ ! Test target update to with custom mapper
+ !CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_mapperFcustom) -> {{.*}}
+ !CHECK: omp.target_update map_entries(%[[MAP_INFO]] : {{.*}})
+ !$omp target update to(mapper(custom): t)
+
+ ! Test target update from with custom mapper
+ !CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFtarget_update_mapperFcustom) -> {{.*}}
+ !CHECK: omp.target_update map_entries(%[[MAP_INFO2]] : {{.*}})
+ !$omp target update from(mapper(custom): t)
+
+ ! Test target update to with default mapper
+ !CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_mapperFtyp_omp_default_mapper) -> {{.*}}
+ !CHECK: omp.target_update map_entries(%[[MAP_INFO3]] : {{.*}})
+ !$omp target update to(mapper(default): t)
+
+end program target_update_mapper
diff --git a/flang/test/Lower/OpenMP/target-update-mapper.f90 b/flang/test/Lower/OpenMP/target-update-mapper.f90
deleted file mode 100644
index df23ea79c08f2..0000000000000
--- a/flang/test/Lower/OpenMP/target-update-mapper.f90
+++ /dev/null
@@ -1,51 +0,0 @@
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-
-! Test mapper usage in target update to/from clauses
-
-program target_update_mapper
- implicit none
-
- integer, parameter :: n = 4
-
- type :: typ
- integer, allocatable :: a(:)
- integer, allocatable :: b(:)
- end type typ
-
- !$omp declare mapper(custom: typ :: t) map(t%a)
-
- ! CHECK-LABEL: omp.declare_mapper @_QQFcustom : !fir.type<_QFTtyp{a:!fir.box<!fir.heap<!fir.array<?xi32>>>,b:!fir.box<!fir.heap<!fir.array<?xi32>>>}>
-
- type(typ) :: t
-
- allocate(t%a(n), source=1)
- allocate(t%b(n), source=2)
-
- !$omp target enter data map(alloc: t)
-
- ! Test target update to with custom mapper
- ! CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
- ! CHECK: omp.target_update map_entries(%[[MAP_INFO]] : {{.*}})
- t%a = 42
- !$omp target update to(mapper(custom): t)
-
- !$omp target
- t%a(:) = t%a(:) / 2
- t%b(:) = -1
- !$omp end target
-
- ! Test target update from with custom mapper
- ! CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
- ! CHECK: omp.target_update map_entries(%[[MAP_INFO2]] : {{.*}})
- !$omp target update from(mapper(custom): t)
-
- ! Test target update to with default mapper
- ! CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtyp_omp_default_mapper) -> {{.*}}
- ! CHECK: omp.target_update map_entries(%[[MAP_INFO3]] : {{.*}})
- !$omp target update to(mapper(default): t)
-
- !$omp target exit data map(delete: t)
- deallocate(t%a)
- deallocate(t%b)
-
-end program target_update_mapper
diff --git a/flang/test/Semantics/OpenMP/target-update-mapper.f90 b/flang/test/Semantics/OpenMP/target-update-mapper.f90
index f03496e155568..f37d288670594 100644
--- a/flang/test/Semantics/OpenMP/target-update-mapper.f90
+++ b/flang/test/Semantics/OpenMP/target-update-mapper.f90
@@ -1,53 +1,29 @@
! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
-! Test mapper usage in target update to/from clauses
+! Test mapper name resolution in target update to/from clauses
program target_update_mapper
- implicit none
-
- integer, parameter :: n = 4
-
type :: typ
- integer, allocatable :: a(:)
- integer, allocatable :: b(:)
+ integer :: a
end type typ
!$omp declare mapper(custom: typ :: t) map(t%a)
type(typ) :: t
integer :: not_a_mapper
- allocate(t%a(n), source=1)
- allocate(t%b(n), source=2)
- !$omp target enter data map(alloc: t)
-
- ! Valid: using custom mapper with target update to
- t%a = 42
+ ! Valid: using custom mapper
!$omp target update to(mapper(custom): t)
-
- !$omp target
- t%a(:) = t%a(:) / 2
- t%b(:) = -1
- !$omp end target
-
- ! Valid: using custom mapper with target update from
!$omp target update from(mapper(custom): t)
- ! Valid: using default mapper explicitly
+ ! Valid: using default mapper
!$omp target update to(mapper(default): t)
- print*, t%a
- print*, t%b
-
- !$omp target exit data map(delete: t)
- deallocate(t%a)
- deallocate(t%b)
-
- ! Test error case: undefined mapper
+ ! Error: undefined mapper
!ERROR: 'undefined_mapper' not declared
!$omp target update to(mapper(undefined_mapper): t)
- ! Test error case: wrong kind of symbol
+ ! Error: wrong kind of symbol
!ERROR: Name 'not_a_mapper' should be a mapper name
!$omp target update from(mapper(not_a_mapper): t)
>From 8d4ffd2b3d8e96904ab137ae73c9da9b49f6744e Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Fri, 28 Nov 2025 00:40:38 +0530
Subject: [PATCH 04/12] [flang] Fix clang-format issues
---
flang/lib/Semantics/resolve-names.cpp | 95 +++++++++++++--------------
1 file changed, 47 insertions(+), 48 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 453bf0d965bcd..db688f5d29549 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1890,8 +1890,8 @@ void OmpVisitor::ResolveMapperModifier(parser::OmpMapper &mapper) {
// declaration so lowering can recognize and handle it. Emit an
// error for any other missing mapper identifier.
if (mapper.v.source.ToString() == "default") {
- mapper.v.symbol = &MakeSymbol(
- mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
+ mapper.v.symbol =
+ &MakeSymbol(mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
context().Say(
mapper.v.source, "'%s' not declared"_err_en_US, mapper.v.source);
@@ -2407,7 +2407,7 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
}
symbol.SetBindName(std::move(*label));
if (!oldBindName.empty()) {
- if (const std::string * newBindName{symbol.GetBindName()}) {
+ if (const std::string *newBindName{symbol.GetBindName()}) {
if (oldBindName != *newBindName) {
Say(symbol.name(),
"The entity '%s' has multiple BIND names ('%s' and '%s')"_err_en_US,
@@ -2533,7 +2533,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
// expression semantics if the DeclTypeSpec is a valid TypeSpec.
// The grammar ensures that it's an intrinsic or derived type spec,
// not TYPE(*) or CLASS(*) or CLASS(T).
- if (const DeclTypeSpec * spec{state_.declTypeSpec}) {
+ if (const DeclTypeSpec *spec{state_.declTypeSpec}) {
switch (spec->category()) {
case DeclTypeSpec::Numeric:
case DeclTypeSpec::Logical:
@@ -2541,7 +2541,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
typeSpec.declTypeSpec = spec;
break;
case DeclTypeSpec::TypeDerived:
- if (const DerivedTypeSpec * derived{spec->AsDerived()}) {
+ if (const DerivedTypeSpec *derived{spec->AsDerived()}) {
CheckForAbstractType(derived->typeSymbol()); // C703
typeSpec.declTypeSpec = spec;
}
@@ -3132,8 +3132,8 @@ Symbol &ScopeHandler::MakeSymbol(const parser::Name &name, Attrs attrs) {
Symbol &ScopeHandler::MakeHostAssocSymbol(
const parser::Name &name, const Symbol &hostSymbol) {
Symbol &symbol{*NonDerivedTypeScope()
- .try_emplace(name.source, HostAssocDetails{hostSymbol})
- .first->second};
+ .try_emplace(name.source, HostAssocDetails{hostSymbol})
+ .first->second};
name.symbol = &symbol;
symbol.attrs() = hostSymbol.attrs(); // TODO: except PRIVATE, PUBLIC?
// These attributes can be redundantly reapplied without error
@@ -3221,7 +3221,7 @@ void ScopeHandler::ApplyImplicitRules(
if (context().HasError(symbol) || !NeedsType(symbol)) {
return;
}
- if (const DeclTypeSpec * type{GetImplicitType(symbol)}) {
+ if (const DeclTypeSpec *type{GetImplicitType(symbol)}) {
if (!skipImplicitTyping_) {
symbol.set(Symbol::Flag::Implicit);
symbol.SetType(*type);
@@ -3321,7 +3321,7 @@ const DeclTypeSpec *ScopeHandler::GetImplicitType(
const auto *type{implicitRulesMap_->at(scope).GetType(
symbol.name(), respectImplicitNoneType)};
if (type) {
- if (const DerivedTypeSpec * derived{type->AsDerived()}) {
+ if (const DerivedTypeSpec *derived{type->AsDerived()}) {
// Resolve any forward-referenced derived type; a quick no-op else.
auto &instantiatable{*const_cast<DerivedTypeSpec *>(derived)};
instantiatable.Instantiate(currScope());
@@ -4342,7 +4342,7 @@ Scope *ModuleVisitor::FindModule(const parser::Name &name,
if (scope) {
if (DoesScopeContain(scope, currScope())) { // 14.2.2(1)
std::optional<SourceName> submoduleName;
- if (const Scope * container{FindModuleOrSubmoduleContaining(currScope())};
+ if (const Scope *container{FindModuleOrSubmoduleContaining(currScope())};
container && container->IsSubmodule()) {
submoduleName = container->GetName();
}
@@ -4447,7 +4447,7 @@ bool InterfaceVisitor::isAbstract() const {
void InterfaceVisitor::AddSpecificProcs(
const std::list<parser::Name> &names, ProcedureKind kind) {
- if (Symbol * symbol{GetGenericInfo().symbol};
+ if (Symbol *symbol{GetGenericInfo().symbol};
symbol && symbol->has<GenericDetails>()) {
for (const auto &name : names) {
specificsForGenericProcs_.emplace(symbol, std::make_pair(&name, kind));
@@ -4547,7 +4547,7 @@ void GenericHandler::DeclaredPossibleSpecificProc(Symbol &proc) {
}
void InterfaceVisitor::ResolveNewSpecifics() {
- if (Symbol * generic{genericInfo_.top().symbol};
+ if (Symbol *generic{genericInfo_.top().symbol};
generic && generic->has<GenericDetails>()) {
ResolveSpecificsInGeneric(*generic, false);
}
@@ -4633,7 +4633,7 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) {
name.source);
MakeSymbol(name, Attrs{}, UnknownDetails{});
} else if (auto *entity{ultimate.detailsIf<EntityDetails>()};
- entity && !ultimate.has<ProcEntityDetails>()) {
+ entity && !ultimate.has<ProcEntityDetails>()) {
resultType = entity->type();
ultimate.details() = UnknownDetails{}; // will be replaced below
} else {
@@ -4692,7 +4692,7 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) {
} else {
Message &msg{Say(*suffix.resultName,
"RESULT(%s) may appear only in a function"_err_en_US)};
- if (const Symbol * subprogram{InclusiveScope().symbol()}) {
+ if (const Symbol *subprogram{InclusiveScope().symbol()}) {
msg.Attach(subprogram->name(), "Containing subprogram"_en_US);
}
}
@@ -5214,7 +5214,7 @@ Symbol *ScopeHandler::FindSeparateModuleProcedureInterface(
symbol = generic->specific();
}
}
- if (const Symbol * defnIface{FindSeparateModuleSubprogramInterface(symbol)}) {
+ if (const Symbol *defnIface{FindSeparateModuleSubprogramInterface(symbol)}) {
// Error recovery in case of multiple definitions
symbol = const_cast<Symbol *>(defnIface);
}
@@ -5353,8 +5353,8 @@ bool SubprogramVisitor::HandlePreviousCalls(
return generic->specific() &&
HandlePreviousCalls(name, *generic->specific(), subpFlag);
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}; proc &&
- !proc->isDummy() &&
- !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) {
+ !proc->isDummy() &&
+ !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) {
// There's a symbol created for previous calls to this subprogram or
// ENTRY's name. We have to replace that symbol in situ to avoid the
// obligation to rewrite symbol pointers in the parse tree.
@@ -5396,7 +5396,7 @@ const Symbol *SubprogramVisitor::CheckExtantProc(
if (prev) {
if (IsDummy(*prev)) {
} else if (auto *entity{prev->detailsIf<EntityDetails>()};
- IsPointer(*prev) && entity && !entity->type()) {
+ IsPointer(*prev) && entity && !entity->type()) {
// POINTER attribute set before interface
} else if (inInterfaceBlock() && currScope() != prev->owner()) {
// Procedures in an INTERFACE block do not resolve to symbols
@@ -5468,7 +5468,7 @@ Symbol *SubprogramVisitor::PushSubprogramScope(const parser::Name &name,
}
set_inheritFromParent(false); // interfaces don't inherit, even if MODULE
}
- if (Symbol * found{FindSymbol(name)};
+ if (Symbol *found{FindSymbol(name)};
found && found->has<HostAssocDetails>()) {
found->set(subpFlag); // PushScope() created symbol
}
@@ -6320,9 +6320,9 @@ void DeclarationVisitor::Post(const parser::VectorTypeSpec &x) {
vectorDerivedType.CookParameters(GetFoldingContext());
}
- if (const DeclTypeSpec *
- extant{ppcBuiltinTypesScope->FindInstantiatedDerivedType(
- vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) {
+ if (const DeclTypeSpec *extant{
+ ppcBuiltinTypesScope->FindInstantiatedDerivedType(
+ vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) {
// This derived type and parameter expressions (if any) are already present
// in the __ppc_intrinsics scope.
SetDeclTypeSpec(*extant);
@@ -6343,8 +6343,8 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Type &) {
}
void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Type &type) {
- const parser::Name &derivedName{std::get<parser::Name>(type.v.t)};
- if (const Symbol * derivedSymbol{derivedName.symbol}) {
+ const parser::Name &derivedName{std::get<parser::Name>(type.derived.t)};
+ if (const Symbol *derivedSymbol{derivedName.symbol}) {
CheckForAbstractType(*derivedSymbol); // C706
}
}
@@ -6413,8 +6413,8 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
if (!spec->MightBeParameterized()) {
spec->EvaluateParameters(context());
}
- if (const DeclTypeSpec *
- extant{currScope().FindInstantiatedDerivedType(*spec, category)}) {
+ if (const DeclTypeSpec *extant{
+ currScope().FindInstantiatedDerivedType(*spec, category)}) {
// This derived type and parameter expressions (if any) are already present
// in this scope.
SetDeclTypeSpec(*extant);
@@ -6445,8 +6445,7 @@ void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Record &rec) {
if (auto spec{ResolveDerivedType(typeName)}) {
spec->CookParameters(GetFoldingContext());
spec->EvaluateParameters(context());
- if (const DeclTypeSpec *
- extant{currScope().FindInstantiatedDerivedType(
+ if (const DeclTypeSpec *extant{currScope().FindInstantiatedDerivedType(
*spec, DeclTypeSpec::TypeDerived)}) {
SetDeclTypeSpec(*extant);
} else {
@@ -7460,7 +7459,7 @@ bool DeclarationVisitor::PassesLocalityChecks(
"Coarray '%s' not allowed in a %s locality-spec"_err_en_US, specName);
return false;
}
- if (const DeclTypeSpec * type{symbol.GetType()}) {
+ if (const DeclTypeSpec *type{symbol.GetType()}) {
if (type->IsPolymorphic() && IsDummy(symbol) && !IsPointer(symbol) &&
!isReduce) { // F'2023 C1130
SayWithDecl(name, symbol,
@@ -7694,7 +7693,7 @@ Symbol *DeclarationVisitor::NoteInterfaceName(const parser::Name &name) {
}
void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
- if (const Symbol * symbol{name.symbol}) {
+ if (const Symbol *symbol{name.symbol}) {
const Symbol &ultimate{symbol->GetUltimate()};
if (!context().HasError(*symbol) && !context().HasError(ultimate) &&
!BypassGeneric(ultimate).HasExplicitInterface()) {
@@ -8014,7 +8013,7 @@ bool ConstructVisitor::Pre(const parser::DataStmtValue &x) {
auto &mutableData{const_cast<parser::DataStmtConstant &>(data)};
if (auto *elem{parser::Unwrap<parser::ArrayElement>(mutableData)}) {
if (const auto *name{std::get_if<parser::Name>(&elem->base.u)}) {
- if (const Symbol * symbol{FindSymbol(*name)};
+ if (const Symbol *symbol{FindSymbol(*name)};
symbol && symbol->GetUltimate().has<DerivedTypeDetails>()) {
mutableData.u = elem->ConvertToStructureConstructor(
DerivedTypeSpec{name->source, *symbol});
@@ -8160,15 +8159,15 @@ void ConstructVisitor::Post(const parser::SelectTypeStmt &x) {
}
}
} else {
- if (const Symbol *
- whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
+ if (const Symbol *whole{
+ UnwrapWholeSymbolDataRef(association.selector.expr)}) {
ConvertToObjectEntity(const_cast<Symbol &>(*whole));
if (!IsVariableName(*whole)) {
Say(association.selector.source, // C901
"Selector is not a variable"_err_en_US);
association = {};
}
- if (const DeclTypeSpec * type{whole->GetType()}) {
+ if (const DeclTypeSpec *type{whole->GetType()}) {
if (!type->IsPolymorphic()) { // C1159
Say(association.selector.source,
"Selector '%s' in SELECT TYPE statement must be "
@@ -8308,8 +8307,8 @@ Symbol *ConstructVisitor::MakeAssocEntity() {
"The associate name '%s' is already used in this associate statement"_err_en_US);
return nullptr;
}
- } else if (const Symbol *
- whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
+ } else if (const Symbol *whole{
+ UnwrapWholeSymbolDataRef(association.selector.expr)}) {
symbol = &MakeSymbol(whole->name());
} else {
return nullptr;
@@ -8930,7 +8929,7 @@ bool DeclarationVisitor::CheckForHostAssociatedImplicit(
if (name.symbol) {
ApplyImplicitRules(*name.symbol, true);
}
- if (Scope * host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) {
+ if (Scope *host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) {
Symbol *hostSymbol{nullptr};
if (!name.symbol) {
if (currScope().CanImport(name.source)) {
@@ -9001,7 +9000,7 @@ const parser::Name *DeclarationVisitor::FindComponent(
if (!type) {
return nullptr; // should have already reported error
}
- if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
+ if (const IntrinsicTypeSpec *intrinsic{type->AsIntrinsic()}) {
auto category{intrinsic->category()};
MiscDetails::Kind miscKind{MiscDetails::Kind::None};
if (component.source == "kind") {
@@ -9023,7 +9022,7 @@ const parser::Name *DeclarationVisitor::FindComponent(
}
} else if (DerivedTypeSpec * derived{type->AsDerived()}) {
derived->Instantiate(currScope()); // in case of forward referenced type
- if (const Scope * scope{derived->scope()}) {
+ if (const Scope *scope{derived->scope()}) {
if (Resolve(component, scope->FindComponent(component.source))) {
if (auto msg{CheckAccessibleSymbol(currScope(), *component.symbol)}) {
context().Say(component.source, *msg);
@@ -9180,8 +9179,8 @@ void DeclarationVisitor::PointerInitialization(
if (evaluate::IsNullProcedurePointer(&*expr)) {
CHECK(!details->init());
details->set_init(nullptr);
- } else if (const Symbol *
- targetSymbol{evaluate::UnwrapWholeSymbolDataRef(*expr)}) {
+ } else if (const Symbol *targetSymbol{
+ evaluate::UnwrapWholeSymbolDataRef(*expr)}) {
CHECK(!details->init());
details->set_init(*targetSymbol);
} else {
@@ -9785,7 +9784,7 @@ void ResolveNamesVisitor::EarlyDummyTypeDeclaration(
for (const auto &ent : entities) {
const auto &objName{std::get<parser::ObjectName>(ent.t)};
Resolve(objName, FindInScope(currScope(), objName));
- if (Symbol * symbol{objName.symbol};
+ if (Symbol *symbol{objName.symbol};
symbol && IsDummy(*symbol) && NeedsType(*symbol)) {
if (!type) {
type = ProcessTypeSpec(declTypeSpec);
@@ -9924,7 +9923,7 @@ void ResolveNamesVisitor::FinishSpecificationPart(
if (auto *proc{symbol.detailsIf<ProcEntityDetails>()}; proc &&
!proc->isDummy() && !IsPointer(symbol) &&
!symbol.attrs().test(Attr::BIND_C)) {
- if (const Symbol * iface{proc->procInterface()};
+ if (const Symbol *iface{proc->procInterface()};
iface && IsBindCProcedure(*iface)) {
SetImplicitAttr(symbol, Attr::BIND_C);
SetBindNameOn(symbol);
@@ -10057,7 +10056,7 @@ bool ResolveNamesVisitor::Pre(const parser::PointerAssignmentStmt &x) {
Symbol *ptrSymbol{parser::GetLastName(dataRef).symbol};
Walk(bounds);
// Resolve unrestricted specific intrinsic procedures as in "p => cos".
- if (const parser::Name * name{parser::Unwrap<parser::Name>(expr)}) {
+ if (const parser::Name *name{parser::Unwrap<parser::Name>(expr)}) {
if (NameIsKnownOrIntrinsic(*name)) {
if (Symbol * symbol{name->symbol}) {
if (IsProcedurePointer(ptrSymbol) &&
@@ -10515,8 +10514,8 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
// implied SAVE so that evaluate::IsSaved() will return true.
if (node.scope()->kind() == Scope::Kind::MainProgram) {
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
- if (const DeclTypeSpec * type{object->type()}) {
- if (const DerivedTypeSpec * derived{type->AsDerived()}) {
+ if (const DeclTypeSpec *type{object->type()}) {
+ if (const DerivedTypeSpec *derived{type->AsDerived()}) {
if (!IsSaved(symbol) && FindCoarrayPotentialComponent(*derived)) {
SetImplicitAttr(symbol, Attr::SAVE);
}
@@ -10775,7 +10774,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) {
if (DerivedTypeSpec * spec{scope.derivedTypeSpec()}) {
spec->Instantiate(currScope());
const Symbol &origTypeSymbol{spec->typeSymbol()};
- if (const Scope * origTypeScope{origTypeSymbol.scope()}) {
+ if (const Scope *origTypeScope{origTypeSymbol.scope()}) {
CHECK(origTypeScope->IsDerivedType() &&
origTypeScope->symbol() == &origTypeSymbol);
auto &foldingContext{GetFoldingContext()};
@@ -10786,7 +10785,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) {
if (IsPointer(comp)) {
if (auto *details{comp.detailsIf<ObjectEntityDetails>()}) {
auto origDetails{origComp.get<ObjectEntityDetails>()};
- if (const MaybeExpr & init{origDetails.init()}) {
+ if (const MaybeExpr &init{origDetails.init()}) {
SomeExpr newInit{*init};
MaybeExpr folded{FoldExpr(std::move(newInit))};
details->set_init(std::move(folded));
>From 816356c01e9538852c8fe1f2df1e5aa023c49c5c Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Fri, 28 Nov 2025 01:06:13 +0530
Subject: [PATCH 05/12] [flang] Fix test expectations for program-scoped mapper
names
---
flang/test/Lower/OpenMP/declare-mapper.f90 | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/flang/test/Lower/OpenMP/declare-mapper.f90 b/flang/test/Lower/OpenMP/declare-mapper.f90
index a2f228811e8f9..18de556e2ce7d 100644
--- a/flang/test/Lower/OpenMP/declare-mapper.f90
+++ b/flang/test/Lower/OpenMP/declare-mapper.f90
@@ -370,8 +370,8 @@ program target_update_mapper
integer :: b
end type typ
- !CHECK: omp.declare_mapper @_QQFtarget_update_mapperFtyp_omp_default_mapper : !fir.type<_QFtarget_update_mapperTtyp{a:i32,b:i32}>
- !CHECK: omp.declare_mapper @_QQFtarget_update_mapperFcustom : !fir.type<_QFtarget_update_mapperTtyp{a:i32,b:i32}>
+ !CHECK: omp.declare_mapper @_QQFcustom : !fir.type<_QFTtyp{a:i32,b:i32}>
+ !CHECK: omp.declare_mapper @_QQFtyp_omp_default_mapper : !fir.type<_QFTtyp{a:i32,b:i32}>
!$omp declare mapper(typ :: t) map(t%a, t%b)
!$omp declare mapper(custom: typ :: t) map(t%a)
@@ -379,17 +379,17 @@ program target_update_mapper
type(typ) :: t
! Test target update to with custom mapper
- !CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_mapperFcustom) -> {{.*}}
+ !CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO]] : {{.*}})
!$omp target update to(mapper(custom): t)
! Test target update from with custom mapper
- !CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFtarget_update_mapperFcustom) -> {{.*}}
+ !CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO2]] : {{.*}})
!$omp target update from(mapper(custom): t)
! Test target update to with default mapper
- !CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_mapperFtyp_omp_default_mapper) -> {{.*}}
+ !CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtyp_omp_default_mapper) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO3]] : {{.*}})
!$omp target update to(mapper(default): t)
>From 481c8eed33d9a639b59b943b0eee2a27dd15e690 Mon Sep 17 00:00:00 2001
From: Krish Gupta <krishgupta at Krishs-MacBook-Air.local>
Date: Thu, 4 Dec 2025 23:45:07 +0530
Subject: [PATCH 06/12] [flang] Extract mapper resolution into helper function
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 61 +++++++++++++---------
1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index d515c8e5ebf23..fba8a6b5945b3 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1449,6 +1449,41 @@ void ClauseProcessor::processMapObjects(
}
}
+/// Extract and mangle the mapper identifier name from a mapper clause.
+/// Returns "__implicit_mapper" if no mapper is specified, or "default" if
+/// the default mapper is specified, otherwise returns the mangled mapper name.
+/// This handles both the Map clause (which uses a vector of mappers) and
+/// To/From clauses (which use a DefinedOperator).
+template <typename MapperType>
+static std::string
+getMapperIdentifier(lower::AbstractConverter &converter,
+ const std::optional<MapperType> &mapper) {
+ std::string mapperIdName = "__implicit_mapper";
+ if (mapper) {
+ const semantics::Symbol *mapperSym = nullptr;
+
+ // Handle different mapper types
+ if constexpr (std::is_same_v<MapperType, omp::clause::DefinedOperator>) {
+ // For To/From clauses: mapper is a DefinedOperator
+ assert(mapper->size() == 1 && "more than one mapper");
+ mapperSym = mapper->front().v.id().symbol;
+ } else {
+ // For Map clause: mappers is a vector
+ assert(mapper->size() == 1 && "more than one mapper");
+ mapperSym = mapper->front().v.id().symbol;
+ }
+
+ mapperIdName = mapperSym->name().ToString();
+ if (mapperIdName != "default") {
+ // Mangle with the ultimate owner so that use-associated mapper
+ // identifiers resolve to the same symbol as their defining scope.
+ const semantics::Symbol &ultimate = mapperSym->GetUltimate();
+ mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
+ }
+ }
+ return mapperIdName;
+}
+
bool ClauseProcessor::processMap(
mlir::Location currentLocation, lower::StatementContext &stmtCtx,
mlir::omp::MapClauseOps &result, llvm::omp::Directive directive,
@@ -1517,17 +1552,7 @@ bool ClauseProcessor::processMap(
TODO(currentLocation,
"Support for iterator modifiers is not implemented yet");
}
- if (mappers) {
- assert(mappers->size() == 1 && "more than one mapper");
- const semantics::Symbol *mapperSym = mappers->front().v.id().symbol;
- mapperIdName = mapperSym->name().ToString();
- if (mapperIdName != "default") {
- // Mangle with the ultimate owner so that use-associated mapper
- // identifiers resolve to the same symbol as their defining scope.
- const semantics::Symbol &ultimate = mapperSym->GetUltimate();
- mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
- }
- }
+ mapperIdName = getMapperIdentifier(converter, mappers);
processMapObjects(stmtCtx, clauseLocation,
std::get<omp::ObjectList>(clause.t), mapTypeBits,
@@ -1559,19 +1584,7 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
mapTypeBits |= mlir::omp::ClauseMapFlags::present;
// Support motion modifiers: mapper, iterator.
- std::string mapperIdName = "__implicit_mapper";
- if (mapper) {
- // Only one mapper is allowed by the parser here.
- assert(mapper->size() == 1 && "more than one mapper");
- const semantics::Symbol *mapperSym = mapper->front().v.id().symbol;
- mapperIdName = mapperSym->name().ToString();
- if (mapperIdName != "default") {
- // Mangle with the ultimate owner so that use-associated mapper
- // identifiers resolve to the same symbol as their defining scope.
- const semantics::Symbol &ultimate = mapperSym->GetUltimate();
- mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
- }
- }
+ std::string mapperIdName = getMapperIdentifier(converter, mapper);
if (iterator) {
TODO(clauseLocation, "Iterator modifier is not supported yet");
}
>From 9be31485c21adadd67ada6c44adb5d45b2ce9b4b Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Thu, 15 Jan 2026 21:21:08 +0530
Subject: [PATCH 07/12] Refactor mapper code based on review feedback
- Use early return in getMapperIdentifier to reduce nesting
- Remove redundant if constexpr with identical branches
- Make ResolveMapperModifier parameter const
- Remove unnecessary const_cast at call sites
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 35 ++++++++--------------
flang/lib/Semantics/resolve-names.cpp | 14 ++++-----
2 files changed, 20 insertions(+), 29 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index fba8a6b5945b3..961285dc1e835 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1458,28 +1458,19 @@ template <typename MapperType>
static std::string
getMapperIdentifier(lower::AbstractConverter &converter,
const std::optional<MapperType> &mapper) {
- std::string mapperIdName = "__implicit_mapper";
- if (mapper) {
- const semantics::Symbol *mapperSym = nullptr;
-
- // Handle different mapper types
- if constexpr (std::is_same_v<MapperType, omp::clause::DefinedOperator>) {
- // For To/From clauses: mapper is a DefinedOperator
- assert(mapper->size() == 1 && "more than one mapper");
- mapperSym = mapper->front().v.id().symbol;
- } else {
- // For Map clause: mappers is a vector
- assert(mapper->size() == 1 && "more than one mapper");
- mapperSym = mapper->front().v.id().symbol;
- }
-
- mapperIdName = mapperSym->name().ToString();
- if (mapperIdName != "default") {
- // Mangle with the ultimate owner so that use-associated mapper
- // identifiers resolve to the same symbol as their defining scope.
- const semantics::Symbol &ultimate = mapperSym->GetUltimate();
- mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
- }
+ if (!mapper)
+ return "__implicit_mapper";
+
+ // Handle mapper types (both have the same structure)
+ assert(mapper->size() == 1 && "more than one mapper");
+ const semantics::Symbol *mapperSym = mapper->front().v.id().symbol;
+
+ std::string mapperIdName = mapperSym->name().ToString();
+ if (mapperIdName != "default") {
+ // Mangle with the ultimate owner so that use-associated mapper
+ // identifiers resolve to the same symbol as their defining scope.
+ const semantics::Symbol &ultimate = mapperSym->GetUltimate();
+ mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
}
return mapperIdName;
}
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index db688f5d29549..1190dfbaad2e7 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1782,7 +1782,7 @@ class OmpVisitor : public virtual DeclarationVisitor {
}
private:
- void ResolveMapperModifier(parser::OmpMapper &mapper);
+ void ResolveMapperModifier(const parser::OmpMapper &mapper);
void ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
const parser::OmpClauseList &clauses);
void ProcessReductionSpecifier(const parser::OmpReductionSpecifier &spec,
@@ -1854,7 +1854,7 @@ void OmpVisitor::Post(const parser::OmpStylizedInstance &x) { //
bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
auto &mods{OmpGetModifiers(x)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
+ ResolveMapperModifier(*mapper);
}
return true;
}
@@ -1862,7 +1862,7 @@ bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
+ ResolveMapperModifier(*mapper);
}
return true;
}
@@ -1870,12 +1870,12 @@ bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
bool OmpVisitor::Pre(const parser::OmpClause::From &x) {
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
- ResolveMapperModifier(const_cast<parser::OmpMapper &>(*mapper));
+ ResolveMapperModifier(*mapper);
}
return true;
}
-void OmpVisitor::ResolveMapperModifier(parser::OmpMapper &mapper) {
+void OmpVisitor::ResolveMapperModifier(const parser::OmpMapper &mapper) {
if (auto *symbol{FindSymbol(currScope(), mapper.v)}) {
auto &ultimate{symbol->GetUltimate()};
auto *misc{ultimate.detailsIf<MiscDetails>()};
@@ -1884,13 +1884,13 @@ void OmpVisitor::ResolveMapperModifier(parser::OmpMapper &mapper) {
context().Say(mapper.v.source,
"Name '%s' should be a mapper name"_err_en_US, mapper.v.source);
else
- mapper.v.symbol = symbol;
+ const_cast<parser::OmpMapper &>(mapper).v.symbol = symbol;
} else {
// Allow the special 'default' mapper identifier without prior
// declaration so lowering can recognize and handle it. Emit an
// error for any other missing mapper identifier.
if (mapper.v.source.ToString() == "default") {
- mapper.v.symbol =
+ const_cast<parser::OmpMapper &>(mapper).v.symbol =
&MakeSymbol(mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
context().Say(
>From e5a71f397a84a985e2c0470db226ad29af7f35fa Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Thu, 15 Jan 2026 21:30:08 +0530
Subject: [PATCH 08/12] Remove unnecessary const_cast for mutable symbol member
The symbol field is already marked mutable in the Name struct,
so it can be modified directly through a const reference.
---
flang/lib/Semantics/resolve-names.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 1190dfbaad2e7..6cfcbba63b708 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1884,13 +1884,13 @@ void OmpVisitor::ResolveMapperModifier(const parser::OmpMapper &mapper) {
context().Say(mapper.v.source,
"Name '%s' should be a mapper name"_err_en_US, mapper.v.source);
else
- const_cast<parser::OmpMapper &>(mapper).v.symbol = symbol;
+ mapper.v.symbol = symbol;
} else {
// Allow the special 'default' mapper identifier without prior
// declaration so lowering can recognize and handle it. Emit an
// error for any other missing mapper identifier.
if (mapper.v.source.ToString() == "default") {
- const_cast<parser::OmpMapper &>(mapper).v.symbol =
+ mapper.v.symbol =
&MakeSymbol(mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
context().Say(
>From e09e918d0320b9b5adc46b6eb8c51d2c2859c4bd Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Fri, 16 Jan 2026 00:11:16 +0530
Subject: [PATCH 09/12] Add offloading test for target update with custom
mappers
Use separate objects (obj1, obj2) for custom and default mapper
tests to avoid device memory mapping conflicts.
---
.../fortran/target-update-custom-mapper.f90 | 85 +++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 offload/test/offloading/fortran/target-update-custom-mapper.f90
diff --git a/offload/test/offloading/fortran/target-update-custom-mapper.f90 b/offload/test/offloading/fortran/target-update-custom-mapper.f90
new file mode 100644
index 0000000000000..e3384c2911d47
--- /dev/null
+++ b/offload/test/offloading/fortran/target-update-custom-mapper.f90
@@ -0,0 +1,85 @@
+! Test custom mappers with target update to/from clauses
+! REQUIRES: flang, amdgpu
+
+! RUN: %libomptarget-compile-fortran-run-and-check-generic
+
+program target_update_mapper_test
+ implicit none
+ integer, parameter :: n = 100
+
+ type :: my_type
+ integer :: a(n)
+ integer :: b(n)
+ end type my_type
+
+ ! Declare custom mapper that only maps field 'a'
+ !$omp declare mapper(custom : my_type :: t) map(t%a)
+
+ ! Declare default mapper that maps both fields
+ !$omp declare mapper(my_type :: t) map(t%a, t%b)
+
+ type(my_type) :: obj1, obj2
+ integer :: i, sum_a, sum_b
+
+ ! ========== Test 1: Custom mapper (field 'a' only) ==========
+
+ ! Initialize data on host
+ do i = 1, n
+ obj1%a(i) = i
+ obj1%b(i) = i * 2
+ end do
+
+ ! Allocate and update using custom mapper (only 'a')
+ !$omp target enter data map(mapper(custom), alloc: obj1)
+
+ obj1%a = 10
+ !$omp target update to(mapper(custom): obj1)
+
+ obj1%a = 0
+ !$omp target update from(mapper(custom): obj1)
+
+ sum_a = sum(obj1%a)
+ sum_b = sum(obj1%b)
+
+ ! CHECK: Sum of a (custom mapper): 1000
+ print *, "Sum of a (custom mapper):", sum_a
+
+ ! Field 'b' was never mapped with custom mapper
+ ! CHECK: Sum of b (never mapped): 10100
+ print *, "Sum of b (never mapped):", sum_b
+
+ !$omp target exit data map(mapper(custom), delete: obj1)
+
+ ! ========== Test 2: Default mapper (both fields) ==========
+
+ ! Initialize separate object for default mapper test
+ do i = 1, n
+ obj2%a(i) = 20
+ obj2%b(i) = 30
+ end do
+
+ ! Allocate and update using default mapper (both 'a' and 'b')
+ !$omp target enter data map(mapper(default), alloc: obj2)
+
+ !$omp target update to(mapper(default): obj2)
+
+ obj2%a = 0
+ obj2%b = 0
+
+ !$omp target update from(mapper(default): obj2)
+
+ sum_a = sum(obj2%a)
+ sum_b = sum(obj2%b)
+
+ ! CHECK: Sum of a (default mapper): 2000
+ print *, "Sum of a (default mapper):", sum_a
+
+ ! CHECK: Sum of b (default mapper): 3000
+ print *, "Sum of b (default mapper):", sum_b
+
+ !$omp target exit data map(mapper(default), delete: obj2)
+
+ ! CHECK: Test passed!
+ print *, "Test passed!"
+
+end program target_update_mapper_test
>From 5dc4a9f9592e4f846b4d6dca59e71f7a1132a0b7 Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Fri, 16 Jan 2026 01:23:44 +0530
Subject: [PATCH 10/12] Fix parser API usage: type.derived.t -> type.v.t
DeclarationTypeSpec::Type is a WRAPPER_CLASS with member 'v',
not 'derived'. Corrects build failure on CI.
---
flang/lib/Semantics/resolve-names.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 649d0a71dbfb6..7df3fe75c5584 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -6343,7 +6343,7 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Type &) {
}
void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Type &type) {
- const parser::Name &derivedName{std::get<parser::Name>(type.derived.t)};
+ const parser::Name &derivedName{std::get<parser::Name>(type.v.t)};
if (const Symbol *derivedSymbol{derivedName.symbol}) {
CheckForAbstractType(*derivedSymbol); // C706
}
>From b24861132f570fec3e6e48f76f08b9e69195a4fa Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Fri, 16 Jan 2026 21:16:02 +0530
Subject: [PATCH 11/12] Remove unrelated formatting changes from rebase
Per review feedback, removed all pointer-style and indentation
changes that were picked up during rebase. Kept only functional
changes for mapper support in target update.
---
flang/lib/Semantics/resolve-names.cpp | 99 ++++++++++++---------------
1 file changed, 45 insertions(+), 54 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 7df3fe75c5584..0d6aab1c1a48c 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -2407,7 +2407,7 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
}
symbol.SetBindName(std::move(*label));
if (!oldBindName.empty()) {
- if (const std::string *newBindName{symbol.GetBindName()}) {
+ if (const std::string * newBindName{symbol.GetBindName()}) {
if (oldBindName != *newBindName) {
Say(symbol.name(),
"The entity '%s' has multiple BIND names ('%s' and '%s')"_err_en_US,
@@ -2533,7 +2533,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
// expression semantics if the DeclTypeSpec is a valid TypeSpec.
// The grammar ensures that it's an intrinsic or derived type spec,
// not TYPE(*) or CLASS(*) or CLASS(T).
- if (const DeclTypeSpec *spec{state_.declTypeSpec}) {
+ if (const DeclTypeSpec * spec{state_.declTypeSpec}) {
switch (spec->category()) {
case DeclTypeSpec::Numeric:
case DeclTypeSpec::Logical:
@@ -2541,7 +2541,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
typeSpec.declTypeSpec = spec;
break;
case DeclTypeSpec::TypeDerived:
- if (const DerivedTypeSpec *derived{spec->AsDerived()}) {
+ if (const DerivedTypeSpec * derived{spec->AsDerived()}) {
CheckForAbstractType(derived->typeSymbol()); // C703
typeSpec.declTypeSpec = spec;
}
@@ -3132,8 +3132,8 @@ Symbol &ScopeHandler::MakeSymbol(const parser::Name &name, Attrs attrs) {
Symbol &ScopeHandler::MakeHostAssocSymbol(
const parser::Name &name, const Symbol &hostSymbol) {
Symbol &symbol{*NonDerivedTypeScope()
- .try_emplace(name.source, HostAssocDetails{hostSymbol})
- .first->second};
+ .try_emplace(name.source, HostAssocDetails{hostSymbol})
+ .first->second};
name.symbol = &symbol;
symbol.attrs() = hostSymbol.attrs(); // TODO: except PRIVATE, PUBLIC?
// These attributes can be redundantly reapplied without error
@@ -3221,7 +3221,7 @@ void ScopeHandler::ApplyImplicitRules(
if (context().HasError(symbol) || !NeedsType(symbol)) {
return;
}
- if (const DeclTypeSpec *type{GetImplicitType(symbol)}) {
+ if (const DeclTypeSpec * type{GetImplicitType(symbol)}) {
if (!skipImplicitTyping_) {
symbol.set(Symbol::Flag::Implicit);
symbol.SetType(*type);
@@ -3321,7 +3321,7 @@ const DeclTypeSpec *ScopeHandler::GetImplicitType(
const auto *type{implicitRulesMap_->at(scope).GetType(
symbol.name(), respectImplicitNoneType)};
if (type) {
- if (const DerivedTypeSpec *derived{type->AsDerived()}) {
+ if (const DerivedTypeSpec * derived{type->AsDerived()}) {
// Resolve any forward-referenced derived type; a quick no-op else.
auto &instantiatable{*const_cast<DerivedTypeSpec *>(derived)};
instantiatable.Instantiate(currScope());
@@ -4342,7 +4342,7 @@ Scope *ModuleVisitor::FindModule(const parser::Name &name,
if (scope) {
if (DoesScopeContain(scope, currScope())) { // 14.2.2(1)
std::optional<SourceName> submoduleName;
- if (const Scope *container{FindModuleOrSubmoduleContaining(currScope())};
+ if (const Scope * container{FindModuleOrSubmoduleContaining(currScope())};
container && container->IsSubmodule()) {
submoduleName = container->GetName();
}
@@ -4447,7 +4447,7 @@ bool InterfaceVisitor::isAbstract() const {
void InterfaceVisitor::AddSpecificProcs(
const std::list<parser::Name> &names, ProcedureKind kind) {
- if (Symbol *symbol{GetGenericInfo().symbol};
+ if (Symbol * symbol{GetGenericInfo().symbol};
symbol && symbol->has<GenericDetails>()) {
for (const auto &name : names) {
specificsForGenericProcs_.emplace(symbol, std::make_pair(&name, kind));
@@ -4547,7 +4547,7 @@ void GenericHandler::DeclaredPossibleSpecificProc(Symbol &proc) {
}
void InterfaceVisitor::ResolveNewSpecifics() {
- if (Symbol *generic{genericInfo_.top().symbol};
+ if (Symbol * generic{genericInfo_.top().symbol};
generic && generic->has<GenericDetails>()) {
ResolveSpecificsInGeneric(*generic, false);
}
@@ -4633,7 +4633,7 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) {
name.source);
MakeSymbol(name, Attrs{}, UnknownDetails{});
} else if (auto *entity{ultimate.detailsIf<EntityDetails>()};
- entity && !ultimate.has<ProcEntityDetails>()) {
+ entity && !ultimate.has<ProcEntityDetails>()) {
resultType = entity->type();
ultimate.details() = UnknownDetails{}; // will be replaced below
} else {
@@ -4692,7 +4692,7 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) {
} else {
Message &msg{Say(*suffix.resultName,
"RESULT(%s) may appear only in a function"_err_en_US)};
- if (const Symbol *subprogram{InclusiveScope().symbol()}) {
+ if (const Symbol * subprogram{InclusiveScope().symbol()}) {
msg.Attach(subprogram->name(), "Containing subprogram"_en_US);
}
}
@@ -5214,7 +5214,7 @@ Symbol *ScopeHandler::FindSeparateModuleProcedureInterface(
symbol = generic->specific();
}
}
- if (const Symbol *defnIface{FindSeparateModuleSubprogramInterface(symbol)}) {
+ if (const Symbol * defnIface{FindSeparateModuleSubprogramInterface(symbol)}) {
// Error recovery in case of multiple definitions
symbol = const_cast<Symbol *>(defnIface);
}
@@ -5353,8 +5353,8 @@ bool SubprogramVisitor::HandlePreviousCalls(
return generic->specific() &&
HandlePreviousCalls(name, *generic->specific(), subpFlag);
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}; proc &&
- !proc->isDummy() &&
- !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) {
+ !proc->isDummy() &&
+ !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) {
// There's a symbol created for previous calls to this subprogram or
// ENTRY's name. We have to replace that symbol in situ to avoid the
// obligation to rewrite symbol pointers in the parse tree.
@@ -5396,7 +5396,7 @@ const Symbol *SubprogramVisitor::CheckExtantProc(
if (prev) {
if (IsDummy(*prev)) {
} else if (auto *entity{prev->detailsIf<EntityDetails>()};
- IsPointer(*prev) && entity && !entity->type()) {
+ IsPointer(*prev) && entity && !entity->type()) {
// POINTER attribute set before interface
} else if (inInterfaceBlock() && currScope() != prev->owner()) {
// Procedures in an INTERFACE block do not resolve to symbols
@@ -5468,7 +5468,7 @@ Symbol *SubprogramVisitor::PushSubprogramScope(const parser::Name &name,
}
set_inheritFromParent(false); // interfaces don't inherit, even if MODULE
}
- if (Symbol *found{FindSymbol(name)};
+ if (Symbol * found{FindSymbol(name)};
found && found->has<HostAssocDetails>()) {
found->set(subpFlag); // PushScope() created symbol
}
@@ -6320,9 +6320,9 @@ void DeclarationVisitor::Post(const parser::VectorTypeSpec &x) {
vectorDerivedType.CookParameters(GetFoldingContext());
}
- if (const DeclTypeSpec *extant{
- ppcBuiltinTypesScope->FindInstantiatedDerivedType(
- vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) {
+ if (const DeclTypeSpec *
+ extant{ppcBuiltinTypesScope->FindInstantiatedDerivedType(
+ vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) {
// This derived type and parameter expressions (if any) are already present
// in the __ppc_intrinsics scope.
SetDeclTypeSpec(*extant);
@@ -6344,7 +6344,7 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Type &) {
void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Type &type) {
const parser::Name &derivedName{std::get<parser::Name>(type.v.t)};
- if (const Symbol *derivedSymbol{derivedName.symbol}) {
+ if (const Symbol * derivedSymbol{derivedName.symbol}) {
CheckForAbstractType(*derivedSymbol); // C706
}
}
@@ -6413,8 +6413,8 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
if (!spec->MightBeParameterized()) {
spec->EvaluateParameters(context());
}
- if (const DeclTypeSpec *extant{
- currScope().FindInstantiatedDerivedType(*spec, category)}) {
+ if (const DeclTypeSpec *
+ extant{currScope().FindInstantiatedDerivedType(*spec, category)}) {
// This derived type and parameter expressions (if any) are already present
// in this scope.
SetDeclTypeSpec(*extant);
@@ -6445,7 +6445,8 @@ void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Record &rec) {
if (auto spec{ResolveDerivedType(typeName)}) {
spec->CookParameters(GetFoldingContext());
spec->EvaluateParameters(context());
- if (const DeclTypeSpec *extant{currScope().FindInstantiatedDerivedType(
+ if (const DeclTypeSpec *
+ extant{currScope().FindInstantiatedDerivedType(
*spec, DeclTypeSpec::TypeDerived)}) {
SetDeclTypeSpec(*extant);
} else {
@@ -7459,7 +7460,7 @@ bool DeclarationVisitor::PassesLocalityChecks(
"Coarray '%s' not allowed in a %s locality-spec"_err_en_US, specName);
return false;
}
- if (const DeclTypeSpec *type{symbol.GetType()}) {
+ if (const DeclTypeSpec * type{symbol.GetType()}) {
if (type->IsPolymorphic() && IsDummy(symbol) && !IsPointer(symbol) &&
!isReduce) { // F'2023 C1130
SayWithDecl(name, symbol,
@@ -7693,7 +7694,7 @@ Symbol *DeclarationVisitor::NoteInterfaceName(const parser::Name &name) {
}
void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
- if (const Symbol *symbol{name.symbol}) {
+ if (const Symbol * symbol{name.symbol}) {
const Symbol &ultimate{symbol->GetUltimate()};
if (!context().HasError(*symbol) && !context().HasError(ultimate) &&
!BypassGeneric(ultimate).HasExplicitInterface()) {
@@ -8013,7 +8014,7 @@ bool ConstructVisitor::Pre(const parser::DataStmtValue &x) {
auto &mutableData{const_cast<parser::DataStmtConstant &>(data)};
if (auto *elem{parser::Unwrap<parser::ArrayElement>(mutableData)}) {
if (const auto *name{std::get_if<parser::Name>(&elem->base.u)}) {
- if (const Symbol *symbol{FindSymbol(*name)};
+ if (const Symbol * symbol{FindSymbol(*name)};
symbol && symbol->GetUltimate().has<DerivedTypeDetails>()) {
mutableData.u = elem->ConvertToStructureConstructor(
DerivedTypeSpec{name->source, *symbol});
@@ -8159,15 +8160,15 @@ void ConstructVisitor::Post(const parser::SelectTypeStmt &x) {
}
}
} else {
- if (const Symbol *whole{
- UnwrapWholeSymbolDataRef(association.selector.expr)}) {
+ if (const Symbol *
+ whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
ConvertToObjectEntity(const_cast<Symbol &>(*whole));
if (!IsVariableName(*whole)) {
Say(association.selector.source, // C901
"Selector is not a variable"_err_en_US);
association = {};
}
- if (const DeclTypeSpec *type{whole->GetType()}) {
+ if (const DeclTypeSpec * type{whole->GetType()}) {
if (!type->IsPolymorphic()) { // C1159
Say(association.selector.source,
"Selector '%s' in SELECT TYPE statement must be "
@@ -8307,8 +8308,8 @@ Symbol *ConstructVisitor::MakeAssocEntity() {
"The associate name '%s' is already used in this associate statement"_err_en_US);
return nullptr;
}
- } else if (const Symbol *whole{
- UnwrapWholeSymbolDataRef(association.selector.expr)}) {
+ } else if (const Symbol *
+ whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
symbol = &MakeSymbol(whole->name());
} else {
return nullptr;
@@ -8929,7 +8930,7 @@ bool DeclarationVisitor::CheckForHostAssociatedImplicit(
if (name.symbol) {
ApplyImplicitRules(*name.symbol, true);
}
- if (Scope *host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) {
+ if (Scope * host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) {
Symbol *hostSymbol{nullptr};
if (!name.symbol) {
if (currScope().CanImport(name.source)) {
@@ -9000,7 +9001,7 @@ const parser::Name *DeclarationVisitor::FindComponent(
if (!type) {
return nullptr; // should have already reported error
}
- if (const IntrinsicTypeSpec *intrinsic{type->AsIntrinsic()}) {
+ if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
auto category{intrinsic->category()};
MiscDetails::Kind miscKind{MiscDetails::Kind::None};
if (component.source == "kind") {
@@ -9022,7 +9023,7 @@ const parser::Name *DeclarationVisitor::FindComponent(
}
} else if (DerivedTypeSpec * derived{type->AsDerived()}) {
derived->Instantiate(currScope()); // in case of forward referenced type
- if (const Scope *scope{derived->scope()}) {
+ if (const Scope * scope{derived->scope()}) {
if (Resolve(component, scope->FindComponent(component.source))) {
if (auto msg{CheckAccessibleSymbol(currScope(), *component.symbol)}) {
context().Say(component.source, *msg);
@@ -9179,8 +9180,8 @@ void DeclarationVisitor::PointerInitialization(
if (evaluate::IsNullProcedurePointer(&*expr)) {
CHECK(!details->init());
details->set_init(nullptr);
- } else if (const Symbol *targetSymbol{
- evaluate::UnwrapWholeSymbolDataRef(*expr)}) {
+ } else if (const Symbol *
+ targetSymbol{evaluate::UnwrapWholeSymbolDataRef(*expr)}) {
CHECK(!details->init());
details->set_init(*targetSymbol);
} else {
@@ -9784,7 +9785,7 @@ void ResolveNamesVisitor::EarlyDummyTypeDeclaration(
for (const auto &ent : entities) {
const auto &objName{std::get<parser::ObjectName>(ent.t)};
Resolve(objName, FindInScope(currScope(), objName));
- if (Symbol *symbol{objName.symbol};
+ if (Symbol * symbol{objName.symbol};
symbol && IsDummy(*symbol) && NeedsType(*symbol)) {
if (!type) {
type = ProcessTypeSpec(declTypeSpec);
@@ -9895,9 +9896,6 @@ void ResolveNamesVisitor::FinishSpecificationPart(
misparsedStmtFuncFound_ = false;
funcResultStack().CompleteFunctionResultType();
CheckImports();
- if (inInterfaceBlock()) {
- FinishNamelists(); // NAMELIST is useless in an interface, but allowed
- }
for (auto &pair : currScope()) {
auto &symbol{*pair.second};
if (inInterfaceBlock()) {
@@ -9926,19 +9924,12 @@ void ResolveNamesVisitor::FinishSpecificationPart(
if (auto *proc{symbol.detailsIf<ProcEntityDetails>()}; proc &&
!proc->isDummy() && !IsPointer(symbol) &&
!symbol.attrs().test(Attr::BIND_C)) {
- if (const Symbol *iface{proc->procInterface()};
+ if (const Symbol * iface{proc->procInterface()};
iface && IsBindCProcedure(*iface)) {
SetImplicitAttr(symbol, Attr::BIND_C);
SetBindNameOn(symbol);
}
}
- // Implicitly treat allocatable arrays as managed when feature is enabled.
- // This is done after all explicit CUDA attributes have been processed.
- if (context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaManaged))
- if (auto *object{symbol.detailsIf<ObjectEntityDetails>()})
- if (IsAllocatable(symbol) && !object->cudaDataAttr())
- object->set_cudaDataAttr(common::CUDADataAttr::Managed);
}
currScope().InstantiateDerivedTypes();
for (const auto &decl : decls) {
@@ -10066,7 +10057,7 @@ bool ResolveNamesVisitor::Pre(const parser::PointerAssignmentStmt &x) {
Symbol *ptrSymbol{parser::GetLastName(dataRef).symbol};
Walk(bounds);
// Resolve unrestricted specific intrinsic procedures as in "p => cos".
- if (const parser::Name *name{parser::Unwrap<parser::Name>(expr)}) {
+ if (const parser::Name * name{parser::Unwrap<parser::Name>(expr)}) {
if (NameIsKnownOrIntrinsic(*name)) {
if (Symbol * symbol{name->symbol}) {
if (IsProcedurePointer(ptrSymbol) &&
@@ -10524,8 +10515,8 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
// implied SAVE so that evaluate::IsSaved() will return true.
if (node.scope()->kind() == Scope::Kind::MainProgram) {
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
- if (const DeclTypeSpec *type{object->type()}) {
- if (const DerivedTypeSpec *derived{type->AsDerived()}) {
+ if (const DeclTypeSpec * type{object->type()}) {
+ if (const DerivedTypeSpec * derived{type->AsDerived()}) {
if (!IsSaved(symbol) && FindCoarrayPotentialComponent(*derived)) {
SetImplicitAttr(symbol, Attr::SAVE);
}
@@ -10784,7 +10775,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) {
if (DerivedTypeSpec * spec{scope.derivedTypeSpec()}) {
spec->Instantiate(currScope());
const Symbol &origTypeSymbol{spec->typeSymbol()};
- if (const Scope *origTypeScope{origTypeSymbol.scope()}) {
+ if (const Scope * origTypeScope{origTypeSymbol.scope()}) {
CHECK(origTypeScope->IsDerivedType() &&
origTypeScope->symbol() == &origTypeSymbol);
auto &foldingContext{GetFoldingContext()};
@@ -10795,7 +10786,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) {
if (IsPointer(comp)) {
if (auto *details{comp.detailsIf<ObjectEntityDetails>()}) {
auto origDetails{origComp.get<ObjectEntityDetails>()};
- if (const MaybeExpr &init{origDetails.init()}) {
+ if (const MaybeExpr & init{origDetails.init()}) {
SomeExpr newInit{*init};
MaybeExpr folded{FoldExpr(std::move(newInit))};
details->set_init(std::move(folded));
>From 324654c2240d58bf229786668a2dceaa76ec5834 Mon Sep 17 00:00:00 2001
From: KrxGu <krishom70 at gmail.com>
Date: Tue, 27 Jan 2026 01:27:27 +0530
Subject: [PATCH 12/12] [Flang][OpenMP] Fix crash with character types in
declare_reduction
Fixes #177501
Character types in OpenMP reductions were causing crashes during lowering.
The issue was that character types need explicit handling for type parameters
and reference wrapping throughout the reduction lowering pipeline. This fix
ensures hlfir.declare operations get proper character length parameters and
that by-ref reductions wrap character types correctly.
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 26 +++++++++--
flang/lib/Lower/OpenMP/OpenMP.cpp | 21 +++++++--
.../Lower/Support/PrivateReductionUtils.cpp | 45 +++++++++++++++++--
.../lib/Lower/Support/ReductionProcessor.cpp | 5 +++
...eclare-reduction-character-allocatable.f90 | 34 ++++++++++++++
5 files changed, 119 insertions(+), 12 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/declare-reduction-character-allocatable.f90
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index a03f0cc73dc2b..cc32cf1363ebd 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -394,16 +394,34 @@ bool ClauseProcessor::processInitializer(
for (const Object &object :
std::get<StylizedInstance::Variables>(inst.t)) {
- mlir::Value addr = builder.createTemporary(loc, ompOrig.getType());
- fir::StoreOp::create(builder, loc, ompOrig, addr);
+ mlir::Value addr;
+ mlir::Type ompOrigType = ompOrig.getType();
+ // If ompOrig is already a reference, we can use it directly
+ if (fir::isa_ref_type(ompOrigType)) {
+ addr = ompOrig;
+ } else {
+ addr = builder.createTemporary(loc, ompOrigType);
+ fir::StoreOp::create(builder, loc, ompOrig, addr);
+ }
fir::FortranVariableFlagsEnum extraFlags = {};
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(
builder.getContext(), *object.sym(), extraFlags);
std::string name = object.sym()->name().ToString();
+ // For character types, we need to provide the length parameter
+ llvm::SmallVector<mlir::Value> typeParams;
+ mlir::Type unwrappedType = fir::unwrapRefType(ompOrigType);
+ if (auto charTy = mlir::dyn_cast<fir::CharacterType>(unwrappedType)) {
+ if (charTy.hasConstantLen()) {
+ mlir::Value lenValue = builder.createIntegerConstant(
+ loc, builder.getIndexType(), charTy.getLen());
+ typeParams.push_back(lenValue);
+ }
+ }
auto declareOp =
- hlfir::DeclareOp::create(builder, loc, addr, name, nullptr, {},
- nullptr, nullptr, 0, attributes);
+ hlfir::DeclareOp::create(builder, loc, addr, name, nullptr,
+ typeParams, nullptr, nullptr, 0,
+ attributes);
if (name == "omp_priv")
ompPrivVar = declareOp.getResult(0);
symMap.addVariableDefinition(*object.sym(), declareOp);
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 989e370870f33..f2ede8d0e5f69 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -3691,9 +3691,19 @@ static ReductionProcessor::GenCombinerCBTy processReductionCombiner(
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(builder.getContext(),
*object.sym(), extraFlags);
+ // For character types, we need to provide the length parameter
+ llvm::SmallVector<mlir::Value> typeParams;
+ mlir::Type unwrappedType = fir::unwrapRefType(type);
+ if (auto charTy = mlir::dyn_cast<fir::CharacterType>(unwrappedType)) {
+ if (charTy.hasConstantLen()) {
+ mlir::Value lenValue = builder.createIntegerConstant(
+ loc, builder.getIndexType(), charTy.getLen());
+ typeParams.push_back(lenValue);
+ }
+ }
auto declareOp =
- hlfir::DeclareOp::create(builder, loc, addr, name, nullptr, {},
- nullptr, nullptr, 0, attributes);
+ hlfir::DeclareOp::create(builder, loc, addr, name, nullptr,
+ typeParams, nullptr, nullptr, 0, attributes);
if (name == "omp_out")
ompOutVar = declareOp.getResult(0);
symTable.addVariableDefinition(*object.sym(), declareOp);
@@ -3714,9 +3724,12 @@ static ReductionProcessor::GenCombinerCBTy processReductionCombiner(
// Optional load may be generated if we get a reference to the
// reduction type.
if (auto refType =
- llvm::dyn_cast<fir::ReferenceType>(exprResult.getType()))
- if (lhs.getType() == refType.getElementType())
+ llvm::dyn_cast<fir::ReferenceType>(exprResult.getType())) {
+ mlir::Type expectedType =
+ isByRef ? fir::unwrapRefType(lhs.getType()) : lhs.getType();
+ if (expectedType == refType.getElementType())
exprResult = fir::LoadOp::create(builder, loc, exprResult);
+ }
return exprResult;
}},
evalExpr.u);
diff --git a/flang/lib/Lower/Support/PrivateReductionUtils.cpp b/flang/lib/Lower/Support/PrivateReductionUtils.cpp
index c6c428860bca1..617a4f4699459 100644
--- a/flang/lib/Lower/Support/PrivateReductionUtils.cpp
+++ b/flang/lib/Lower/Support/PrivateReductionUtils.cpp
@@ -89,6 +89,31 @@ static void createCleanupRegion(Fortran::lower::AbstractConverter &converter,
mlir::Value arg = builder.loadIfRef(loc, block->getArgument(0));
assert(mlir::isa<fir::BaseBoxType>(arg.getType()));
+ // Special handling for boxed character types - they need to use
+ // fir.box_addr instead of hlfir::genVariableRawAddress to avoid creating
+ // an hlfir::Entity from a value that doesn't satisfy isFortranEntity.
+ mlir::Type innerTy = fir::unwrapRefType(
+ mlir::cast<fir::BaseBoxType>(arg.getType()).getEleTy());
+ if (fir::isa_char(innerTy)) {
+ mlir::Value addr = fir::BoxAddrOp::create(builder, loc, arg);
+ mlir::Value isAllocated = builder.genIsNotNullAddr(loc, addr);
+ fir::IfOp ifOp = fir::IfOp::create(builder, loc, isAllocated,
+ /*withElseRegion=*/false);
+ builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+
+ mlir::Value cast = builder.createConvert(
+ loc, fir::HeapType::get(fir::dyn_cast_ptrEleTy(addr.getType())),
+ addr);
+ fir::FreeMemOp::create(builder, loc, cast);
+
+ builder.setInsertionPointAfter(ifOp);
+ if (isDoConcurrent)
+ fir::YieldOp::create(builder, loc);
+ else
+ mlir::omp::YieldOp::create(builder, loc);
+ return;
+ }
+
// Deallocate box
// The FIR type system doesn't nesecarrily know that this is a mutable box
// if we allocated the thread local array on the heap to avoid looped stack
@@ -620,7 +645,9 @@ void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() {
assert(sym && "Symbol information is required to privatize derived types");
assert(!scalarInitValue && "ScalarInitvalue is unused for privatization");
}
- if (hlfir::Entity{moldArg}.isAssumedRank())
+ // Only check for assumed rank if moldArg is a valid Fortran entity.
+ // Boxed types (like allocatable characters) may not be valid entities yet.
+ if (hlfir::isFortranEntity(moldArg) && hlfir::Entity{moldArg}.isAssumedRank())
TODO(loc, "Privatization of assumed rank variable");
mlir::Type valTy = fir::unwrapRefType(argType);
@@ -649,8 +676,9 @@ void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() {
bool isChar = fir::isa_char(innerTy);
if (fir::isa_trivial(innerTy) || isDerived || isChar) {
// boxed non-sequence value e.g. !fir.box<!fir.heap<i32>>
- if ((isDerived || isChar) && (isReduction(kind) || scalarInitValue))
- TODO(loc, "Reduction of an unsupported boxed type");
+ // Character types in reductions are supported, but derived types are not yet.
+ if (isDerived && (isReduction(kind) || scalarInitValue))
+ TODO(loc, "Reduction of an unsupported boxed derived type");
initAndCleanupBoxedScalar(boxTy, needsInitialization);
return;
}
@@ -663,10 +691,19 @@ void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() {
}
// Unboxed types:
- if (auto boxCharTy = mlir::dyn_cast<fir::BoxCharType>(argType)) {
+ if (auto boxCharTy = mlir::dyn_cast<fir::BoxCharType>(valTy)) {
initAndCleanupBoxchar(boxCharTy);
return;
}
+ // Handle unboxed character types (e.g., !fir.char<1,1>).
+ // For fixed-length character types, we just need to initialize the value.
+ if (fir::isa_char(valTy)) {
+ builder.setInsertionPointToEnd(initBlock);
+ if (scalarInitValue)
+ builder.createStoreWithConvert(loc, scalarInitValue, allocatedPrivVarArg);
+ createYield(allocatedPrivVarArg);
+ return;
+ }
if (fir::isa_derived(valType)) {
initAndCleanupUnboxedDerivedType(needsInitialization);
return;
diff --git a/flang/lib/Lower/Support/ReductionProcessor.cpp b/flang/lib/Lower/Support/ReductionProcessor.cpp
index db8ad909b1d2f..7cf92f8fe9eec 100644
--- a/flang/lib/Lower/Support/ReductionProcessor.cpp
+++ b/flang/lib/Lower/Support/ReductionProcessor.cpp
@@ -582,6 +582,11 @@ DeclareRedType ReductionProcessor::createDeclareReductionHelper(
if (isByRef) {
boxedTy = fir::unwrapPassByRefType(valTy);
boxedTyAttr = mlir::TypeAttr::get(boxedTy);
+ // For character types that are not already references, we need to wrap
+ // them in a reference type for by-ref reductions.
+ if (fir::isa_char(valTy) && !fir::isa_ref_type(type)) {
+ type = fir::ReferenceType::get(valTy);
+ }
} else
type = valTy;
diff --git a/flang/test/Lower/OpenMP/declare-reduction-character-allocatable.f90 b/flang/test/Lower/OpenMP/declare-reduction-character-allocatable.f90
new file mode 100644
index 0000000000000..ccc5669a19ee1
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-reduction-character-allocatable.f90
@@ -0,0 +1,34 @@
+! Test OpenMP declare reduction with character type and allocatable variable.
+! This test ensures that Flang doesn't crash when processing user-defined
+! reductions with character types on allocatable variables.
+! See issue: https://github.com/llvm/llvm-project/issues/177501
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+! Test basic character reduction with allocatable variable
+program test_character_reduction
+ character(len=1), allocatable :: k1
+
+ !$omp declare reduction (char_max:character(len=1):omp_out=max(omp_out,omp_in)) &
+ !$omp initializer(omp_priv='a')
+
+ !$omp parallel sections reduction(char_max:k1)
+ k1 = max(k1, 'z')
+ !$omp end parallel sections
+
+end program test_character_reduction
+
+! Verify the declare_reduction is generated with reference type for character
+! CHECK-LABEL: omp.declare_reduction @char_max : !fir.ref<!fir.char<1>>
+! CHECK-SAME: init
+! CHECK: omp.yield
+
+! Verify the combiner region works
+! CHECK: combiner
+! CHECK: hlfir.declare
+! CHECK: omp.yield
+
+! Verify the reduction is used in the parallel sections
+! CHECK: omp.parallel
+! CHECK: omp.sections reduction(byref @char_max
+
More information about the flang-commits
mailing list