[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