[flang-commits] [flang] b25ecac - [flang] Implement conditional expressions lowering (F2023) (#186490)
via flang-commits
flang-commits at lists.llvm.org
Mon Apr 13 09:54:33 PDT 2026
Author: Caroline Newcombe
Date: 2026-04-13T11:54:24-05:00
New Revision: b25ecac3c3cc759f100d2c9b2b8379f9eb76241b
URL: https://github.com/llvm/llvm-project/commit/b25ecac3c3cc759f100d2c9b2b8379f9eb76241b
DIFF: https://github.com/llvm/llvm-project/commit/b25ecac3c3cc759f100d2c9b2b8379f9eb76241b.diff
LOG: [flang] Implement conditional expressions lowering (F2023) (#186490)
## Implement Lowering for Fortran 2023 Conditional Expressions (R1002)
***This PR contains the lowering steps only for ease of review. DO NOT
MERGE until #186489 is merged.***
Implements Fortran 2023 conditional expressions with syntax: `result =
(condition ? value1 : condition2 ? value2 : ... : elseValue)`
Issue: #176999
Discourse:
https://discourse.llvm.org/t/rfc-adding-conditional-expressions-in-flang-f2023/89869/1
-- note that some of the details provided in the RFC post are no longer
accurate
### Implementation Details
**Lowering to HLFIR:**
- Lazy evaluation via nested if-then-else control flow
- Only the selected branch is evaluated
- Temporary allocation with proper cleanup
- Special handling for:
- CHARACTER types with deferred length
- Arrays (shape determined by selected branch per F2023 10.1.4(7))
- Derived types
**LIT Testing:**
- Lowering tests: HLFIR code generation verification
- Note: Executable tests will be added to the llvm-test-suite repo
(https://github.com/llvm/llvm-test-suite/pull/369)
**Limitations**
- Conditional arguments are not yet supported. This work is planned
- #180592
- Polymorphic types (CLASS) not yet supported in lowering
- Both limitations will emit clear error message if encountered
### Examples
```
! Simple conditional
x = (flag ? 10 : 20)
! Chained
result = (x > 0 ? 1 : x < 0 ? -1 : 0)
! Examples from F2023
( ABS (RESIDUAL)<=TOLERANCE ? ’ok’ : ’did not converge’ )
( I>0 .AND. I<=SIZE (A) ? A (I) : PRESENT (VAL) ? VAL : 0.0 )
```
AI Usage Disclosure: AI tools (Claude Sonnet 4.5) were used to assist
with implementation of this feature and test code generation. I have
reviewed, modified, and tested all AI-generated code.
Added:
flang/test/Lower/HLFIR/conditional-expr.f90
Modified:
flang/lib/Lower/ConvertExprToHLFIR.cpp
Removed:
################################################################################
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index 5ee409b031357..cad54fc441d88 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1814,12 +1814,6 @@ class HlfirBuilder {
TODO(getLoc(), "lowering type parameter inquiry to HLFIR");
}
- template <typename T>
- hlfir::EntityWithAttributes
- gen(const Fortran::evaluate::ConditionalExpr<T> &) {
- TODO(getLoc(), "lowering conditional expression to HLFIR");
- }
-
hlfir::EntityWithAttributes
gen(const Fortran::evaluate::DescriptorInquiry &desc) {
mlir::Location loc = getLoc();
@@ -1853,6 +1847,155 @@ class HlfirBuilder {
llvm_unreachable("unknown descriptor inquiry");
}
+ /// Build nested if-then-else chain by walking the right-skewed
+ /// ConditionalExpr tree. The assignValue callback generates and assigns
+ /// each value to avoid evaluating non-taken branches.
+ template <typename T, typename Callback>
+ void
+ buildConditionalIfChain(const Fortran::evaluate::ConditionalExpr<T> &condExpr,
+ const Callback &assignValue) {
+ const mlir::Location loc{getLoc()};
+ fir::FirOpBuilder &builder{getBuilder()};
+ getStmtCtx().pushScope();
+ const hlfir::EntityWithAttributes condEntity{gen(condExpr.condition())};
+ mlir::Value condition{hlfir::loadTrivialScalar(loc, builder, condEntity)};
+ condition = builder.createConvert(loc, builder.getI1Type(), condition);
+ builder.genIfOp(loc, {}, condition, /*withElseRegion=*/true)
+ .genThen([&]() {
+ getStmtCtx().pushScope();
+ assignValue(condExpr.thenValue());
+ getStmtCtx().finalizeAndPop();
+ })
+ .genElse([&]() {
+ getStmtCtx().pushScope();
+ assignValue(condExpr.elseValue());
+ getStmtCtx().finalizeAndPop();
+ })
+ .end();
+ getStmtCtx().finalizeAndPop();
+ }
+
+ /// Generate scalar conditional with lazy evaluation using assignment.
+ /// Creates a temporary and assigns the selected branch value to it.
+ template <typename T>
+ hlfir::Entity
+ genScalarConditional(const Fortran::evaluate::ConditionalExpr<T> &condExpr,
+ mlir::Type elementType,
+ const llvm::SmallVector<mlir::Value, 1> &typeParams) {
+ const mlir::Location loc{getLoc()};
+ fir::FirOpBuilder &builder{getBuilder()};
+ const mlir::Value tempStorage{builder.createTemporary(
+ loc, elementType, ".cond.scalar",
+ /*shape=*/mlir::ValueRange{}, /*typeParams=*/typeParams)};
+ const hlfir::DeclareOp tempDecl{hlfir::DeclareOp::create(
+ builder, loc, tempStorage, ".cond.result",
+ /*shape=*/mlir::Value{}, /*typeParams=*/typeParams)};
+ const hlfir::Entity temp{tempDecl};
+ buildConditionalIfChain(
+ condExpr, [&](const Fortran::evaluate::Expr<T> &expr) {
+ hlfir::Entity entity{gen(expr)};
+ entity = hlfir::loadTrivialScalar(loc, builder, entity);
+ hlfir::AssignOp::create(builder, loc, entity, temp);
+ });
+ return temp;
+ }
+
+ /// Generate conditional expression using an allocatable temporary with lazy
+ /// evaluation. Creates an unallocated allocatable, then uses assignment to
+ /// set the value from the chosen branch (allocation/reallocation handled by
+ /// runtime).
+ template <typename T>
+ hlfir::Entity genAllocatableConditional(
+ const Fortran::evaluate::ConditionalExpr<T> &condExpr,
+ mlir::Type resultType, llvm::StringRef debugName) {
+ const mlir::Location loc{getLoc()};
+ fir::FirOpBuilder &builder{getBuilder()};
+ const mlir::Type heapType{fir::HeapType::get(resultType)};
+ const mlir::Type boxHeapType{fir::BoxType::get(heapType)};
+ const mlir::Value tempStorage{
+ builder.createTemporary(loc, boxHeapType, debugName)};
+ const mlir::Value unallocBox{fir::factory::createUnallocatedBox(
+ builder, loc, boxHeapType, /*nonDeferredParams=*/{})};
+ builder.createStoreWithConvert(loc, unallocBox, tempStorage);
+ const hlfir::DeclareOp tempDecl{
+ hlfir::DeclareOp::create(builder, loc, tempStorage, ".cond.result")};
+ const hlfir::Entity temp{tempDecl};
+ // Lazy evaluation: only the selected branch is evaluated and assigned.
+ buildConditionalIfChain(
+ condExpr, [&](const Fortran::evaluate::Expr<T> &expr) {
+ const hlfir::Entity entity{gen(expr)};
+ hlfir::AssignOp::create(builder, loc, entity, temp,
+ /*isWholeAllocatableAssignment=*/true,
+ /*keepLhsLengthIfRealloc=*/false,
+ /*temporary_lhs=*/true);
+ });
+ fir::FirOpBuilder *const bldr{&builder};
+ getStmtCtx().attachCleanup([=]() {
+ fir::factory::genFreememIfAllocated(
+ *bldr, loc,
+ fir::MutableBoxValue{tempStorage, /*lenParams=*/{},
+ fir::MutableProperties{}});
+ });
+ return temp;
+ }
+
+ /// Generate scalar CHARACTER conditional with proper length handling.
+ template <typename T>
+ std::optional<hlfir::EntityWithAttributes> genCharacterConditional(
+ const Fortran::evaluate::ConditionalExpr<T> &condExpr) {
+ const mlir::Location loc{getLoc()};
+ fir::FirOpBuilder &builder{getBuilder()};
+ const mlir::Type resultType{Fortran::lower::translateSomeExprToFIRType(
+ converter, toEvExpr(condExpr))};
+ const mlir::Type elementType{hlfir::getFortranElementType(resultType)};
+ if (auto charType = mlir::dyn_cast<fir::CharacterType>(elementType)) {
+ if (charType.hasConstantLen()) {
+ llvm::SmallVector<mlir::Value, 1> typeParams;
+ const mlir::Value len{builder.createIntegerConstant(
+ loc, builder.getCharacterLengthType(), charType.getLen())};
+ typeParams.push_back(len);
+ return hlfir::EntityWithAttributes{
+ genScalarConditional(condExpr, elementType, typeParams)};
+ }
+ // Non-constant/varying length: use allocatable conditional to get length
+ // from selected branch.
+ return hlfir::EntityWithAttributes{
+ genAllocatableConditional(condExpr, elementType, ".cond.char")};
+ }
+ return std::nullopt;
+ }
+
+ /// Conditional expression (Fortran 2023)
+ template <typename T>
+ hlfir::EntityWithAttributes
+ gen(const Fortran::evaluate::ConditionalExpr<T> &condExpr) {
+ const int rank{condExpr.Rank()};
+ mlir::Type resultType{Fortran::lower::translateSomeExprToFIRType(
+ converter, toEvExpr(condExpr))};
+ if (fir::isPolymorphicType(resultType))
+ TODO(getLoc(), "polymorphic conditional expression");
+ if (fir::isRecordWithTypeParameters(
+ hlfir::getFortranElementType(resultType)))
+ TODO(getLoc(), "conditional expression with length-parameterized "
+ "derived type");
+ // Arrays: handle early to avoid unnecessary type checks.
+ // Per F2023 10.1.4(7), the shape is determined by the chosen branch.
+ if (rank != 0) {
+ const mlir::Type condResultType{
+ hlfir::getFortranElementOrSequenceType(resultType)};
+ return hlfir::EntityWithAttributes{
+ genAllocatableConditional(condExpr, condResultType, ".cond.array")};
+ }
+ // CHARACTER scalars require special handling for type parameters.
+ if constexpr (T::category == Fortran::common::TypeCategory::Character) {
+ if (auto result = genCharacterConditional(condExpr))
+ return *result;
+ }
+ // Scalar types (INTEGER, REAL, COMPLEX, LOGICAL, UNSIGNED, Derived).
+ return hlfir::EntityWithAttributes{genScalarConditional(
+ condExpr, hlfir::getFortranElementType(resultType), {})};
+ }
+
hlfir::EntityWithAttributes
gen(const Fortran::evaluate::ImpliedDoIndex &var) {
mlir::Value value = symMap.lookupImpliedDo(toStringRef(var.name));
diff --git a/flang/test/Lower/HLFIR/conditional-expr.f90 b/flang/test/Lower/HLFIR/conditional-expr.f90
new file mode 100644
index 0000000000000..d0d7f41a92124
--- /dev/null
+++ b/flang/test/Lower/HLFIR/conditional-expr.f90
@@ -0,0 +1,273 @@
+! Test lowering of conditional expressions (Fortran 2023)
+! RUN: %flang_fc1 -emit-hlfir -o - %s 2>&1 | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_scalar_integer(
+! CHECK-SAME: %[[FLAG:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "flag"},
+! CHECK-SAME: %[[X:.*]]: !fir.ref<i32> {fir.bindc_name = "x"},
+! CHECK-SAME: %[[Y:.*]]: !fir.ref<i32> {fir.bindc_name = "y"})
+subroutine test_scalar_integer(flag, x, y)
+ logical :: flag
+ integer :: x, y, result
+ ! CHECK: %[[TEMP:.*]] = fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! CHECK-DAG: %[[FLAG_DECL:.*]]:2 = hlfir.declare %[[FLAG]]
+ ! CHECK-DAG: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]]
+ ! CHECK-DAG: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]]
+ ! CHECK: %[[TEMP_DECL:.*]]:2 = hlfir.declare %[[TEMP]] {uniq_name = ".cond.result"}
+
+ result = (flag ? x : y)
+ ! CHECK: %[[FLAG_LOAD:.*]] = fir.load %[[FLAG_DECL]]#0
+ ! CHECK: %[[FLAG_CONV:.*]] = fir.convert %[[FLAG_LOAD]] : (!fir.logical<4>) -> i1
+ ! CHECK: fir.if %[[FLAG_CONV]] {
+ ! CHECK: %[[X_LOAD:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+ ! CHECK: hlfir.assign %[[X_LOAD]] to %[[TEMP_DECL]]#0 : i32, !fir.ref<i32>
+ ! CHECK: } else {
+ ! CHECK: %[[Y_LOAD:.*]] = fir.load %[[Y_DECL]]#0 : !fir.ref<i32>
+ ! CHECK: hlfir.assign %[[Y_LOAD]] to %[[TEMP_DECL]]#0 : i32, !fir.ref<i32>
+ ! CHECK: }
+ ! CHECK: %[[LOAD:.*]] = fir.load %[[TEMP_DECL]]#0
+ ! CHECK: hlfir.assign %[[LOAD]] to %{{.*}} : i32, !fir.ref<i32>
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_scalar_real(
+subroutine test_scalar_real(flag, x, y)
+ logical :: flag
+ real :: x, y, result
+ result = (flag ? x : y)
+ ! CHECK: %[[TEMP:.*]] = fir.alloca f32 {bindc_name = ".cond.scalar"
+ ! CHECK: %[[TEMP_DECL:.*]]:2 = hlfir.declare %[[TEMP]] {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : f32, !fir.ref<f32>
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : f32, !fir.ref<f32>
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_scalar_complex(
+subroutine test_scalar_complex(flag, x, y)
+ logical :: flag
+ complex :: x, y, result
+ result = (flag ? x : y)
+ ! CHECK: %[[TEMP:.*]] = fir.alloca complex<f32> {bindc_name = ".cond.scalar"
+ ! CHECK: %[[TEMP_DECL:.*]]:2 = hlfir.declare %[[TEMP]] {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : complex<f32>, !fir.ref<complex<f32>>
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : complex<f32>, !fir.ref<complex<f32>>
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_scalar_logical(
+subroutine test_scalar_logical(flag, x, y)
+ logical :: flag, x, y, result
+ result = (flag ? x : y)
+ ! CHECK: %[[TEMP:.*]] = fir.alloca !fir.logical<4> {bindc_name = ".cond.scalar"
+ ! CHECK: fir.if
+ ! CHECK: } else {
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_multi_branch(
+subroutine test_multi_branch(x)
+ integer :: x, result
+ ! Multi-branch: x > 10 ? 100 : x > 5 ? 50 : 0
+ result = (x > 10 ? 100 : x > 5 ? 50 : 0)
+ ! Both outer and inner temps are hoisted to function entry.
+ ! CHECK-DAG: fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! CHECK-DAG: fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! Outer temp declaration and first condition: x > 10
+ ! CHECK: hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: arith.cmpi sgt
+ ! CHECK: fir.if {{.*}} {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: } else {
+ ! Inner temp for the nested conditional: x > 5 ? 50 : 0
+ ! CHECK: hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: arith.cmpi sgt
+ ! CHECK: fir.if {{.*}} {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: }
+ ! CHECK: fir.load
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_char_constant_len(
+subroutine test_char_constant_len(flag)
+ logical :: flag
+ character(len=5) :: str1, str2, result
+ str1 = "HELLO"
+ str2 = "WORLD"
+ result = (flag ? str1 : str2)
+ ! Constant length: use scalar temp path.
+ ! CHECK: %[[TEMP:.*]] = fir.alloca !fir.char<1,5> {bindc_name = ".cond.scalar"
+ ! CHECK: %[[TEMP_DECL:.*]]:2 = hlfir.declare %[[TEMP]] typeparams {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_char_deferred_len(
+subroutine test_char_deferred_len(flag)
+ logical :: flag
+ character(len=:), allocatable :: str1, str2, result
+ str1 = "SHORT"
+ str2 = "A MUCH LONGER STRING"
+ ! Result length comes from selected branch
+ result = (flag ? str1 : str2)
+ ! CHECK-DAG: %[[BOX_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,?>>> {bindc_name = ".cond.char"
+ ! CHECK-DAG: %[[UNALLOC:.*]] = fir.zero_bits !fir.heap<!fir.char<1,?>>
+ ! CHECK-DAG: %[[C0:.*]] = arith.constant 0 : index
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[UNALLOC]] typeparams %[[C0]]
+ ! CHECK: fir.store %[[BOX]] to %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
+ ! CHECK: %[[BOX_DECL:.*]]:2 = hlfir.declare %[[BOX_ALLOC]] {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[BOX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[BOX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_array(
+subroutine test_array(flag)
+ logical :: flag
+ integer :: arr1(10), arr2(10), result(10)
+ arr1 = 1
+ arr2 = 2
+ result = (flag ? arr1 : arr2)
+ ! CHECK: %[[BOX_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<{{.*}}xi32>>> {bindc_name = ".cond.array"
+ ! CHECK: %[[UNALLOC:.*]] = fir.zero_bits !fir.heap<!fir.array<{{.*}}xi32>>
+ ! CHECK: %[[SHAPE:.*]] = fir.shape
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[UNALLOC]](%[[SHAPE]])
+ ! CHECK: fir.store %[[BOX]] to %[[BOX_ALLOC]]
+ ! CHECK: %[[BOX_DECL:.*]]:2 = hlfir.declare %[[BOX_ALLOC]] {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[BOX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[BOX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_derived_type(
+subroutine test_derived_type(flag)
+ type :: point
+ real :: x, y
+ end type
+ logical :: flag
+ type(point) :: p1, p2, result
+ p1 = point(1.0, 2.0)
+ p2 = point(3.0, 4.0)
+ result = (flag ? p1 : p2)
+ ! CHECK: %[[TEMP:.*]] = fir.alloca !fir.type<_QFtest_derived_typeTpoint{x:f32,y:f32}> {bindc_name = ".cond.scalar"
+ ! CHECK: %[[TEMP_DECL:.*]]:2 = hlfir.declare %[[TEMP]] {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : !fir.ref<!fir.type<_QFtest_derived_typeTpoint{x:f32,y:f32}>>, !fir.ref<!fir.type<_QFtest_derived_typeTpoint{x:f32,y:f32}>>
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[TEMP_DECL]]#0 : !fir.ref<!fir.type<_QFtest_derived_typeTpoint{x:f32,y:f32}>>, !fir.ref<!fir.type<_QFtest_derived_typeTpoint{x:f32,y:f32}>>
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_nested_conditionals(
+subroutine test_nested_conditionals(flag1, flag2, x, y, z)
+ logical :: flag1, flag2
+ integer :: x, y, z, result
+ ! Nested: flag1 ? (flag2 ? x : y) : z
+ result = (flag1 ? (flag2 ? x : y) : z)
+ ! Both outer and inner temps are hoisted to function entry.
+ ! CHECK-DAG: fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! CHECK-DAG: fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! Outer temp declaration and conditional
+ ! CHECK: hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if {{%.*}} {
+ ! Inner temp declaration and conditional
+ ! CHECK: hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if {{%.*}} {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: }
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}}
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_in_expression(
+subroutine test_in_expression(flag, x, y)
+ logical :: flag
+ integer :: x, y, z
+ ! Conditional in larger expression: (flag ? x : y) + 10
+ z = (flag ? x : y) + 10
+ ! CHECK: %[[TEMP:.*]] = fir.alloca i32 {bindc_name = ".cond.scalar"
+ ! CHECK: fir.if
+ ! CHECK: } else {
+ ! CHECK: }
+ ! CHECK: %[[COND_RESULT:.*]] = fir.load
+ ! CHECK: %[[C10:.*]] = arith.constant 10
+ ! CHECK: %[[SUM:.*]] = arith.addi %[[COND_RESULT]], %[[C10]]
+ ! CHECK: hlfir.assign %[[SUM]]
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_assumed_length_char(
+subroutine test_assumed_length_char(flag, str1, str2)
+ logical :: flag
+ character(len=*) :: str1, str2
+ character(len=100) :: result
+ result = (flag ? str1 : str2)
+ ! Deferred length path since len=* is not constant
+ ! CHECK: %[[BOX_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,?>>> {bindc_name = ".cond.char"
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_
diff erent_kinds(
+subroutine test_
diff erent_kinds(flag)
+ logical :: flag
+ integer(kind=4) :: i4_1, i4_2, i4_result
+ integer(kind=8) :: i8_1, i8_2, i8_result
+
+ ! Both temps allocated at function start
+ ! CHECK-DAG: %{{.*}} = fir.alloca i64 {bindc_name = ".cond.scalar"}
+ ! CHECK-DAG: %{{.*}} = fir.alloca i32 {bindc_name = ".cond.scalar"}
+
+ i4_1 = 1
+ i4_2 = 2
+ i4_result = (flag ? i4_1 : i4_2)
+
+ i8_1 = 3
+ i8_2 = 4
+ i8_result = (flag ? i8_1 : i8_2)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_array_section(
+subroutine test_array_section(flag)
+ logical :: flag
+ integer :: arr1(20), arr2(20), result(10)
+ result = (flag ? arr1(1:10) : arr2(11:20))
+ ! CHECK: %[[BOX_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<{{.*}}xi32>>> {bindc_name = ".cond.array"
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_noncontiguous_section(
+subroutine test_noncontiguous_section(flag)
+ logical :: flag
+ integer :: arr1(20), arr2(20), result(5)
+ ! Non-contiguous stride-2 sections: result must be contiguous.
+ result = (flag ? arr1(1:10:2) : arr2(2:10:2))
+ ! CHECK: %[[BOX_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<{{.*}}xi32>>> {bindc_name = ".cond.array"
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
+ ! CHECK: }
+end subroutine
More information about the flang-commits
mailing list