[flang-commits] [flang] [Flang][OpenMP] Fix interop-var handling and diagnostics (#203959) (PR #211693)
Sunil Shrestha via flang-commits
flang-commits at lists.llvm.org
Fri Jul 24 15:48:27 PDT 2026
https://github.com/sshrestha-aa updated https://github.com/llvm/llvm-project/pull/211693
>From f818eac2a16bdf3725f2f17b07755948a955fcee Mon Sep 17 00:00:00 2001
From: Sunil Shrestha <sunil.shrestha at hpe.com>
Date: Thu, 23 Jul 2026 12:23:24 -0500
Subject: [PATCH 1/2] [Flang][OpenMP] Fix interop-var handling and diagnostics
(#203959)
This change builds on top of the work done in PR #203959
- Diagnose interop destroy without an interop variable
- Lower array-element / derived-component interop-vars via their designator
- Require interop-var to be a scalar integer of omp_interop_kind
- Remove "hsa" prefer_type (no omp_ifr_hsa in the runtime)
- Fix a latent bug in Parser/OpenMP/interop-construct.f90
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 12 +++-
flang/lib/Semantics/check-omp-structure.cpp | 56 ++++++++++++++++
flang/test/Lower/OpenMP/interop.f90 | 27 ++++++++
.../test/Parser/OpenMP/interop-construct.f90 | 6 +-
flang/test/Semantics/OpenMP/init-clause.f90 | 1 +
.../Semantics/OpenMP/interop-construct.f90 | 65 +++++++++++++++++++
6 files changed, 162 insertions(+), 5 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 387b83793dd1b..ae6039c253732 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -6316,8 +6316,17 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
}
}
- // Helper to get the address of an interop variable from an Object.
+ // Helper to get the address of an interop variable from an Object. A
+ // designator such as arr(1) or rec%obj must lower through genExprAddr so we
+ // obtain the address of the actual scalar element/component with the correct
+ // type, rather than the base symbol address (which would be the whole array,
+ // or null for a component defined inside a derived type).
auto getInteropVarAddr = [&](const Object &object) -> mlir::Value {
+ if (const auto &designator = object.ref()) {
+ fir::ExtendedValue exv =
+ converter.genExprAddr(*designator, stmtCtx, &loc);
+ return fir::getBase(exv);
+ }
const semantics::Symbol *sym = object.sym();
assert(sym && "interop variable must have a symbol");
mlir::Value addr = converter.getSymbolAddress(*sym);
@@ -6376,7 +6385,6 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
.Case("sycl", 4)
.Case("hip", 5)
.Case("level_zero", 6)
- .Case("hsa", 7)
.Default(std::nullopt);
if (frId)
prefValues.push_back(*frId);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 4e29a1c387ec2..b40909fab76aa 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5978,6 +5978,48 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
bool hasInitClause{false};
int targetCount{0}, targetSyncCount{0};
std::set<const Symbol *> objectSymbolList;
+ // An interop-var must be a scalar variable of integer type. Reject
+ // non-scalar designators (whole arrays, array sections) and non-integer
+ // designators; array elements and scalar derived-type components are valid.
+ auto checkInteropVar = [&](const parser::OmpObject &object,
+ parser::CharBlock source) {
+ // Type-parameter inquiries (e.g. x%kind) are diagnosed separately by
+ // CheckTypeParamInquiry, so don't also flag them here.
+ if (const Symbol *sym{GetObjectSymbol(object)};
+ sym && IsTypeParamInquiry(*sym)) {
+ return;
+ }
+ const parser::Designator *designator{GetDesignatorFromObj(object)};
+ if (!designator) {
+ return;
+ }
+ // Analyze the designator only to inspect its rank and type. Permit a
+ // whole assumed-size array so the analyzer does not add its own "may not
+ // appear here" error on top of the interop-specific diagnostic below,
+ // keeping a single clean message for any non-scalar interop-var. This
+ // mirrors other OMP-object checks here.
+ evaluate::ExpressionAnalyzer ea{context_};
+ auto restore{ea.AllowWholeAssumedSizeArray(true)};
+ MaybeExpr expr{ea.Analyze(*designator)};
+ if (!expr) {
+ return;
+ }
+ if (expr->Rank() != 0) {
+ context_.Say(source,
+ "The interop variable in an INTEROP construct must be a scalar variable"_err_en_US);
+ return;
+ }
+ // The interop-var must be a scalar integer of omp_interop_kind
+ // (c_intptr_t): the runtime uses its storage as a pointer-sized handle.
+ std::optional<evaluate::DynamicType> type{expr->GetType()};
+ int interopKind{static_cast<int>(
+ context_.targetCharacteristics().integerKindForPointer())};
+ if (!type || type->category() != evaluate::TypeCategory::Integer ||
+ type->kind() != interopKind) {
+ context_.Say(source,
+ "The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind"_err_en_US);
+ }
+ };
const auto &clauseList{std::get<std::optional<parser::OmpClauseList>>(x.v.t)};
for (const auto &clause : clauseList->v) {
common::visit(
@@ -6043,6 +6085,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
std::get<parser::OmpObject>(initClause.v.t))};
CheckTypeParamInquiry(
clause.source, *interopVar, llvm::omp::Clause::OMPC_init);
+ if (interopVar) {
+ checkInteropVar(*interopVar, clause.source);
+ }
if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
const auto *objectSymbol{name->symbol};
if (llvm::is_contained(objectSymbolList, objectSymbol)) {
@@ -6057,8 +6102,16 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
isDependClauseOccurred = true;
},
[&](const parser::OmpClause::Destroy &destroyClause) {
+ if (!destroyClause.v) {
+ context_.Say(GetContext().directiveSource,
+ "The DESTROY clause on an INTEROP construct must specify an interop variable"_err_en_US);
+ return;
+ }
const auto *interopVar{
parser::Unwrap<parser::OmpObject>(destroyClause.v)};
+ if (interopVar) {
+ checkInteropVar(*interopVar, clause.source);
+ }
if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
const auto *objectSymbol{name->symbol};
if (llvm::is_contained(objectSymbolList, objectSymbol)) {
@@ -6074,6 +6127,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
parser::Unwrap<parser::OmpObject>(useClause.v)};
CheckTypeParamInquiry(
clause.source, *interopVar, llvm::omp::Clause::OMPC_use);
+ if (interopVar) {
+ checkInteropVar(*interopVar, clause.source);
+ }
if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
const auto *objectSymbol{name->symbol};
if (llvm::is_contained(objectSymbolList, objectSymbol)) {
diff --git a/flang/test/Lower/OpenMP/interop.f90 b/flang/test/Lower/OpenMP/interop.f90
index b20332bef6893..1fd9b353b535f 100644
--- a/flang/test/Lower/OpenMP/interop.f90
+++ b/flang/test/Lower/OpenMP/interop.f90
@@ -173,3 +173,30 @@ subroutine test_interop_destroy_device(obj, dev)
integer :: dev
!$omp interop destroy(obj) device(dev)
end subroutine
+
+!===============================================================================
+! Interop Init — array element interop-var
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init_array_element(
+!CHECK: %[[EL:.*]] = hlfir.designate %{{.*}} (%{{.*}}) : (!fir.ref<!fir.array<10xi64>>, index) -> !fir.ref<i64>
+!CHECK: omp.interop.init %[[EL]] : !fir.ref<i64> interop_types([#omp<interop_type(target)>])
+subroutine test_interop_init_array_element(arr)
+ integer(8) :: arr(10)
+ !$omp interop init(target: arr(1))
+end subroutine
+
+!===============================================================================
+! Interop Use — derived-type component interop-var
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_use_component(
+!CHECK: %[[COMP:.*]] = hlfir.designate %{{.*}}{"obj"} : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<i64>
+!CHECK: omp.interop.use %[[COMP]] : !fir.ref<i64>
+subroutine test_interop_use_component(rec)
+ type t
+ integer(8) :: obj
+ end type
+ type(t) :: rec
+ !$omp interop use(rec%obj)
+end subroutine
diff --git a/flang/test/Parser/OpenMP/interop-construct.f90 b/flang/test/Parser/OpenMP/interop-construct.f90
index c46d15cff8082..a5ae197f3c534 100644
--- a/flang/test/Parser/OpenMP/interop-construct.f90
+++ b/flang/test/Parser/OpenMP/interop-construct.f90
@@ -22,14 +22,14 @@ END SUBROUTINE test_interop_01
SUBROUTINE test_interop_02()
USE omp_lib
INTEGER(OMP_INTEROP_KIND) :: obj1, obj2, obj3
- !$OMP INTEROP INIT(TARGETSYNC: obj) USE(obj1) DESTROY(obj3)
+ !$OMP INTEROP INIT(TARGETSYNC: obj2) USE(obj1) DESTROY(obj3)
PRINT *,'pass'
END SUBROUTINE test_interop_02
!UNPARSE: SUBROUTINE test_interop_02
!UNPARSE: USE :: omp_lib
!UNPARSE: INTEGER(KIND=8_4) obj1, obj2, obj3
-!UNPARSE: !$OMP INTEROP INIT(TARGETSYNC: obj) USE(obj1) DESTROY(obj3)
+!UNPARSE: !$OMP INTEROP INIT(TARGETSYNC: obj2) USE(obj1) DESTROY(obj3)
!UNPARSE: PRINT *, "pass"
!UNPARSE: END SUBROUTINE test_interop_02
@@ -37,7 +37,7 @@ END SUBROUTINE test_interop_02
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interop
!PARSE-TREE: | OmpClauseList -> OmpClause -> Init -> OmpInitClause
!PARSE-TREE: | | Modifier -> OmpInteropType -> Value = Targetsync
-!PARSE-TREE: | | OmpObject -> Designator -> DataRef -> Name = 'obj'
+!PARSE-TREE: | | OmpObject -> Designator -> DataRef -> Name = 'obj2'
!PARSE-TREE: | OmpClause -> Use -> OmpUseClause -> OmpObject -> Designator -> DataRef -> Name = 'obj1'
!PARSE-TREE: | OmpClause -> Destroy -> OmpDestroyClause -> OmpObject -> Designator -> DataRef -> Name = 'obj3'
!PARSE-TREE: | Flags = {}
diff --git a/flang/test/Semantics/OpenMP/init-clause.f90 b/flang/test/Semantics/OpenMP/init-clause.f90
index e47b0af7d0aa6..714cd9faed880 100644
--- a/flang/test/Semantics/OpenMP/init-clause.f90
+++ b/flang/test/Semantics/OpenMP/init-clause.f90
@@ -24,6 +24,7 @@ subroutine f02
subroutine f03
integer :: x, y
+ !ERROR: The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind
!ERROR: The 'depinfo-modifier' is not allowed on INTEROP construct
!$omp interop init(mutexinoutset(x): y)
end
diff --git a/flang/test/Semantics/OpenMP/interop-construct.f90 b/flang/test/Semantics/OpenMP/interop-construct.f90
index bc32bd80c22a6..987f59536c9fe 100644
--- a/flang/test/Semantics/OpenMP/interop-construct.f90
+++ b/flang/test/Semantics/OpenMP/interop-construct.f90
@@ -28,3 +28,68 @@ SUBROUTINE test_interop_03()
!$OMP INTEROP INIT(TARGET: obj) DEPEND(INOUT: obj)
PRINT *, 'pass'
END SUBROUTINE test_interop_03
+
+SUBROUTINE test_interop_04()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ !$OMP INTEROP INIT(TARGETSYNC: obj)
+ !ERROR: The DESTROY clause on an INTEROP construct must specify an interop variable
+ !$OMP INTEROP DESTROY
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_04
+
+SUBROUTINE test_interop_05()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !ERROR: The interop variable in an INTEROP construct must be a scalar variable
+ !$OMP INTEROP INIT(TARGET: arr)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_05
+
+SUBROUTINE test_interop_06()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !ERROR: The interop variable in an INTEROP construct must be a scalar variable
+ !$OMP INTEROP USE(arr(1:5))
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_06
+
+SUBROUTINE test_interop_07()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !ERROR: The interop variable in an INTEROP construct must be a scalar variable
+ !$OMP INTEROP DESTROY(arr)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_07
+
+SUBROUTINE test_interop_08()
+ REAL(8) :: x
+ !ERROR: The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind
+ !$OMP INTEROP INIT(TARGET: x)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_08
+
+SUBROUTINE test_interop_09()
+ INTEGER(4) :: obj
+ !ERROR: The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind
+ !$OMP INTEROP USE(obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_09
+
+SUBROUTINE test_interop_10()
+ INTEGER(4) :: obj
+ !ERROR: The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind
+ !$OMP INTEROP DESTROY(obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_10
+
+! An array element is a valid interop-var (scalar, of omp_interop_kind) and
+! must be accepted on all action clauses.
+SUBROUTINE test_interop_11()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !$OMP INTEROP INIT(TARGET: arr(1))
+ !$OMP INTEROP USE(arr(1))
+ !$OMP INTEROP DESTROY(arr(1))
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_11
>From 51728e6482aa8ff18ec4f6305c011bbe2aa5c402 Mon Sep 17 00:00:00 2001
From: Sunil Shrestha <sunil.shrestha at hpe.com>
Date: Fri, 24 Jul 2026 15:15:50 -0500
Subject: [PATCH 2/2] Address reviewers feedback
---
flang/lib/Semantics/check-omp-structure.cpp | 66 ++++++++++---------
.../Semantics/OpenMP/interop-construct.f90 | 56 ++++++++++++++++
2 files changed, 90 insertions(+), 32 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index b40909fab76aa..17375d4fdd99e 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5977,12 +5977,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
bool isDependClauseOccurred{false};
bool hasInitClause{false};
int targetCount{0}, targetSyncCount{0};
- std::set<const Symbol *> objectSymbolList;
+ std::set<std::string> interopVarNames;
// An interop-var must be a scalar variable of integer type. Reject
// non-scalar designators (whole arrays, array sections) and non-integer
// designators; array elements and scalar derived-type components are valid.
auto checkInteropVar = [&](const parser::OmpObject &object,
- parser::CharBlock source) {
+ parser::CharBlock source, bool requireDefinable) {
// Type-parameter inquiries (e.g. x%kind) are diagnosed separately by
// CheckTypeParamInquiry, so don't also flag them here.
if (const Symbol *sym{GetObjectSymbol(object)};
@@ -6018,6 +6018,32 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
type->kind() != interopKind) {
context_.Say(source,
"The interop variable in an INTEROP construct must be a scalar integer variable of kind omp_interop_kind"_err_en_US);
+ return;
+ }
+ // An interop-var must be a variable (never a constant). For init and
+ // destroy the runtime stores the handle through the interop-var, so it
+ // must additionally be definable (not an INTENT(IN) dummy, PROTECTED,
+ // etc.). A use clause only reads the handle, so a non-definable variable
+ // (e.g. an INTENT(IN) dummy holding an initialized handle) is acceptable.
+ if (requireDefinable) {
+ if (auto msg{WhyNotDefinable(source, context_.FindScope(source),
+ DefinabilityFlags{}, *expr)}) {
+ context_
+ .Say(source,
+ "The interop variable in an INTEROP construct must be a definable variable"_err_en_US)
+ .Attach(std::move(msg->set_severity(parser::Severity::Because)));
+ }
+ } else if (!evaluate::IsVariable(*expr)) {
+ context_.Say(source,
+ "The interop variable in an INTEROP construct must be a variable"_err_en_US);
+ }
+ // Each interop-var may appear in at most one action-clause. Compare the
+ // full designator in canonical form so distinct array elements or
+ // structure components (e.g. arr(1) vs arr(2)) are allowed, while a
+ // repeated designator is flagged as a duplicate.
+ if (!interopVarNames.insert(expr->AsFortran()).second) {
+ context_.Say(source,
+ "Each interop-var may be specified for at most one action-clause of each INTEROP construct."_err_en_US);
}
};
const auto &clauseList{std::get<std::optional<parser::OmpClauseList>>(x.v.t)};
@@ -6086,16 +6112,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
CheckTypeParamInquiry(
clause.source, *interopVar, llvm::omp::Clause::OMPC_init);
if (interopVar) {
- checkInteropVar(*interopVar, clause.source);
- }
- if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
- const auto *objectSymbol{name->symbol};
- if (llvm::is_contained(objectSymbolList, objectSymbol)) {
- context_.Say(GetContext().directiveSource,
- "Each interop-var may be specified for at most one action-clause of each INTEROP construct."_err_en_US);
- } else {
- objectSymbolList.insert(objectSymbol);
- }
+ checkInteropVar(*interopVar, clause.source,
+ /*requireDefinable=*/true);
}
},
[&](const parser::OmpClause::Depend &dependClause) {
@@ -6110,16 +6128,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
const auto *interopVar{
parser::Unwrap<parser::OmpObject>(destroyClause.v)};
if (interopVar) {
- checkInteropVar(*interopVar, clause.source);
- }
- if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
- const auto *objectSymbol{name->symbol};
- if (llvm::is_contained(objectSymbolList, objectSymbol)) {
- context_.Say(GetContext().directiveSource,
- "Each interop-var may be specified for at most one action-clause of each INTEROP construct."_err_en_US);
- } else {
- objectSymbolList.insert(objectSymbol);
- }
+ checkInteropVar(*interopVar, clause.source,
+ /*requireDefinable=*/true);
}
},
[&](const parser::OmpClause::Use &useClause) {
@@ -6128,16 +6138,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
CheckTypeParamInquiry(
clause.source, *interopVar, llvm::omp::Clause::OMPC_use);
if (interopVar) {
- checkInteropVar(*interopVar, clause.source);
- }
- if (const auto *name{parser::Unwrap<parser::Name>(interopVar)}) {
- const auto *objectSymbol{name->symbol};
- if (llvm::is_contained(objectSymbolList, objectSymbol)) {
- context_.Say(GetContext().directiveSource,
- "Each interop-var may be specified for at most one action-clause of each INTEROP construct."_err_en_US);
- } else {
- objectSymbolList.insert(objectSymbol);
- }
+ checkInteropVar(*interopVar, clause.source,
+ /*requireDefinable=*/false);
}
},
[&](const auto &) {},
diff --git a/flang/test/Semantics/OpenMP/interop-construct.f90 b/flang/test/Semantics/OpenMP/interop-construct.f90
index 987f59536c9fe..2083c62b7b2eb 100644
--- a/flang/test/Semantics/OpenMP/interop-construct.f90
+++ b/flang/test/Semantics/OpenMP/interop-construct.f90
@@ -93,3 +93,59 @@ SUBROUTINE test_interop_11()
!$OMP INTEROP DESTROY(arr(1))
PRINT *, 'pass'
END SUBROUTINE test_interop_11
+
+! Uniqueness is compared per complete designator: distinct array elements or
+! structure components are different interop-vars and must be accepted.
+SUBROUTINE test_interop_12()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !$OMP INTEROP INIT(TARGETSYNC: arr(1)) USE(arr(2))
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_12
+
+! Repeating the same designator in two action-clauses is a duplicate.
+SUBROUTINE test_interop_13()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: arr(10)
+ !ERROR: Each interop-var may be specified for at most one action-clause of each INTEROP construct.
+ !$OMP INTEROP INIT(TARGETSYNC: arr(1)) USE(arr(1))
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_13
+
+! init and destroy store the new handle through the interop-var, so it must be
+! a definable variable, not a constant or other non-definable entity.
+SUBROUTINE test_interop_14()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND), PARAMETER :: handle = 0
+ !ERROR: The interop variable in an INTEROP construct must be a definable variable
+ !BECAUSE: 'handle' is not a variable
+ !$OMP INTEROP INIT(TARGETSYNC: handle)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_14
+
+SUBROUTINE test_interop_15(obj)
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND), INTENT(IN) :: obj
+ !ERROR: The interop variable in an INTEROP construct must be a definable variable
+ !BECAUSE: 'obj' is an INTENT(IN) dummy argument
+ !$OMP INTEROP DESTROY(obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_15
+
+! A use clause only reads the handle, so it does not require a definable
+! variable, but the interop-var must still be a variable (not a constant).
+SUBROUTINE test_interop_16()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND), PARAMETER :: handle = 0
+ !ERROR: The interop variable in an INTEROP construct must be a variable
+ !$OMP INTEROP USE(handle)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_16
+
+! An INTENT(IN) dummy holding an initialized handle is a valid use interop-var.
+SUBROUTINE test_interop_17(obj)
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND), INTENT(IN) :: obj
+ !$OMP INTEROP USE(obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_17
More information about the flang-commits
mailing list