[flang-commits] [flang] [flang] Implement conditional expressions parser/semantics (F2023) (PR #186489)

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Mon Mar 16 12:19:40 PDT 2026


================
@@ -3880,6 +3880,216 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) {
   return MakeFunctionRef(loc, ActualArguments{std::move(*arg)});
 }
 
+// Helper to detect Expr<T> types (have ::Result typedef)
+template <typename T, typename = void>
+struct HasResultType : std::false_type {};
+template <typename T>
+struct HasResultType<T, std::void_t<typename T::Result>> : std::true_type {};
+
+MaybeExpr ExpressionAnalyzer::Analyze(const parser::ConditionalExpr &x) {
+  // Analyze all branches (condition ? value pairs)
+  const auto &branches{
+      std::get<std::list<parser::ConditionalExpr::Branch>>(x.t)};
+  const auto &elseExpr{std::get<common::Indirection<parser::Expr>>(x.t)};
+  std::vector<MaybeExpr> conditions;
+  std::vector<MaybeExpr> values;
+  for (const auto &branch : branches) {
+    const auto &condition{std::get<parser::ScalarLogicalExpr>(branch.t)};
+    const auto &value{std::get<common::Indirection<parser::Expr>>(branch.t)};
+    MaybeExpr condExpr{Analyze(condition.thing.thing.value())};
+    if (!condExpr) {
+      return std::nullopt;
+    }
+    if (!std::get_if<Expr<SomeLogical>>(&condExpr->u)) {
+      if (const auto type{condExpr->GetType()}) {
+        Say("Condition in conditional expression must be LOGICAL; have %s"_err_en_US,
+            type->AsFortran());
+      } else {
+        Say("Condition in conditional expression must be LOGICAL"_err_en_US);
+      }
+      return std::nullopt;
+    }
+    if (condExpr->Rank() != 0) {
+      Say("Condition in conditional expression must be scalar; have rank %d"_err_en_US,
+          condExpr->Rank());
+      return std::nullopt;
+    }
+    conditions.push_back(std::move(condExpr));
+    MaybeExpr valExpr{Analyze(value.value())};
+    if (!valExpr) {
+      return std::nullopt;
+    }
+    if (semantics::IsAssumedRank(*valExpr)) {
+      Say("An assumed-rank dummy argument may not be used as a value in a conditional expression"_err_en_US);
+      return std::nullopt;
+    }
+    values.push_back(std::move(valExpr));
+  }
+
+  // Analyze else expression
+  MaybeExpr elseValue{Analyze(elseExpr.value())};
+  if (!elseValue) {
+    return std::nullopt;
+  }
+  if (semantics::IsAssumedRank(*elseValue)) {
+    Say("An assumed-rank dummy argument may not be used as a value in a conditional expression"_err_en_US);
+    return std::nullopt;
+  }
+  values.push_back(std::move(elseValue));
+  CHECK(values.size() == conditions.size() + 1 &&
+      "values must have exactly one more element than conditions");
+
+  // F2023 C1004: Each expr shall have the same declared type, kind type
+  // parameters, and rank Reject typeless expressions (BOZ and NULL)
+  for (const auto &value : values) {
+    // BOZ arrays are auto-converted in array constructors, but bare BOZ are not
+    // allowed
+    if (std::holds_alternative<BOZLiteralConstant>(value->u)) {
+      Say("BOZ literal constant in conditional expression must have explicit "
+          "type "
+          "(e.g., INT(z'FF'), REAL(z'3F800000'))"_err_en_US);
+      return std::nullopt;
+    }
+    if (std::holds_alternative<evaluate::NullPointer>(value->u)) {
+      Say("NULL() not allowed in conditional expression (expressions must have declared type)"_err_en_US);
+      return std::nullopt;
+    }
+  }
+
+  // Determine result type from first value
+  const std::optional<DynamicType> resultType = values[0]->GetType();
+  if (!resultType) {
+    Say("Cannot determine type of conditional expression"_err_en_US);
+    return std::nullopt;
+  }
+
+  // Check that all values have the exact same type and kind (no promotion
+  // allowed)
+  const TypeCategory resultCategory{resultType->category()};
+  const int resultKind{
+      resultCategory != TypeCategory::Derived ? resultType->kind() : 0};
+  const int resultRank = values[0]->Rank();
+  // Check for polymorphic types (not yet supported in lowering)
+  if (resultCategory == TypeCategory::Derived && resultType->IsPolymorphic()) {
+    Say("Conditional expressions with polymorphic types (CLASS) are not yet supported"_err_en_US);
+    return std::nullopt;
+  }
+  for (const auto &value : values) {
+    // Check for coindexed objects
+    if (const auto dataRef{ExtractDataRef(value)}) {
+      if (ExtractCoarrayRef(*dataRef)) {
+        Say("Conditional expression values may not be coindexed"_err_en_US);
+        return std::nullopt;
+      }
+    }
+    const auto valueType{value->GetType()};
+    if (!valueType) {
+      Say("Cannot determine type of expression in conditional expression"_err_en_US);
+      return std::nullopt;
+    }
+    const TypeCategory valueCategory{valueType->category()};
+    const int valueKind{
+        valueCategory != TypeCategory::Derived ? valueType->kind() : 0};
+    if (resultCategory != valueCategory ||
+        (resultCategory != TypeCategory::Derived && resultKind != valueKind)) {
+      Say("All values in conditional expression must have the same type and kind; have %s and %s"_err_en_US,
+          resultType->AsFortran(), valueType->AsFortran());
+      return std::nullopt;
+    }
+    // For derived types, check they are the exact same type (not just
+    // compatible)
+    if (resultCategory == TypeCategory::Derived) {
+      if (&resultType->GetDerivedTypeSpec().typeSymbol() !=
+          &valueType->GetDerivedTypeSpec().typeSymbol()) {
+        Say("All values in conditional expression must be the same derived type; have %s and %s"_err_en_US,
+            resultType->AsFortran(), valueType->AsFortran());
+        return std::nullopt;
+      }
+    }
+    const int valueRank{value->Rank()};
+    if (resultRank != valueRank) {
+      Say("All values in conditional expression must have the same rank; have rank %d and %d"_err_en_US,
+          resultRank, valueRank);
+      return std::nullopt;
+    }
+  }
+
+  // Dispatch on the runtime type of values[0] to build the appropriately
----------------
klausler wrote:

A recursive representation rather than an array would make this much more clean and straightforward.

https://github.com/llvm/llvm-project/pull/186489


More information about the flang-commits mailing list