[flang-commits] [flang] [flang][cuda] Reject DEVICE derived types with PINNED allocatable components (PR #220411)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Wed Sep 2 12:58:38 PDT 2026
https://github.com/clementval updated https://github.com/llvm/llvm-project/pull/220411
>From b3ca922599b143594f57db23fe7a3b4eb546c1c7 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Tue, 1 Sep 2026 15:42:45 -0700
Subject: [PATCH 1/2] [flang][cuda] Reject allocation of PINNED components of
DEVICE objects
PINNED allocatables are allocated in host page-locked memory, so their
descriptors must be host accessible. The descriptors of a DEVICE object
live in device global memory, so allocating a PINNED allocatable
component of such an object is not valid.
Diagnose this in the ALLOCATE statement, where the component actually
gets allocated, rather than at the declaration of the DEVICE object.
Declaring a DEVICE object of a type with a PINNED allocatable component
is fine as long as that component is never allocated, and the check also
covers cases the declaration check could not see, such as allocating
through a chain of intermediate allocatable components. Host and MANAGED
objects of the same type remain allowed.
---
flang/lib/Semantics/check-allocate.cpp | 15 +++++++
flang/test/Semantics/CUDA/cuf32.cuf | 58 ++++++++++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 flang/test/Semantics/CUDA/cuf32.cuf
diff --git a/flang/lib/Semantics/check-allocate.cpp b/flang/lib/Semantics/check-allocate.cpp
index 2ac242abd5788..c5c5ddaa6e7dc 100644
--- a/flang/lib/Semantics/check-allocate.cpp
+++ b/flang/lib/Semantics/check-allocate.cpp
@@ -715,6 +715,21 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
"Object in ALLOCATE must have DEVICE attribute when STREAM option is specified"_err_en_US);
}
}
+ if (const auto *component{
+ std::get_if<parser::StructureComponent>(&allocateObject_.u)};
+ component && GetCUDADataAttr(ultimate_) == common::CUDADataAttr::Pinned) {
+ // A PINNED allocatable is allocated in host page-locked memory, so its
+ // descriptor must be host accessible. The descriptors of a DEVICE object
+ // live in device global memory.
+ const parser::Name &base{parser::GetFirstName(*component)};
+ if (base.symbol &&
+ GetCUDADataAttr(&base.symbol->GetUltimate()) ==
+ common::CUDADataAttr::Device) {
+ context.Say(name_.source,
+ "PINNED allocatable component '%s' must not be allocated in DEVICE object '%s'"_err_en_US,
+ name_.source, base.source);
+ }
+ }
if (const SomeExpr *allocObj{GetExpr(context, allocateObject_)}) {
if (AreSameAllocation(allocObj, allocateInfo_.statVar)) {
diff --git a/flang/test/Semantics/CUDA/cuf32.cuf b/flang/test/Semantics/CUDA/cuf32.cuf
new file mode 100644
index 0000000000000..2b8eb2a34b9d2
--- /dev/null
+++ b/flang/test/Semantics/CUDA/cuf32.cuf
@@ -0,0 +1,58 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1
+! PINNED allocatables must live in host page-locked memory, so a PINNED
+! allocatable component of a DEVICE object may not be allocated.
+module m
+ type :: t
+ integer(4), allocatable, pinned :: pc(:)
+ end type
+ type :: inner_alloc
+ type(t), allocatable :: x
+ end type
+
+ type(t), device :: mds(10)
+
+contains
+ subroutine test_device_derived_pinned()
+ type(t), allocatable, device :: d(:)
+ type(t), device :: ds(10)
+ type(t), allocatable :: h(:)
+ type(t), allocatable, managed :: md(:)
+ type(inner_alloc), allocatable, device :: nest(:)
+ integer :: n, i
+ n = 2
+ i = 1
+
+ allocate(h(n))
+ allocate(h(i)%pc(3))
+ h(i)%pc = 10
+
+ allocate(md(n))
+ allocate(md(i)%pc(3))
+
+ allocate(d(n))
+ !ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'd'
+ allocate(d(i)%pc(3))
+
+ !ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'ds'
+ allocate(ds(i)%pc(3))
+
+ !ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'mds'
+ allocate(mds(i)%pc(3))
+
+ allocate(nest(n))
+ allocate(nest(i)%x)
+ !ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'nest'
+ allocate(nest(i)%x%pc(3))
+ end subroutine
+
+ subroutine dummy_ok(h)
+ type(t), intent(inout) :: h(:)
+ allocate(h(1)%pc(3))
+ end subroutine
+
+ subroutine dummy_device(d)
+ type(t), device :: d(:)
+ !ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'd'
+ allocate(d(1)%pc(3))
+ end subroutine
+end module
>From 11a68caed3dc72a30be8fa60fd78ae070f4a4961 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Wed, 2 Sep 2026 11:52:01 -0700
Subject: [PATCH 2/2] Update check
---
flang/lib/Semantics/check-allocate.cpp | 34 ++++++++----
flang/test/Semantics/CUDA/cuf32.cuf | 73 ++++++++++++++++++++++++--
2 files changed, 92 insertions(+), 15 deletions(-)
diff --git a/flang/lib/Semantics/check-allocate.cpp b/flang/lib/Semantics/check-allocate.cpp
index c5c5ddaa6e7dc..4bbc652f6f9cd 100644
--- a/flang/lib/Semantics/check-allocate.cpp
+++ b/flang/lib/Semantics/check-allocate.cpp
@@ -12,6 +12,7 @@
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/shape.h"
#include "flang/Evaluate/type.h"
+#include "flang/Parser/characters.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/tools.h"
#include "flang/Semantics/attr.h"
@@ -716,18 +717,29 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
}
}
if (const auto *component{
- std::get_if<parser::StructureComponent>(&allocateObject_.u)};
- component && GetCUDADataAttr(ultimate_) == common::CUDADataAttr::Pinned) {
- // A PINNED allocatable is allocated in host page-locked memory, so its
- // descriptor must be host accessible. The descriptors of a DEVICE object
- // live in device global memory.
+ std::get_if<parser::StructureComponent>(&allocateObject_.u)}) {
+ // The descriptors of a DEVICE object live in device global memory, so a
+ // component of one can only be allocated where they are addressable.
+ const auto *details{ultimate_->detailsIf<ObjectEntityDetails>()};
+ std::optional<common::CUDADataAttr> attr{
+ details ? details->cudaDataAttr() : std::nullopt};
const parser::Name &base{parser::GetFirstName(*component)};
- if (base.symbol &&
- GetCUDADataAttr(&base.symbol->GetUltimate()) ==
- common::CUDADataAttr::Device) {
- context.Say(name_.source,
- "PINNED allocatable component '%s' must not be allocated in DEVICE object '%s'"_err_en_US,
- name_.source, base.source);
+ if (attr && base.symbol && IsCUDADevice(*base.symbol)) {
+ if (*attr == common::CUDADataAttr::Pinned ||
+ *attr == common::CUDADataAttr::Managed ||
+ *attr == common::CUDADataAttr::Unified) {
+ // These allocatables are placed in host-accessible memory, so their
+ // descriptors have to be host accessible too.
+ context.Say(name_.source,
+ "%s allocatable component '%s' must not be allocated in DEVICE object '%s'"_err_en_US,
+ parser::ToUpperCaseLetters(common::EnumToString(*attr)),
+ name_.source, base.source);
+ } else if (*attr == common::CUDADataAttr::Device &&
+ !FindCUDADeviceContext(&context.FindScope(name_.source))) {
+ context.Say(name_.source,
+ "DEVICE allocatable component '%s' of DEVICE object '%s' may only be allocated in a device subprogram"_err_en_US,
+ name_.source, base.source);
+ }
}
}
diff --git a/flang/test/Semantics/CUDA/cuf32.cuf b/flang/test/Semantics/CUDA/cuf32.cuf
index 2b8eb2a34b9d2..a012b2e71c476 100644
--- a/flang/test/Semantics/CUDA/cuf32.cuf
+++ b/flang/test/Semantics/CUDA/cuf32.cuf
@@ -1,18 +1,25 @@
! RUN: %python %S/../test_errors.py %s %flang_fc1
-! PINNED allocatables must live in host page-locked memory, so a PINNED
-! allocatable component of a DEVICE object may not be allocated.
+! The descriptors of a DEVICE object live in device global memory, so a
+! component of one can only be allocated where they are addressable.
module m
type :: t
integer(4), allocatable, pinned :: pc(:)
+ integer(4), allocatable, managed :: mc(:)
+ integer(4), allocatable, unified :: uc(:)
end type
type :: inner_alloc
type(t), allocatable :: x
end type
+ type :: td
+ integer(4), allocatable, device :: dc(:)
+ end type
type(t), device :: mds(10)
contains
- subroutine test_device_derived_pinned()
+ ! PINNED, MANAGED, and UNIFIED allocatables are placed in host-accessible
+ ! memory, so such a component of a DEVICE object may never be allocated.
+ subroutine test_device_derived_host_mem_components()
type(t), allocatable, device :: d(:)
type(t), device :: ds(10)
type(t), allocatable :: h(:)
@@ -24,35 +31,93 @@ contains
allocate(h(n))
allocate(h(i)%pc(3))
+ allocate(h(i)%mc(3))
+ allocate(h(i)%uc(3))
h(i)%pc = 10
allocate(md(n))
allocate(md(i)%pc(3))
+ allocate(md(i)%mc(3))
+ allocate(md(i)%uc(3))
allocate(d(n))
!ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'd'
allocate(d(i)%pc(3))
+ !ERROR: MANAGED allocatable component 'mc' must not be allocated in DEVICE object 'd'
+ allocate(d(i)%mc(3))
+ !ERROR: UNIFIED allocatable component 'uc' must not be allocated in DEVICE object 'd'
+ allocate(d(i)%uc(3))
!ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'ds'
allocate(ds(i)%pc(3))
+ !ERROR: MANAGED allocatable component 'mc' must not be allocated in DEVICE object 'ds'
+ allocate(ds(i)%mc(3))
+ !ERROR: UNIFIED allocatable component 'uc' must not be allocated in DEVICE object 'ds'
+ allocate(ds(i)%uc(3))
!ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'mds'
allocate(mds(i)%pc(3))
+ !ERROR: MANAGED allocatable component 'mc' must not be allocated in DEVICE object 'mds'
+ allocate(mds(i)%mc(3))
+ !ERROR: UNIFIED allocatable component 'uc' must not be allocated in DEVICE object 'mds'
+ allocate(mds(i)%uc(3))
allocate(nest(n))
allocate(nest(i)%x)
!ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'nest'
allocate(nest(i)%x%pc(3))
+ !ERROR: MANAGED allocatable component 'mc' must not be allocated in DEVICE object 'nest'
+ allocate(nest(i)%x%mc(3))
+ !ERROR: UNIFIED allocatable component 'uc' must not be allocated in DEVICE object 'nest'
+ allocate(nest(i)%x%uc(3))
end subroutine
- subroutine dummy_ok(h)
+ subroutine host_ok(h)
type(t), intent(inout) :: h(:)
allocate(h(1)%pc(3))
+ allocate(h(1)%mc(3))
+ allocate(h(1)%uc(3))
end subroutine
subroutine dummy_device(d)
type(t), device :: d(:)
!ERROR: PINNED allocatable component 'pc' must not be allocated in DEVICE object 'd'
allocate(d(1)%pc(3))
+ !ERROR: MANAGED allocatable component 'mc' must not be allocated in DEVICE object 'd'
+ allocate(d(1)%mc(3))
+ !ERROR: UNIFIED allocatable component 'uc' must not be allocated in DEVICE object 'd'
+ allocate(d(1)%uc(3))
+ end subroutine
+
+ ! A DEVICE component of a DEVICE object may only be allocated from device
+ ! code, where the device-resident descriptor can be updated.
+ subroutine test_device_derived_device_component()
+ type(td), device :: dd(10)
+ type(td), allocatable, device :: ad(:)
+ type(td) :: h
+ type(td), allocatable, managed :: md(:)
+ integer :: i
+ i = 1
+
+ ! Allocating a DEVICE component of a host or MANAGED object is fine.
+ allocate(h%dc(3))
+ allocate(md(2))
+ allocate(md(i)%dc(3))
+
+ !ERROR: DEVICE allocatable component 'dc' of DEVICE object 'dd' may only be allocated in a device subprogram
+ allocate(dd(i)%dc(3))
+ allocate(ad(2))
+ !ERROR: DEVICE allocatable component 'dc' of DEVICE object 'ad' may only be allocated in a device subprogram
+ allocate(ad(i)%dc(3))
+ end subroutine
+
+ attributes(global) subroutine global_ok(dd)
+ type(td), device :: dd(:)
+ allocate(dd(1)%dc(3))
+ end subroutine
+
+ attributes(device) subroutine device_ok(dd)
+ type(td), device :: dd(:)
+ allocate(dd(1)%dc(3))
end subroutine
end module
More information about the flang-commits
mailing list