[flang-commits] [flang] 7779ee8 - [flang] Support polymorphic types in conditional expressions (#192684)
via flang-commits
flang-commits at lists.llvm.org
Thu Apr 23 09:16:48 PDT 2026
Author: Caroline Newcombe
Date: 2026-04-23T11:16:43-05:00
New Revision: 7779ee8c0e957a9a8247ad540e5b59c86f1c819c
URL: https://github.com/llvm/llvm-project/commit/7779ee8c0e957a9a8247ad540e5b59c86f1c819c
DIFF: https://github.com/llvm/llvm-project/commit/7779ee8c0e957a9a8247ad540e5b59c86f1c819c.diff
LOG: [flang] Support polymorphic types in conditional expressions (#192684)
Add lowering support for polymorphic (`CLASS(T)`) conditional
expressions.
`ConditionalExpr::GetType()` now checks both branches and returns the
polymorphic type if either is polymorphic (F2023 10.1.4(7)). In
lowering, polymorphic conditionals use `fir::ClassType` instead of
`fir::BoxType` for the allocatable temporary so that dynamic type info
is preserved through `hlfir.assign` realloc.
Covers scalar, array, and mixed `TYPE(T)`/`CLASS(T)` operands. Unlimited
polymorphic `CLASS(*)` does not have a declared type, and is therefore
rejected in semantics. LIT tests for lowering and semantics have been
updated.
Added:
Modified:
flang/include/flang/Evaluate/expression.h
flang/lib/Lower/ConvertExprToHLFIR.cpp
flang/lib/Semantics/expression.cpp
flang/test/Lower/HLFIR/conditional-expr.f90
flang/test/Semantics/conditional-expr.f90
Removed:
################################################################################
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index c8570e4a52e78..1afe8ae74c415 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -408,7 +408,20 @@ template <typename T> class ConditionalExpr {
Expr<Result> &elseValue() { return elseValue_.value(); }
const Expr<Result> &elseValue() const { return elseValue_.value(); }
int Rank() const { return thenValue().Rank(); }
- std::optional<DynamicType> GetType() const { return thenValue().GetType(); }
+ std::optional<DynamicType> GetType() const {
+ const auto thenType{thenValue().GetType()};
+ if constexpr (T::category == TypeCategory::Derived) {
+ // F2023 10.1.4(7) A conditional-expr is polymorphic if any branch is
+ if (thenType && !thenType->IsPolymorphic()) {
+ if (const auto elseType{elseValue().GetType()}) {
+ if (elseType->IsPolymorphic()) {
+ return elseType;
+ }
+ }
+ }
+ }
+ return thenType;
+ }
static constexpr int Corank() { return 0; }
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index e3ffd9f7194c2..a57fce53c0ca5 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1950,8 +1950,15 @@ class HlfirBuilder {
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)};
+ // Polymorphic types need fir.class (not fir.box) to carry dynamic type
+ // info. Both scalar and array polymorphic types reach here.
+ const bool isPolymorphic{fir::isPolymorphicType(resultType)};
+ const mlir::Type allocType{
+ hlfir::getFortranElementOrSequenceType(resultType)};
+ const mlir::Type heapType{fir::HeapType::get(allocType)};
+ const mlir::Type boxHeapType{isPolymorphic
+ ? mlir::Type{fir::ClassType::get(heapType)}
+ : mlir::Type{fir::BoxType::get(heapType)}};
const mlir::Value tempStorage{
builder.createTemporary(loc, boxHeapType, debugName)};
const mlir::Value unallocBox{fir::factory::createUnallocatedBox(
@@ -2012,8 +2019,6 @@ class HlfirBuilder {
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 "
@@ -2021,10 +2026,8 @@ class HlfirBuilder {
// 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")};
+ genAllocatableConditional(condExpr, resultType, ".cond.array")};
}
// CHARACTER scalars require special handling for type parameters.
if constexpr (T::category == Fortran::common::TypeCategory::Character) {
@@ -2033,6 +2036,9 @@ class HlfirBuilder {
}
// Scalar types (INTEGER, REAL, COMPLEX, LOGICAL, UNSIGNED, Derived).
const mlir::Type elementType{hlfir::getFortranElementType(resultType)};
+ if (fir::isPolymorphicType(resultType))
+ return hlfir::EntityWithAttributes{
+ genAllocatableConditional(condExpr, resultType, ".cond.polymorphic")};
if (fir::isa_trivial(elementType))
return hlfir::EntityWithAttributes{
genTrivialScalarConditional(condExpr, elementType)};
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 80d0c595ce284..b578f6c4f978b 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3961,17 +3961,18 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ConditionalExpr &x) {
thenType->AsFortran(), elseType->AsFortran());
return std::nullopt;
}
- if (thenCat == TypeCategory::Derived &&
- (thenType->IsPolymorphic() || elseType->IsPolymorphic())) {
- Say("Conditional expressions with polymorphic types (CLASS) are not yet supported"_todo_en_US);
- return std::nullopt;
- }
- if (thenCat == TypeCategory::Derived &&
- !AreSameDerivedType(
- thenType->GetDerivedTypeSpec(), elseType->GetDerivedTypeSpec())) {
- Say("All values in conditional expression must be the same derived type; have %s and %s"_err_en_US,
- thenType->AsFortran(), elseType->AsFortran());
- return std::nullopt;
+ if (thenCat == TypeCategory::Derived) {
+ if (thenType->IsUnlimitedPolymorphic() ||
+ elseType->IsUnlimitedPolymorphic()) {
+ Say("Unlimited polymorphic types (CLASS(*)) not allowed in conditional expression"_err_en_US);
+ return std::nullopt;
+ }
+ if (!AreSameDerivedType(
+ thenType->GetDerivedTypeSpec(), elseType->GetDerivedTypeSpec())) {
+ Say("All values in conditional expression must be the same derived type; have %s and %s"_err_en_US,
+ thenType->AsFortran(), elseType->AsFortran());
+ return std::nullopt;
+ }
}
// Dispatch on the else-expr to recover the concrete kind type T.
diff --git a/flang/test/Lower/HLFIR/conditional-expr.f90 b/flang/test/Lower/HLFIR/conditional-expr.f90
index 5ec5db26fa970..56d9bc4c45369 100644
--- a/flang/test/Lower/HLFIR/conditional-expr.f90
+++ b/flang/test/Lower/HLFIR/conditional-expr.f90
@@ -287,3 +287,108 @@ subroutine test_noncontiguous_section(flag)
! CHECK: hlfir.assign {{.*}} to {{.*}} realloc temporary_lhs
! CHECK: }
end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_polymorphic(
+subroutine test_polymorphic(flag, x, y)
+ type :: base_type
+ integer :: val
+ end type
+ logical :: flag
+ class(base_type), intent(in) :: x, y
+ type(base_type) :: result
+ ! Polymorphic conditional: uses fir.class (not fir.box) to carry dynamic type.
+ result = (flag ? x : y)
+ ! CHECK: %[[BOX_ALLOC:.*]] = fir.alloca !fir.class<!fir.heap<!fir.type<_QFtest_polymorphicTbase_type{val:i32}>>> {bindc_name = ".cond.polymorphic"
+ ! CHECK: %[[UNALLOC:.*]] = fir.zero_bits !fir.heap<!fir.type<_QFtest_polymorphicTbase_type{val:i32}>>
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[UNALLOC]] : (!fir.heap<!fir.type<_QFtest_polymorphicTbase_type{val:i32}>>) -> !fir.class<!fir.heap<!fir.type<_QFtest_polymorphicTbase_type{val:i32}>>>
+ ! 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_polymorphic_array(
+subroutine test_polymorphic_array(flag, x, y)
+ type :: base_type
+ integer :: val
+ end type
+ logical :: flag
+ class(base_type), intent(in) :: x(:), y(:)
+ type(base_type), allocatable :: result(:)
+ ! Polymorphic array: uses fir.class (not fir.box) for the allocatable temp.
+ result = (flag ? x : y)
+ ! CHECK: fir.alloca !fir.class<!fir.heap<!fir.array<?x!fir.type<_QFtest_polymorphic_arrayTbase_type{val:i32}>>>> {bindc_name = ".cond.array"
+ ! CHECK: %[[PA_DECL:.*]]:2 = hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[PA_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[PA_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_polymorphic_char_component(
+subroutine test_polymorphic_char_component(flag, x, y)
+ type :: named_type
+ character(20) :: name
+ integer :: id
+ end type
+ logical :: flag
+ class(named_type), intent(in) :: x, y
+ type(named_type), allocatable :: result
+ result = (flag ? x : y)
+ ! The alloca type proves fir.class is used for the polymorphic temp.
+ ! CHECK: fir.alloca !fir.class<!fir.heap<!fir.type<_QFtest_polymorphic_char_componentTnamed_type{name:!fir.char<1,20>,id:i32}>>> {bindc_name = ".cond.polymorphic"
+ ! CHECK: %[[PC_DECL:.*]]:2 = hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[PC_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[PC_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_mixed_type_class(
+subroutine test_mixed_type_class(flag, x, y)
+ type :: base_type
+ integer :: val
+ end type
+ logical :: flag
+ type(base_type), intent(in) :: x
+ class(base_type), intent(in) :: y
+ type(base_type) :: result
+ ! Mixed TYPE(t)/CLASS(t): GetType() returns polymorphic when either branch
+ ! is polymorphic, so the result must use fir.class (not fir.box).
+ result = (flag ? x : y)
+ ! CHECK: fir.alloca !fir.class<!fir.heap<!fir.type<_QFtest_mixed_type_classTbase_type{val:i32}>>> {bindc_name = ".cond.polymorphic"
+ ! CHECK: %[[MX_DECL:.*]]:2 = hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[MX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[MX_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_polymorphic_extends(
+subroutine test_polymorphic_extends(flag, x, y)
+ type :: base_type
+ integer :: val
+ end type
+ type, extends(base_type) :: extended_type
+ real :: extra
+ end type
+ logical :: flag
+ class(base_type), intent(in) :: x, y
+ type(base_type) :: result
+ ! Polymorphic with type hierarchy: x or y may hold extended_type at runtime.
+ ! The lowering must use fir.class to preserve dynamic type info.
+ result = (flag ? x : y)
+ ! CHECK: fir.alloca !fir.class<!fir.heap<!fir.type<_QFtest_polymorphic_extendsTbase_type{val:i32}>>> {bindc_name = ".cond.polymorphic"
+ ! CHECK: %[[PE_DECL:.*]]:2 = hlfir.declare {{.*}} {uniq_name = ".cond.result"}
+ ! CHECK: fir.if
+ ! CHECK: hlfir.assign {{.*}} to %[[PE_DECL]]#0 realloc temporary_lhs
+ ! CHECK: } else {
+ ! CHECK: hlfir.assign {{.*}} to %[[PE_DECL]]#0 realloc temporary_lhs
+ ! CHECK: }
+end subroutine
diff --git a/flang/test/Semantics/conditional-expr.f90 b/flang/test/Semantics/conditional-expr.f90
index 12fbea7e86488..46312cd5b6847 100644
--- a/flang/test/Semantics/conditional-expr.f90
+++ b/flang/test/Semantics/conditional-expr.f90
@@ -342,8 +342,8 @@ subroutine valid_multi_branch(x)
result = (x > 20 ? 1 : x > 15 ? 2 : x > 10 ? 3 : x > 5 ? 4 : 5)
end subroutine
-! Error: polymorphic types not yet supported
-subroutine error_polymorphic(flag)
+! Valid: polymorphic types
+subroutine valid_polymorphic(flag)
type :: base_t
integer :: i
end type
@@ -351,10 +351,18 @@ subroutine error_polymorphic(flag)
logical :: flag
class(base_t), allocatable :: poly1, poly2, result
- !ERROR: not yet implemented: Conditional expressions with polymorphic types (CLASS) are not yet supported
result = (flag ? poly1 : poly2)
end subroutine
+! Error: unlimited polymorphic (CLASS(*)) not allowed
+subroutine error_unlimited_polymorphic(flag)
+ logical :: flag
+ class(*), allocatable :: star1, star2, result
+
+ !ERROR: Unlimited polymorphic types (CLASS(*)) not allowed in conditional expression
+ result = (flag ? star1 : star2)
+end subroutine
+
! Error: mismatched character kinds
subroutine error_character_kind_mismatch(flag)
logical :: flag
More information about the flang-commits
mailing list