[flang-commits] [flang] [flang] Add lowering tests for parenthesized initial values (PR #209664)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Wed Jul 15 07:00:16 PDT 2026


https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/209664

>From cd07f3ea09b09e85bee8441b46987ecb7cfb2e64 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 6 Jul 2026 00:22:50 -0400
Subject: [PATCH 1/2] [flang] Add lowering tests for parenthesized initial
 values

Add a LIT test that baselines the current lowering of parenthesized
initial values in fir.global initializer regions. Today a parenthesized
scalar or derived constant survives constant folding as a Parentheses
node and is lowered to a fir.no_reassoc operation inside the global init
region, while parentheses around characters, array named-constants, and
structure-constructor components are stripped before lowering.

This test pins that behavior so that an upcoming change to the
initializer-lowering path shows its behavior delta explicitly and has a
regression guard.
---
 .../Lower/global-initialization-parens.f90    | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 flang/test/Lower/global-initialization-parens.f90

diff --git a/flang/test/Lower/global-initialization-parens.f90 b/flang/test/Lower/global-initialization-parens.f90
new file mode 100644
index 0000000000000..ef137c0125979
--- /dev/null
+++ b/flang/test/Lower/global-initialization-parens.f90
@@ -0,0 +1,94 @@
+! Test lowering of parenthesized initial values in fir.global initializer
+! regions.
+!
+! This test baselines the *current* lowering behavior of parenthesized
+! initializers, which an upcoming change to the initializer-lowering path will
+! modify: today a parenthesized scalar or derived constant survives folding as
+! a Parentheses node and is lowered to a fir.no_reassoc operation inside the
+! global init region. The forms whose parentheses are stripped before lowering
+! (character, array named-constant, and parenthesized structure-constructor
+! components) are pinned here belt-and-braces so the follow-up change is shown
+! to leave them untouched.
+
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+
+module m
+  type t
+    integer :: n
+  end type t
+  type t2
+    ! Parenthesized component default initializer.
+    integer :: n = (5)
+  end type t2
+
+  integer :: i = (42)
+  real :: r = (3.5)
+  logical :: l = (.true.)
+  ! Double parentheses so the initializer exercises a Parentheses node rather
+  ! than plain complex-literal syntax.
+  complex :: z = ((1.0, 2.0))
+  ! Parenthesized structure constructor.
+  type(t) :: x = (t(7))
+  ! Default-initialized object exercising the parenthesized component default.
+  type(t2) :: w
+
+  ! Belt-and-braces: parentheses stripped before lowering.
+  character(2) :: c = ('ab')
+  integer, parameter :: iparm(2) = [1, 2]
+  integer :: a(2) = (iparm)
+  type(t) :: y = t((5))
+end module m
+
+! Globals are emitted in the order: a, c, i, iparm, l, r, w, x, y, z.
+
+! Parenthesized array named-constant: parentheses stripped, dense global.
+! CHECK: fir.global @_QMmEa(dense<[1, 2]> : tensor<2xi32>) {{.*}} : !fir.array<2xi32>
+
+! Character: parentheses stripped during initializer conversion, plain string.
+! CHECK-LABEL: fir.global @_QMmEc : !fir.char<1,2> {
+! CHECK:         %[[S:.*]] = fir.string_lit "ab"(2) : !fir.char<1,2>
+! CHECK-NOT:     fir.no_reassoc
+! CHECK:         fir.has_value %[[S]] : !fir.char<1,2>
+
+! CHECK-LABEL: fir.global @_QMmEi : i32 {
+! CHECK:         %[[C:.*]] = arith.constant 42 : i32
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i32
+! CHECK:         fir.has_value %[[NR]] : i32
+
+! CHECK-LABEL: fir.global @_QMmEl : !fir.logical<4> {
+! CHECK:         %[[C:.*]] = arith.constant true
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i1
+! CHECK:         %[[CV:.*]] = fir.convert %[[NR]] : (i1) -> !fir.logical<4>
+! CHECK:         fir.has_value %[[CV]] : !fir.logical<4>
+
+! CHECK-LABEL: fir.global @_QMmEr : f32 {
+! CHECK:         %[[C:.*]] = arith.constant 3.500000e+00 : f32
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : f32
+! CHECK:         fir.has_value %[[NR]] : f32
+
+! Parenthesized component default: the parentheses wrap the component value in a
+! fir.no_reassoc before it is inserted.
+! CHECK-LABEL: fir.global @_QMmEw : !fir.type<_QMmTt2{n:i32}> {
+! CHECK:         %[[C:.*]] = arith.constant 5 : i32
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i32
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %[[NR]], ["n", !fir.type<_QMmTt2{n:i32}>]
+! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMmTt2{n:i32}>
+
+! Parenthesized structure constructor: the parentheses survive folding and wrap
+! the insert_value chain in a fir.no_reassoc.
+! CHECK-LABEL: fir.global @_QMmEx : !fir.type<_QMmTt{n:i32}> {
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMmTt{n:i32}>]
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[IV]] : !fir.type<_QMmTt{n:i32}>
+! CHECK:         fir.has_value %[[NR]] : !fir.type<_QMmTt{n:i32}>
+
+! Parenthesized structure-constructor component: parentheses stripped, plain
+! insert_value chain with no fir.no_reassoc.
+! CHECK-LABEL: fir.global @_QMmEy : !fir.type<_QMmTt{n:i32}> {
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMmTt{n:i32}>]
+! CHECK-NOT:     fir.no_reassoc
+! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMmTt{n:i32}>
+
+! CHECK-LABEL: fir.global @_QMmEz : complex<f32> {
+! CHECK:         fir.insert_value
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %{{.*}} : complex<f32>
+! CHECK:         fir.has_value %[[NR]] : complex<f32>

>From 9bce60549f566ae41a44ea158d40b64ca074efa1 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 15 Jul 2026 08:25:57 -0400
Subject: [PATCH 2/2] [flang] Restructure parenthesized-init test; use
 -emit-hlfir

Split the single-module test into one subroutine per case, each with a
SAVE'd local so its fir.global is emitted in source order. That lets every
CHECK block sit directly after the Fortran source line it checks instead of
being collected at the bottom in symbol-sorted emission order.

Also switch the RUN line from -emit-fir to -emit-hlfir: the fir.no_reassoc
operations this test pins are emitted by both pipelines, so exercising the
default (HLFIR) lowering path is preferable.

Test-only; no functional change.
---
 .../Lower/global-initialization-parens.f90    | 135 ++++++++++--------
 1 file changed, 77 insertions(+), 58 deletions(-)

diff --git a/flang/test/Lower/global-initialization-parens.f90 b/flang/test/Lower/global-initialization-parens.f90
index ef137c0125979..6769ad5775a7e 100644
--- a/flang/test/Lower/global-initialization-parens.f90
+++ b/flang/test/Lower/global-initialization-parens.f90
@@ -9,10 +9,14 @@
 ! (character, array named-constant, and parenthesized structure-constructor
 ! components) are pinned here belt-and-braces so the follow-up change is shown
 ! to leave them untouched.
+!
+! Each case is a SAVE'd local in its own subroutine so that the fir.global for
+! it is emitted in source order, keeping every CHECK block next to the Fortran
+! it checks.
 
-! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
 
-module m
+module types
   type t
     integer :: n
   end type t
@@ -20,75 +24,90 @@ module m
     ! Parenthesized component default initializer.
     integer :: n = (5)
   end type t2
+end module types
 
-  integer :: i = (42)
-  real :: r = (3.5)
-  logical :: l = (.true.)
-  ! Double parentheses so the initializer exercises a Parentheses node rather
-  ! than plain complex-literal syntax.
-  complex :: z = ((1.0, 2.0))
-  ! Parenthesized structure constructor.
-  type(t) :: x = (t(7))
-  ! Default-initialized object exercising the parenthesized component default.
-  type(t2) :: w
-
-  ! Belt-and-braces: parentheses stripped before lowering.
-  character(2) :: c = ('ab')
-  integer, parameter :: iparm(2) = [1, 2]
-  integer :: a(2) = (iparm)
-  type(t) :: y = t((5))
-end module m
-
-! Globals are emitted in the order: a, c, i, iparm, l, r, w, x, y, z.
-
-! Parenthesized array named-constant: parentheses stripped, dense global.
-! CHECK: fir.global @_QMmEa(dense<[1, 2]> : tensor<2xi32>) {{.*}} : !fir.array<2xi32>
-
-! Character: parentheses stripped during initializer conversion, plain string.
-! CHECK-LABEL: fir.global @_QMmEc : !fir.char<1,2> {
-! CHECK:         %[[S:.*]] = fir.string_lit "ab"(2) : !fir.char<1,2>
-! CHECK-NOT:     fir.no_reassoc
-! CHECK:         fir.has_value %[[S]] : !fir.char<1,2>
-
-! CHECK-LABEL: fir.global @_QMmEi : i32 {
+subroutine scalar_int()
+  integer, save :: i = (42)
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFscalar_intEi : i32 {
 ! CHECK:         %[[C:.*]] = arith.constant 42 : i32
 ! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i32
 ! CHECK:         fir.has_value %[[NR]] : i32
 
-! CHECK-LABEL: fir.global @_QMmEl : !fir.logical<4> {
+subroutine scalar_real()
+  real, save :: r = (3.5)
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFscalar_realEr : f32 {
+! CHECK:         %[[C:.*]] = arith.constant 3.500000e+00 : f32
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : f32
+! CHECK:         fir.has_value %[[NR]] : f32
+
+subroutine scalar_logical()
+  logical, save :: l = (.true.)
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFscalar_logicalEl : !fir.logical<4> {
 ! CHECK:         %[[C:.*]] = arith.constant true
 ! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i1
 ! CHECK:         %[[CV:.*]] = fir.convert %[[NR]] : (i1) -> !fir.logical<4>
 ! CHECK:         fir.has_value %[[CV]] : !fir.logical<4>
 
-! CHECK-LABEL: fir.global @_QMmEr : f32 {
-! CHECK:         %[[C:.*]] = arith.constant 3.500000e+00 : f32
-! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : f32
-! CHECK:         fir.has_value %[[NR]] : f32
+subroutine scalar_complex()
+  ! Double parentheses so the initializer exercises a Parentheses node rather
+  ! than plain complex-literal syntax.
+  complex, save :: z = ((1.0, 2.0))
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFscalar_complexEz : complex<f32> {
+! CHECK:         fir.insert_value
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %{{.*}} : complex<f32>
+! CHECK:         fir.has_value %[[NR]] : complex<f32>
 
-! Parenthesized component default: the parentheses wrap the component value in a
-! fir.no_reassoc before it is inserted.
-! CHECK-LABEL: fir.global @_QMmEw : !fir.type<_QMmTt2{n:i32}> {
+subroutine paren_ctor()
+  use types
+  ! Parenthesized structure constructor: the parentheses survive folding and
+  ! wrap the insert_value chain in a fir.no_reassoc.
+  type(t), save :: x = (t(7))
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFparen_ctorEx : !fir.type<_QMtypesTt{n:i32}> {
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMtypesTt{n:i32}>]
+! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[IV]] : !fir.type<_QMtypesTt{n:i32}>
+! CHECK:         fir.has_value %[[NR]] : !fir.type<_QMtypesTt{n:i32}>
+
+subroutine comp_default()
+  use types
+  ! Default-initialized object exercising the parenthesized component default:
+  ! the parentheses wrap the component value in a fir.no_reassoc before it is
+  ! inserted.
+  type(t2), save :: w
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFcomp_defaultEw : !fir.type<_QMtypesTt2{n:i32}> {
 ! CHECK:         %[[C:.*]] = arith.constant 5 : i32
 ! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[C]] : i32
-! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %[[NR]], ["n", !fir.type<_QMmTt2{n:i32}>]
-! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMmTt2{n:i32}>
-
-! Parenthesized structure constructor: the parentheses survive folding and wrap
-! the insert_value chain in a fir.no_reassoc.
-! CHECK-LABEL: fir.global @_QMmEx : !fir.type<_QMmTt{n:i32}> {
-! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMmTt{n:i32}>]
-! CHECK:         %[[NR:.*]] = fir.no_reassoc %[[IV]] : !fir.type<_QMmTt{n:i32}>
-! CHECK:         fir.has_value %[[NR]] : !fir.type<_QMmTt{n:i32}>
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %[[NR]], ["n", !fir.type<_QMtypesTt2{n:i32}>]
+! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMtypesTt2{n:i32}>
 
-! Parenthesized structure-constructor component: parentheses stripped, plain
-! insert_value chain with no fir.no_reassoc.
-! CHECK-LABEL: fir.global @_QMmEy : !fir.type<_QMmTt{n:i32}> {
-! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMmTt{n:i32}>]
+subroutine char_paren()
+  ! Belt-and-braces: parentheses stripped before lowering.
+  character(2), save :: c = ('ab')
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFchar_parenEc : !fir.char<1,2> {
+! CHECK:         %[[S:.*]] = fir.string_lit "ab"(2) : !fir.char<1,2>
 ! CHECK-NOT:     fir.no_reassoc
-! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMmTt{n:i32}>
+! CHECK:         fir.has_value %[[S]] : !fir.char<1,2>
 
-! CHECK-LABEL: fir.global @_QMmEz : complex<f32> {
-! CHECK:         fir.insert_value
-! CHECK:         %[[NR:.*]] = fir.no_reassoc %{{.*}} : complex<f32>
-! CHECK:         fir.has_value %[[NR]] : complex<f32>
+subroutine array_named_const()
+  ! Parenthesized array named-constant: parentheses stripped, dense global.
+  integer, parameter :: iparm(2) = [1, 2]
+  integer, save :: a(2) = (iparm)
+end subroutine
+! CHECK: fir.global internal @_QFarray_named_constEa(dense<[1, 2]> : tensor<2xi32>) {{.*}} : !fir.array<2xi32>
+
+subroutine paren_ctor_comp()
+  use types
+  ! Parenthesized structure-constructor component: parentheses stripped, plain
+  ! insert_value chain with no fir.no_reassoc.
+  type(t), save :: y = t((5))
+end subroutine
+! CHECK-LABEL: fir.global internal @_QFparen_ctor_compEy : !fir.type<_QMtypesTt{n:i32}> {
+! CHECK:         %[[IV:.*]] = fir.insert_value %{{.*}}, %{{.*}}, ["n", !fir.type<_QMtypesTt{n:i32}>]
+! CHECK-NOT:     fir.no_reassoc
+! CHECK:         fir.has_value %[[IV]] : !fir.type<_QMtypesTt{n:i32}>



More information about the flang-commits mailing list