[flang-commits] [flang] [flang] Add kind-preservation regression tests (PR #208760)
via flang-commits
flang-commits at lists.llvm.org
Fri Jul 10 09:10:53 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
@llvm/pr-subscribers-flang-fir-hlfir
Author: Eugene Epshteyn (eugeneepshteyn)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/208760.diff
7 Files Affected:
- (added) flang/test/Evaluate/fold-kind-logical.f90 (+43)
- (added) flang/test/Evaluate/fold-kind-substring.f90 (+14)
- (added) flang/test/Evaluate/fold-kind-zero-size.f90 (+28)
- (modified) flang/test/Evaluate/logical-args.f90 (+13)
- (added) flang/test/Lower/HLFIR/array-ctor-character-kind.f90 (+32)
- (added) flang/test/Semantics/modfile84.F90 (+29)
- (added) flang/test/Semantics/substring-literal-kind.f90 (+13)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/208760
More information about the flang-commits
mailing list