[flang-commits] [flang] b15f932 - [flang] Add kind-preservation regression tests (#208760)

via flang-commits flang-commits at lists.llvm.org
Mon Jul 13 20:56:39 PDT 2026


Author: Eugene Epshteyn
Date: 2026-07-13T23:56:34-04:00
New Revision: b15f9326d21cbd05463fcb34d989bae95982cc9b

URL: https://github.com/llvm/llvm-project/commit/b15f9326d21cbd05463fcb34d989bae95982cc9b
DIFF: https://github.com/llvm/llvm-project/commit/b15f9326d21cbd05463fcb34d989bae95982cc9b.diff

LOG: [flang] Add kind-preservation regression tests (#208760)

These tests pin currently-correct behaviors where the KIND type
parameter of a result must be preserved by constant folding, semantics,
and lowering. They all pass at head; their purpose is to guard against
silent kind loss during future refactoring of the Evaluate library's
type-parameterized machinery (see the discussion on PR #206907, where an
experimental rewrite passed check-flang while dropping kinds in several
places).

Folded LOGICAL operations: `.NOT./.AND./.OR./.EQV./.NEQV.` keep their
operand kind; ANY/ALL/PARITY and logical DOT_PRODUCT results have the
MASK/argument kind (F2023 16.9.14/16.9.16/16.9.148), including for empty
masks where there is no element value to take a kind from; MERGE and
TRANSFER keep the TSOURCE/MOLD kind; STORAGE_SIZE of folded
non-default-kind logicals folds to the right value; the default BOUNDARY
fill element of a folded EOSHIFT has the array's kind; relational
results are default logical regardless of operand kinds; and generic
resolution over LOGICAL kinds is unchanged by folding
(flang/test/Evaluate/fold-kind-logical.f90 and an extension of
flang/test/Evaluate/logical-args.f90).

Empty typed array constructors and zero-size PACK/SPREAD/RESHAPE/UNPACK
results keep the kind (and character length) of their type-spec (F2023
7.8), including across a module file into a separately compiled
consumer, and empty non-default-kind character array constructors lower
with the correct `!fir.char` element type
(flang/test/Evaluate/fold-kind-zero-size.f90,
flang/test/Semantics/modfile84.F90,
flang/test/Lower/HLFIR/array-ctor-character-kind.f90).

A substring of a character literal has the kind of the literal (F2023
9.4.1): KIND() and LEN() fold correctly, and concatenation with a
same-kind literal is accepted
(flang/test/Evaluate/fold-kind-substring.f90,
flang/test/Semantics/substring-literal-kind.f90).

Assisted-by: AI

Added: 
    flang/test/Evaluate/fold-kind-logical.f90
    flang/test/Evaluate/fold-kind-substring.f90
    flang/test/Evaluate/fold-kind-zero-size.f90
    flang/test/Lower/HLFIR/array-ctor-character-kind.f90
    flang/test/Semantics/modfile84.F90
    flang/test/Semantics/substring-literal-kind.f90

Modified: 
    flang/test/Evaluate/logical-args.f90

Removed: 
    


################################################################################
diff  --git a/flang/test/Evaluate/fold-kind-logical.f90 b/flang/test/Evaluate/fold-kind-logical.f90
new file mode 100644
index 0000000000000..91ea9bdeb5b4e
--- /dev/null
+++ b/flang/test/Evaluate/fold-kind-logical.f90
@@ -0,0 +1,43 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Regression tests: constant folding must preserve non-default LOGICAL kinds.
+module m
+  ! Unary and binary logical operations have the kind of their operands
+  ! (F2023 10.1.5.4.2); use same-kind operands only, since the kind of a
+  ! mixed-kind logical operation is processor dependent.
+  logical, parameter :: test_not1 = kind(.not. .true._1) == 1
+  logical, parameter :: test_not2 = kind(.not. .true._2) == 2
+  logical, parameter :: test_not8 = kind(.not. .true._8) == 8
+  logical, parameter :: test_and8 = kind(.true._8 .and. .false._8) == 8
+  logical, parameter :: test_or2 = kind(.true._2 .or. .false._2) == 2
+  logical, parameter :: test_eqv8 = kind(.true._8 .eqv. .true._8) == 8
+  logical, parameter :: test_neqv1 = kind(.true._1 .neqv. .false._1) == 1
+  ! STORAGE_SIZE of a folded non-default-kind logical operation.
+  logical, parameter :: test_ss8 = storage_size(.not. .true._8) == 64
+  logical, parameter :: test_ss2 = storage_size(.true._2 .and. .true._2) == 16
+  ! ALL/ANY/PARITY results have the same type and kind type parameters as
+  ! MASK (F2023 16.9.14, 16.9.16, 16.9.148), including for empty masks,
+  ! where there is no element value to take a kind from.
+  logical, parameter :: test_any8 = kind(any([.true._8])) == 8
+  logical, parameter :: test_all2 = kind(all([.false._2])) == 2
+  logical, parameter :: test_parity8 = kind(parity([.true._8, .false._8])) == 8
+  logical, parameter :: test_parity1 = kind(parity([.true._1])) == 1
+  logical, parameter :: test_anyempty8 = kind(any([logical(8) ::])) == 8
+  logical, parameter :: test_allempty2 = kind(all([logical(2) ::])) == 2
+  ! DOT_PRODUCT of logical arrays is ANY(X.AND.Y) (F2023 16.9.79), so its
+  ! kind is that of the operands.
+  logical, parameter :: test_dot8 = kind(dot_product([.true._8], [.true._8])) == 8
+  logical, parameter :: test_dot2 = kind(dot_product([.true._2], [.false._2])) == 2
+  ! MERGE and TRANSFER results keep the kind of TSOURCE/MOLD.
+  logical, parameter :: test_merge8 = kind(merge(.true._8, .false._8, .true.)) == 8
+  logical, parameter :: test_transfer8 = kind(transfer(.true._8, .false._8)) == 8
+  ! A relational operation yields default logical regardless of the kinds
+  ! of its operands (F2023 10.1.5.5.1); 4 is the default logical kind under
+  ! default compilation options.
+  logical, parameter :: test_rel2 = kind(1_2 == 2_2) == 4
+  ! The default BOUNDARY fill element of a folded EOSHIFT must have the
+  ! array's kind. Probe the extracted fill element: a wrong-kind element
+  ! can hide inside a constant whose overall type is correct.
+  logical(8), parameter :: eo8(2) = eoshift([.true._8, .true._8], 1)
+  logical, parameter :: test_eo8kind = storage_size(eo8(2)) == 64
+  logical, parameter :: test_eo8value = eo8(1) .and. .not. eo8(2)
+end module

diff  --git a/flang/test/Evaluate/fold-kind-substring.f90 b/flang/test/Evaluate/fold-kind-substring.f90
new file mode 100644
index 0000000000000..056d8dd5adb53
--- /dev/null
+++ b/flang/test/Evaluate/fold-kind-substring.f90
@@ -0,0 +1,14 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Regression tests: a substring of a character literal has the kind of the
+! literal (F2023 9.4.1), and its length folds from the substring bounds.
+! Only type inquiries are asserted here: KIND() and LEN() do not require
+! their argument to be a constant, and the value of a substring of a
+! non-default-kind literal does not currently fold.
+module m
+  logical, parameter :: test_k4 = kind(4_"abcd"(2:3)) == 4
+  logical, parameter :: test_k2 = kind(2_"ab"(1:1)) == 2
+  logical, parameter :: test_k1 = kind(1_"ab"(1:1)) == 1
+  logical, parameter :: test_l4 = len(4_"abcd"(2:3)) == 2
+  logical, parameter :: test_l4a = len(4_"abcd"(:3)) == 3
+  logical, parameter :: test_l4b = len(4_"abcd"(2:)) == 3
+end module

diff  --git a/flang/test/Evaluate/fold-kind-zero-size.f90 b/flang/test/Evaluate/fold-kind-zero-size.f90
new file mode 100644
index 0000000000000..046c196fdf27a
--- /dev/null
+++ b/flang/test/Evaluate/fold-kind-zero-size.f90
@@ -0,0 +1,28 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Regression tests: empty typed array constructors and zero-size
+! transformational results must preserve the kind (and character length)
+! of their type. An array constructor with a type-spec has the type and
+! type parameters of the type-spec (F2023 7.8), even when it has no
+! element values to take them from.
+module m
+  ! Empty typed array constructors, each intrinsic category.
+  logical, parameter :: test_ec4 = kind([character(kind=4, len=0) ::]) == 4
+  logical, parameter :: test_ec2 = kind([character(kind=2, len=0) ::]) == 2
+  logical, parameter :: test_ec4len = len([character(kind=4, len=5) ::]) == 5
+  logical, parameter :: test_ec4size = size([character(kind=4, len=0) ::]) == 0
+  logical, parameter :: test_ei2 = kind([integer(2) ::]) == 2
+  logical, parameter :: test_er2 = kind([real(2) ::]) == 2
+  logical, parameter :: test_el8 = kind([logical(8) ::]) == 8
+  ! Empty typed implied-do array constructor.
+  logical, parameter :: test_eido = kind([integer(1) :: (int(i, 1), i = 1, 0)]) == 1
+  logical, parameter :: test_eidosize = size([integer(1) :: (int(i, 1), i = 1, 0)]) == 0
+  ! Zero-size transformational results keep the source's kind.
+  logical, parameter :: test_pack2 = kind(pack([1_2, 2_2], [.false., .false.])) == 2
+  logical, parameter :: test_pack2size = size(pack([1_2, 2_2], [.false., .false.])) == 0
+  logical, parameter :: test_pack4c = kind(pack([4_"ab"], [.false.])) == 4
+  logical, parameter :: test_pack4clen = len(pack([4_"ab"], [.false.])) == 2
+  logical, parameter :: test_spread2 = kind(spread(1_2, 1, 0)) == 2
+  logical, parameter :: test_reshape8 = kind(reshape([integer(8) ::], [0])) == 8
+  logical, parameter :: test_reshape2r = kind(reshape([real(2) ::], [0])) == 2
+  logical, parameter :: test_unpack2 = kind(unpack([integer(2) ::], [logical ::], 0_2)) == 2
+end module

diff  --git a/flang/test/Evaluate/logical-args.f90 b/flang/test/Evaluate/logical-args.f90
index 04153016e174e..b324b101802c4 100644
--- a/flang/test/Evaluate/logical-args.f90
+++ b/flang/test/Evaluate/logical-args.f90
@@ -41,6 +41,19 @@ program main
   ! CHECK-8: CALL foo8(logical(x(1_8)>y,kind=8))
   call foog(x(1) > y)
 
+  ! Folded actual arguments with explicit non-default kinds must keep their
+  ! kind through generic resolution.
+  ! CHECK: CALL foo8(.false._8)
+  ! CHECK-8: CALL foo8(.false._8)
+  call foog(.not. .true._8)
+  ! CHECK: CALL foo8(.true._8)
+  ! CHECK-8: CALL foo8(.true._8)
+  call foog(.true._8 .and. .true._8)
+  ! A relational result is default logical regardless of its operand kinds.
+  ! CHECK: CALL foo4(.false._4)
+  ! CHECK-8: CALL foo8(.false._8)
+  call foog(1_2 == 2_2)
+
  contains
   subroutine foo(l)
     logical :: l

diff  --git a/flang/test/Lower/HLFIR/array-ctor-character-kind.f90 b/flang/test/Lower/HLFIR/array-ctor-character-kind.f90
new file mode 100644
index 0000000000000..aa8c1c1a71f1f
--- /dev/null
+++ b/flang/test/Lower/HLFIR/array-ctor-character-kind.f90
@@ -0,0 +1,32 @@
+! Test lowering of empty character array constructors with non-default
+! kinds: the element type must carry the constructor's character kind
+! (!fir.char<4>/!fir.char<2>), not default !fir.char<1>.
+! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_empty_char4(
+! CHECK: fir.address_of(@_QQro.0x0xc4.null{{.*}}) : !fir.ref<!fir.array<0x!fir.char<4,0>>>
+! CHECK: hlfir.assign %{{.*}} to %{{.*}} realloc : !fir.ref<!fir.array<0x!fir.char<4,0>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<4,?>>>>>
+subroutine test_empty_char4(c)
+  character(kind=4, len=:), allocatable :: c(:)
+  c = [character(kind=4, len=0) ::]
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_empty_char4_len3(
+! CHECK: fir.address_of(@_QQro.0x3xc4.null{{.*}}) : !fir.ref<!fir.array<0x!fir.char<4,3>>>
+! CHECK: hlfir.assign %{{.*}} to %{{.*}} realloc : !fir.ref<!fir.array<0x!fir.char<4,3>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<4,?>>>>>
+subroutine test_empty_char4_len3(c)
+  character(kind=4, len=:), allocatable :: c(:)
+  c = [character(kind=4, len=3) ::]
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_empty_char2(
+! CHECK: fir.address_of(@_QQro.0x0xc2.null{{.*}}) : !fir.ref<!fir.array<0x!fir.char<2,0>>>
+! CHECK: hlfir.assign %{{.*}} to %{{.*}} realloc : !fir.ref<!fir.array<0x!fir.char<2,0>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<2,?>>>>>
+subroutine test_empty_char2(c)
+  character(kind=2, len=:), allocatable :: c(:)
+  c = [character(kind=2, len=0) ::]
+end subroutine
+
+! CHECK-DAG: fir.global internal @_QQro.0x0xc4.null{{.*}} constant : !fir.array<0x!fir.char<4,0>>
+! CHECK-DAG: fir.global internal @_QQro.0x3xc4.null{{.*}} constant : !fir.array<0x!fir.char<4,3>>
+! CHECK-DAG: fir.global internal @_QQro.0x0xc2.null{{.*}} constant : !fir.array<0x!fir.char<2,0>>

diff  --git a/flang/test/Semantics/modfile84.F90 b/flang/test/Semantics/modfile84.F90
new file mode 100644
index 0000000000000..03ba31304240e
--- /dev/null
+++ b/flang/test/Semantics/modfile84.F90
@@ -0,0 +1,29 @@
+!RUN: rm -rf %t && mkdir -p %t
+!RUN: %flang_fc1 -fsyntax-only -DWHICH=1 -J%t %s
+!RUN: %flang_fc1 -fdebug-unparse -DWHICH=2 -J%t %s | FileCheck %s
+! Empty typed named-constant arrays must keep their kind (and character
+! length) across a module file into a separately compiled consumer.
+
+#if WHICH == 1
+module modfile84
+  character(kind=4, len=0), parameter :: ec4(*) = [character(kind=4, len=0) ::]
+  character(kind=2, len=5), parameter :: ec2(*) = [character(kind=2, len=5) ::]
+  integer(2), parameter :: ei2(*) = [integer(2) ::]
+  logical(8), parameter :: el8(*) = [logical(8) ::]
+  real(2), parameter :: er2(*) = [real(2) ::]
+end
+#else
+program test
+  use modfile84
+  !CHECK: PRINT *, 4_4, 0_4, 0_8
+  print *, kind(ec4), len(ec4), size(ec4, kind=8)
+  !CHECK: PRINT *, 2_4, 5_4
+  print *, kind(ec2), len(ec2)
+  !CHECK: PRINT *, 2_4
+  print *, kind(ei2)
+  !CHECK: PRINT *, 8_4
+  print *, kind(el8)
+  !CHECK: PRINT *, 2_4
+  print *, kind(er2)
+end
+#endif

diff  --git a/flang/test/Semantics/substring-literal-kind.f90 b/flang/test/Semantics/substring-literal-kind.f90
new file mode 100644
index 0000000000000..7fa1d40605567
--- /dev/null
+++ b/flang/test/Semantics/substring-literal-kind.f90
@@ -0,0 +1,13 @@
+! RUN: %flang_fc1 -fsyntax-only %s
+! Regression test: a substring of a character literal has the kind of the
+! literal (F2023 9.4.1), so concatenating it with another literal of that
+! kind is valid (F2023 10.1.5.3 requires the same kind on both operands).
+! This must stay a -fsyntax-only test of a non-constant context: the value
+! of a substring of a non-default-kind literal does not fold, so a constant
+! context would be rejected, and lowering it is not implemented yet.
+subroutine s()
+  character(kind=4, len=3) :: t4
+  character(kind=2, len=3) :: t2
+  t4 = 4_"ab"(1:1) // 4_"cd"
+  t2 = 2_"ab"(1:1) // 2_"cd"
+end subroutine


        


More information about the flang-commits mailing list