[flang-commits] [flang] [flang][HLFIR] Preserve RHS lower bounds in SeparateAllocatableAssign (PR #202373)

via flang-commits flang-commits at lists.llvm.org
Mon Jun 8 08:52:02 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: khaki3

<details>
<summary>Changes</summary>

Example:
```fortran
type t
  integer, allocatable :: a(:)
end type
integer, pointer :: p(:)
allocate(p(-5:-3))
x = t(p)                  ! allocatable component <- source, lbound -5
print *, lbound(x%a, 1)   ! should be -5
```

In this code, the structure constructor assigns the allocatable component from a source with a non-default lower bound, so `lbound(x%a,1)` must be `-5`. Lowering wraps the source in an `hlfir.expr` and emits `hlfir.assign ... realloc`. `SeparateAllocatableAssign` split the realloc but assumed an `hlfir.expr` RHS always has lower bound `1`, reallocating the component with `1` instead of `-5`.

Fix: read the reallocation lower bounds from the entity that owns the RHS descriptor — the variable RHS itself, or the variable wrapped by `hlfir.as_expr`. Only truly synthetic exprs (elemental, array constructor) keep lower bound `1`.

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


2 Files Affected:

- (modified) flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp (+17-7) 
- (modified) flang/test/HLFIR/separate-allocatable-assign.fir (+32) 


``````````diff
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp
index 0160ff7d75f76..b4391f9069d48 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp
@@ -115,14 +115,24 @@ class SeparateAllocatableAssignConversion
     llvm::SmallVector<mlir::Value> rhsExtents =
         hlfir::getIndexExtents(loc, builder, rhsShape);
 
-    // F2018 10.2.1.3: when the LHS is (re-)allocated, its lower bounds
-    // come from LBOUND(rhs).  For variable RHS, extract the actual lower
-    // bounds from the entity; for hlfir.expr RHS, LBOUND is always 1.
+    // The re-allocated LHS takes the RHS lower bounds, which lowering has
+    // already put in the RHS descriptor. Read them from the entity that owns
+    // that descriptor: a variable RHS itself, or the variable wrapped by
+    // hlfir.as_expr. Other hlfir.exprs have no descriptor, so lower bound is 1.
+    mlir::Value lboundSource;
+    if (!mlir::isa<hlfir::ExprType>(rhs.getType()))
+      lboundSource = assign.getRhs();
+    else if (auto asExpr = assign.getRhs().getDefiningOp<hlfir::AsExprOp>())
+      lboundSource = asExpr.getVar();
+
     llvm::SmallVector<mlir::Value> rhsLbounds;
-    if (!mlir::isa<hlfir::ExprType>(rhs.getType())) {
-      auto bounds = hlfir::genBounds(loc, builder, rhs);
-      for (auto &[lb, ub] : bounds)
-        rhsLbounds.push_back(lb);
+    if (lboundSource) {
+      hlfir::Entity boundsEntity{lboundSource};
+      if (boundsEntity.isArray()) {
+        auto bounds = hlfir::genBounds(loc, builder, boundsEntity);
+        for (auto &[lb, ub] : bounds)
+          rhsLbounds.push_back(lb);
+      }
     }
 
     fir::MutableBoxValue mutableBox(lhs.getFirBase(), /*lenParameters=*/{},
diff --git a/flang/test/HLFIR/separate-allocatable-assign.fir b/flang/test/HLFIR/separate-allocatable-assign.fir
index 97c664c38a94f..53ae8594a1d98 100644
--- a/flang/test/HLFIR/separate-allocatable-assign.fir
+++ b/flang/test/HLFIR/separate-allocatable-assign.fir
@@ -179,3 +179,35 @@ func.func @test_lower_bounds(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32
 // Lower bound 10 should appear in the embox/store of the new allocation.
 // CHECK: %[[BOX:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
 // CHECK: hlfir.assign %{{.*}} to %[[BOX]] : !fir.box<!fir.array<3xi32>>, !fir.box<!fir.heap<!fir.array<?xi32>>>
+
+// Test: lower bounds from an hlfir.expr RHS that wraps a variable (via
+// hlfir.as_expr) must be preserved during reallocation.  This is the
+// structure-constructor case `t = x(f())` where `f` is an allocatable function
+// result with a non-default lower bound: the allocatable component must take
+// the source's bounds.  The pass must read the lower bound from the wrapped
+// variable's descriptor instead of assuming 1.
+func.func @test_expr_lower_bounds(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.array<3xi32>>) {
+  %c10 = arith.constant 10 : index
+  %c3 = arith.constant 3 : index
+  %true = arith.constant true
+  %shapeshift = fir.shape_shift %c10, %c3 : (index, index) -> !fir.shapeshift<1>
+
+  %dest:2 = hlfir.declare %arg0 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEdest"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  %source:2 = hlfir.declare %arg1(%shapeshift) {uniq_name = "_QFEsource"} : (!fir.ref<!fir.array<3xi32>>, !fir.shapeshift<1>) -> (!fir.box<!fir.array<3xi32>>, !fir.ref<!fir.array<3xi32>>)
+  %expr = hlfir.as_expr %source#0 move %true : (!fir.box<!fir.array<3xi32>>, i1) -> !hlfir.expr<3xi32>
+
+  hlfir.assign %expr to %dest#0 realloc temporary_lhs : !hlfir.expr<3xi32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  hlfir.destroy %expr : !hlfir.expr<3xi32>
+  return
+}
+
+// CHECK-LABEL: func.func @test_expr_lower_bounds
+// The realloc should be separated with the wrapped variable's lower bound 10.
+// CHECK-NOT: hlfir.assign{{.*}}realloc
+// CHECK: %[[C10:.*]] = arith.constant 10 : index
+// CHECK: fir.if
+// CHECK: fir.allocmem
+// Lower bound 10 (not 1) must appear in the embox/store of the new allocation.
+// CHECK: fir.shape_shift %[[C10]]
+// CHECK: %[[BOX:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+// CHECK: hlfir.assign %{{.*}} to %[[BOX]] temporary_lhs : !hlfir.expr<3xi32>, !fir.box<!fir.heap<!fir.array<?xi32>>>

``````````

</details>


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


More information about the flang-commits mailing list