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

via flang-commits flang-commits at lists.llvm.org
Wed Apr 8 09:27:19 PDT 2026


Author: Caroline Newcombe
Date: 2026-04-08T11:27:12-05:00
New Revision: 802c5cbe5b4be7eb7b442ddbd76acb781921e5f7

URL: https://github.com/llvm/llvm-project/commit/802c5cbe5b4be7eb7b442ddbd76acb781921e5f7
DIFF: https://github.com/llvm/llvm-project/commit/802c5cbe5b4be7eb7b442ddbd76acb781921e5f7.diff

LOG: [flang] Implement conditional expressions parser/semantics (F2023) (#186489)

## Implement Fortran 2023 Conditional Expressions (R1002)

***This PR contains the implementation for parsing and semantic
analysis. Lowering is implemented in a separate PR (#186490)***

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
**Parser:**
- Added ConditionalExpr as primary expression (F2023 R1002)  
- Right-associative chaining for multi-way conditionals
 
**Semantics:**
- Expression tree node ConditionalExpr<T> with N conditions and N+1
values
- Strict type checking: all values must have identical type, kind, and
rank
- Conditions must be scalar LOGICAL

**LIT Testing:**
- Parser tests: Syntax validation, precedence, nesting
- Semantic tests: Type checking, error messages
- 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/Evaluate/fold-conditional-expr.f90
    flang/test/Parser/conditional-expr.f90
    flang/test/Semantics/conditional-expr.f90

Modified: 
    flang/examples/FeatureList/FeatureList.cpp
    flang/include/flang/Evaluate/expression.h
    flang/include/flang/Evaluate/fold.h
    flang/include/flang/Evaluate/shape.h
    flang/include/flang/Evaluate/tools.h
    flang/include/flang/Evaluate/traverse.h
    flang/include/flang/Parser/characters.h
    flang/include/flang/Parser/dump-parse-tree.h
    flang/include/flang/Parser/parse-tree.h
    flang/include/flang/Semantics/dump-expr.h
    flang/include/flang/Semantics/expression.h
    flang/lib/Evaluate/check-expression.cpp
    flang/lib/Evaluate/expression.cpp
    flang/lib/Evaluate/fold-implementation.h
    flang/lib/Evaluate/formatting.cpp
    flang/lib/Evaluate/tools.cpp
    flang/lib/Lower/ConvertExpr.cpp
    flang/lib/Lower/ConvertExprToHLFIR.cpp
    flang/lib/Lower/IterationSpace.cpp
    flang/lib/Lower/Support/Utils.cpp
    flang/lib/Parser/expr-parsers.cpp
    flang/lib/Parser/unparse.cpp
    flang/lib/Semantics/definable.cpp
    flang/lib/Semantics/expression.cpp
    flang/lib/Semantics/openmp-utils.cpp

Removed: 
    


################################################################################
diff  --git a/flang/examples/FeatureList/FeatureList.cpp b/flang/examples/FeatureList/FeatureList.cpp
index 355d79a04e4ba..bee18096f9fb2 100644
--- a/flang/examples/FeatureList/FeatureList.cpp
+++ b/flang/examples/FeatureList/FeatureList.cpp
@@ -311,6 +311,7 @@ struct NodeVisitor {
   READ_FEATURE(Expr::NEQV)
   READ_FEATURE(Expr::DefinedBinary)
   READ_FEATURE(Expr::ComplexConstructor)
+  READ_FEATURE(ConditionalExpr)
   READ_FEATURE(External)
   READ_FEATURE(ExternalStmt)
   READ_FEATURE(FailImageStmt)

diff  --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index f7a1f9b955181..c8570e4a52e78 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -390,6 +390,34 @@ struct LogicalOperation
   LogicalOperator logicalOperator;
 };
 
+// Fortran 2023 conditional expression: (cond ? val : cond ? val : ... : else)
+// All branches have the same type and rank (verified during semantic analysis).
+template <typename T> class ConditionalExpr {
+public:
+  using Result = T;
+  CLASS_BOILERPLATE(ConditionalExpr)
+  ConditionalExpr(Expr<LogicalResult> &&cond, Expr<Result> &&thenVal,
+      Expr<Result> &&elseVal)
+      : condition_{std::move(cond)}, thenValue_{std::move(thenVal)},
+        elseValue_{std::move(elseVal)} {}
+  bool operator==(const ConditionalExpr &) const;
+  Expr<LogicalResult> &condition() { return condition_.value(); }
+  const Expr<LogicalResult> &condition() const { return condition_.value(); }
+  Expr<Result> &thenValue() { return thenValue_.value(); }
+  const Expr<Result> &thenValue() const { return thenValue_.value(); }
+  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(); }
+  static constexpr int Corank() { return 0; }
+  llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
+
+private:
+  common::CopyableIndirection<Expr<LogicalResult>> condition_;
+  common::CopyableIndirection<Expr<Result>> thenValue_;
+  common::CopyableIndirection<Expr<Result>> elseValue_;
+};
+
 // Array constructors
 template <typename RESULT> class ArrayConstructorValues;
 
@@ -536,7 +564,7 @@ class Expr<Type<TypeCategory::Integer, KIND>>
       Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
-      Power<Result>, Extremum<Result>>;
+      Power<Result>, Extremum<Result>, ConditionalExpr<Result>>;
   using Indices = std::conditional_t<KIND == ImpliedDoIndex::Result::kind,
       std::tuple<ImpliedDoIndex>, std::tuple<>>;
   using TypeParamInquiries =
@@ -568,7 +596,7 @@ class Expr<Type<TypeCategory::Unsigned, KIND>>
       Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
-      Power<Result>, Extremum<Result>>;
+      Power<Result>, Extremum<Result>, ConditionalExpr<Result>>;
   using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
       Designator<Result>, FunctionRef<Result>>;
 
@@ -594,7 +622,8 @@ class Expr<Type<TypeCategory::Real, KIND>>
       Convert<Result, TypeCategory::Unsigned>>;
   using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
       Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
-      Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
+      Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>,
+      ConditionalExpr<Result>>;
   using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
       Designator<Result>, FunctionRef<Result>>;
 
@@ -612,7 +641,7 @@ class Expr<Type<TypeCategory::Complex, KIND>>
   using Operations = std::variant<Parentheses<Result>, Negate<Result>,
       Convert<Result, TypeCategory::Complex>, Add<Result>, Subtract<Result>,
       Multiply<Result>, Divide<Result>, Power<Result>, RealToIntPower<Result>,
-      ComplexConstructor<KIND>>;
+      ComplexConstructor<KIND>, ConditionalExpr<Result>>;
   using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
       Designator<Result>, FunctionRef<Result>>;
 
@@ -638,7 +667,7 @@ class Expr<Type<TypeCategory::Character, KIND>>
 
   std::variant<Constant<Result>, ArrayConstructor<Result>, Designator<Result>,
       FunctionRef<Result>, Parentheses<Result>, Convert<Result>, Concat<KIND>,
-      Extremum<Result>, SetLength<KIND>>
+      Extremum<Result>, SetLength<KIND>, ConditionalExpr<Result>>
       u;
 };
 
@@ -710,7 +739,7 @@ class Expr<Type<TypeCategory::Logical, KIND>>
 
 private:
   using Operations = std::tuple<Convert<Result>, Parentheses<Result>, Not<KIND>,
-      LogicalOperation<KIND>>;
+      LogicalOperation<KIND>, ConditionalExpr<Result>>;
   using Relations = std::conditional_t<KIND == LogicalResult::kind,
       std::tuple<Relational<SomeType>>, std::tuple<>>;
   using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
@@ -788,7 +817,8 @@ template <> class Expr<SomeDerived> : public ExpressionBase<SomeDerived> {
   using Result = SomeDerived;
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
   std::variant<Constant<Result>, ArrayConstructor<Result>, StructureConstructor,
-      Designator<Result>, FunctionRef<Result>, Parentheses<Result>>
+      Designator<Result>, FunctionRef<Result>, Parentheses<Result>,
+      ConditionalExpr<Result>>
       u;
 };
 
@@ -929,6 +959,7 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
   template class Relational<SomeType>; \
   FOR_EACH_TYPE_AND_KIND(template class ExpressionBase, ) \
   FOR_EACH_INTRINSIC_KIND(template class ArrayConstructorValues, ) \
-  FOR_EACH_INTRINSIC_KIND(template class ArrayConstructor, )
+  FOR_EACH_INTRINSIC_KIND(template class ArrayConstructor, ) \
+  FOR_EACH_INTRINSIC_KIND(template class ConditionalExpr, )
 } // namespace Fortran::evaluate
 #endif // FORTRAN_EVALUATE_EXPRESSION_H_

diff  --git a/flang/include/flang/Evaluate/fold.h b/flang/include/flang/Evaluate/fold.h
index b21c0f311fd35..df43489aa679c 100644
--- a/flang/include/flang/Evaluate/fold.h
+++ b/flang/include/flang/Evaluate/fold.h
@@ -105,6 +105,15 @@ std::optional<std::int64_t> ToInt64(const Expr<SomeUnsigned> &);
 std::optional<std::int64_t> ToInt64(const Expr<SomeType> &);
 std::optional<std::int64_t> ToInt64(const ActualArgument &);
 
+// When an expression is a constant logical scalar, ToLogical() extracts its
+// value.
+inline std::optional<bool> ToLogical(const Expr<LogicalResult> &expr) {
+  if (auto val{GetScalarConstantValue<LogicalResult>(expr)}) {
+    return val->IsTrue();
+  }
+  return std::nullopt;
+}
+
 template <typename A>
 std::optional<std::int64_t> ToInt64(const std::optional<A> &x) {
   if (x) {

diff  --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index f0505cfcdf2d7..e5c2d6e8cb63d 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -189,6 +189,21 @@ class GetShapeHelper
   Result operator()(const ArrayConstructor<T> &aconst) const {
     return Shape{GetArrayConstructorExtent(aconst)};
   }
+  template <typename T>
+  Result operator()(const ConditionalExpr<T> &conditional) const {
+    // Per F2023 10.1.4(7), the shape is that of the selected branch.
+    // When all branches have identical static extents, return the common shape.
+    int rank{conditional.thenValue().Rank()};
+    Result thenShape{(*this)(conditional.thenValue())};
+    if (!thenShape) {
+      return Shape(rank, std::nullopt);
+    }
+    Result elseShape{(*this)(conditional.elseValue())};
+    if (thenShape != elseShape) {
+      return Shape(rank, std::nullopt);
+    }
+    return thenShape;
+  }
   template <typename D, typename R, typename LO, typename RO>
   Result operator()(const Operation<D, R, LO, RO> &operation) const {
     if (int rr{operation.right().Rank()}; rr > 0) {

diff  --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 51dc0582fcdea..cab6ddbb6b27c 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -50,6 +50,9 @@ struct IsVariableHelper
   Result operator()(const CoarrayRef &) const { return true; }
   Result operator()(const ComplexPart &) const { return true; }
   Result operator()(const ProcedureDesignator &) const;
+  template <typename T> Result operator()(const ConditionalExpr<T> &) const {
+    return false;
+  }
   template <typename T> Result operator()(const Expr<T> &x) const {
     if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
         std::is_same_v<T, SomeDerived>) {
@@ -1410,6 +1413,7 @@ enum class Operator {
   Call,
   Constant,
   Convert,
+  Conditional,
   Div,
   Eq,
   Eqv,

diff  --git a/flang/include/flang/Evaluate/traverse.h b/flang/include/flang/Evaluate/traverse.h
index d63c16f93230a..44cfaa2a7073d 100644
--- a/flang/include/flang/Evaluate/traverse.h
+++ b/flang/include/flang/Evaluate/traverse.h
@@ -224,6 +224,10 @@ class Traverse {
   Result operator()(const StructureConstructor &x) const {
     return visitor_.Combine(visitor_(x.derivedTypeSpec()), CombineContents(x));
   }
+  // Conditional expressions (Fortran 2023)
+  template <typename T> Result operator()(const ConditionalExpr<T> &x) const {
+    return Combine(x.condition(), x.thenValue(), x.elseValue());
+  }
 
   // Operations and wrappers
   // Have a single operator() for all Operations.

diff  --git a/flang/include/flang/Parser/characters.h b/flang/include/flang/Parser/characters.h
index 3761700ad348c..620c6b357f948 100644
--- a/flang/include/flang/Parser/characters.h
+++ b/flang/include/flang/Parser/characters.h
@@ -170,6 +170,7 @@ inline constexpr bool IsValidFortranTokenCharacter(char ch) {
   case '<':
   case '=':
   case '>':
+  case '?': // Used in conditional expressions (Fortran 2023)
   case '[':
   case ']':
   case '{': // Used in OpenMP context selector specification

diff  --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 84c7b8d2a5349..eefab487413da 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -252,6 +252,7 @@ class ParseTreeDumper {
   NODE(parser, ComputedGotoStmt)
   NODE(parser, ConcurrentControl)
   NODE(parser, ConcurrentHeader)
+  NODE(parser, ConditionalExpr)
   NODE(parser, ConnectSpec)
   NODE(ConnectSpec, CharExpr)
   NODE_ENUM(ConnectSpec::CharExpr, Kind)

diff  --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a0106cac84620..960ebbcc99efb 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1678,6 +1678,17 @@ struct ImageSelector {
   std::tuple<std::list<Cosubscript>, std::list<ImageSelectorSpec>> t;
 };
 
+// F2023 R1002 conditional-expr ->
+//   ( scalar-logical-expr ? expr
+//     [ : scalar-logical-expr ? expr ]...
+//     : expr )
+struct ConditionalExpr {
+  TUPLE_CLASS_BOILERPLATE(ConditionalExpr);
+  std::tuple<ScalarLogicalExpr, common::Indirection<Expr>,
+      common::Indirection<Expr>>
+      t;
+};
+
 // R1001 - R1022 expressions
 struct Expr {
   UNION_CLASS_BOILERPLATE(Expr);
@@ -1776,11 +1787,12 @@ struct Expr {
   CharBlock source;
 
   std::variant<common::Indirection<CharLiteralConstantSubstring>,
-      LiteralConstant, common::Indirection<Designator>, ArrayConstructor,
-      StructureConstructor, common::Indirection<FunctionReference>, Parentheses,
-      UnaryPlus, Negate, NOT, PercentLoc, DefinedUnary, Power, Multiply, Divide,
-      Add, Subtract, Concat, LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV,
-      DefinedBinary, ComplexConstructor, common::Indirection<SubstringInquiry>>
+      LiteralConstant, ConditionalExpr, common::Indirection<Designator>,
+      ArrayConstructor, StructureConstructor,
+      common::Indirection<FunctionReference>, Parentheses, UnaryPlus, Negate,
+      NOT, PercentLoc, DefinedUnary, Power, Multiply, Divide, Add, Subtract,
+      Concat, LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, DefinedBinary,
+      ComplexConstructor, common::Indirection<SubstringInquiry>>
       u;
 };
 

diff  --git a/flang/include/flang/Semantics/dump-expr.h b/flang/include/flang/Semantics/dump-expr.h
index 8cbb78b585f4a..d79a294258ff1 100644
--- a/flang/include/flang/Semantics/dump-expr.h
+++ b/flang/include/flang/Semantics/dump-expr.h
@@ -201,6 +201,19 @@ class DumpEvaluateExpr {
     Show(op.right());
     Outdent();
   }
+  template <typename T> void Show(const evaluate::ConditionalExpr<T> &x) {
+    Indent("conditional expr "s + std::string(TypeOf<T>::name));
+    Indent("condition");
+    Show(x.condition());
+    Outdent();
+    Indent("then");
+    Show(x.thenValue());
+    Outdent();
+    Indent("else");
+    Show(x.elseValue());
+    Outdent();
+    Outdent();
+  }
   void Show(const evaluate::Relational<evaluate::SomeType> &x);
   template <typename T> void Show(const evaluate::Expr<T> &x) {
     Indent("expr <"s + std::string(TypeOf<T>::name) + ">"s);

diff  --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index e2817ea542131..f93b9a892715a 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -169,6 +169,7 @@ class ExpressionAnalyzer {
   MaybeExpr Analyze(const parser::DataStmtValue &);
   MaybeExpr Analyze(const parser::AllocateObject &);
   MaybeExpr Analyze(const parser::PointerObject &);
+  MaybeExpr Analyze(const parser::ConditionalExpr &);
 
   template <typename A> MaybeExpr Analyze(const common::Indirection<A> &x) {
     return Analyze(x.value());

diff  --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index e73a4d82951af..be7db0f20821d 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -112,6 +112,19 @@ class IsConstantExprHelper
     return result;
   }
 
+  template <typename T> bool operator()(const ConditionalExpr<T> &x) const {
+    // A conditional expression is a primary.  Therefore, only the selected
+    // branch must be constant. If the condition is a constant expression
+    // whose value cannot yet be determined, both branches must be constant.
+    if (!(*this)(x.condition())) {
+      return false;
+    } else if (auto condVal{ToLogical(x.condition())}) {
+      return *condVal ? (*this)(x.thenValue()) : (*this)(x.elseValue());
+    } else {
+      return (*this)(x.thenValue()) && (*this)(x.elseValue());
+    }
+  }
+
 private:
   bool IsConstantStructureConstructorComponent(
       const Symbol &, const Expr<SomeType> &) const;
@@ -358,6 +371,10 @@ class IsInitialDataTargetHelper
   bool operator()(const Operation<D, R, O...> &) const {
     return false;
   }
+  template <typename T> bool operator()(const ConditionalExpr<T> &) const {
+    // A conditional expression cannot be an initial data target
+    return false;
+  }
   template <typename T> bool operator()(const Parentheses<T> &x) const {
     return (*this)(x.left());
   }
@@ -1193,6 +1210,12 @@ class IsContiguousHelper
 
   Result operator()(const NullPointer &) const { return true; }
 
+  template <typename T> Result operator()(const ConditionalExpr<T> &x) {
+    // Conditional expressions are never variables; expression results are
+    // always contiguous.
+    return true;
+  }
+
 private:
   // Returns "true" for a provably empty or simply contiguous array section;
   // return "false" for a provably nonempty discontiguous section or for use
@@ -1760,6 +1783,12 @@ class CollectUsedSymbolValuesHelper
     return {}; // doesn't count as a use
   }
 
+  template <typename T> Result operator()(const ConditionalExpr<T> &condExpr) {
+    auto restorer{common::ScopedSet(isDefinition_, false)};
+    return Combine((*this)(condExpr.condition()),
+        Combine((*this)(condExpr.thenValue()), (*this)(condExpr.elseValue())));
+  }
+
 private:
   static bool IsBindingUsedAsProcedure(const Expr<SomeType> &expr) {
     if (const auto *pd{std::get_if<ProcedureDesignator>(&expr.u)}) {

diff  --git a/flang/lib/Evaluate/expression.cpp b/flang/lib/Evaluate/expression.cpp
index 759fe5bc71b69..128c39b3eb004 100644
--- a/flang/lib/Evaluate/expression.cpp
+++ b/flang/lib/Evaluate/expression.cpp
@@ -64,6 +64,16 @@ Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
             }
             return std::nullopt;
           },
+          [](const ConditionalExpr<Result> &x) -> T {
+            if (auto tlen{x.thenValue().LEN()}) {
+              if (auto elen{x.elseValue().LEN()}) {
+                if (*tlen == *elen) {
+                  return tlen;
+                }
+              }
+            }
+            return std::nullopt;
+          },
           [](const Designator<Result> &dr) { return dr.LEN(); },
           [](const FunctionRef<Result> &fr) { return fr.LEN(); },
           [](const SetLength<KIND> &x) -> T { return x.right(); },
@@ -141,6 +151,12 @@ template <typename A> bool Extremum<A>::operator==(const Extremum &that) const {
   return ordering == that.ordering && Base::operator==(that);
 }
 
+template <typename A>
+bool ConditionalExpr<A>::operator==(const ConditionalExpr &that) const {
+  return condition_ == that.condition_ && thenValue_ == that.thenValue_ &&
+      elseValue_ == that.elseValue_;
+}
+
 template <int KIND>
 bool LogicalOperation<KIND>::operator==(const LogicalOperation &that) const {
   return logicalOperator == that.logicalOperator && Base::operator==(that);

diff  --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 529e3a9ad5a08..d4d7f2b705b3d 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -143,6 +143,8 @@ Expr<ImpliedDoIndex::Result> FoldOperation(
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &, ArrayConstructor<T> &&);
 Expr<SomeDerived> FoldOperation(FoldingContext &, StructureConstructor &&);
+template <typename T>
+Expr<T> FoldOperation(FoldingContext &, ConditionalExpr<T> &&);
 
 template <typename T>
 std::optional<Constant<T>> Folder<T>::GetNamedConstant(const Symbol &symbol0) {
@@ -2211,6 +2213,17 @@ Expr<T> FoldOperation(FoldingContext &context, RealToIntPower<T> &&x) {
       x.right().u);
 }
 
+template <typename T>
+Expr<T> FoldOperation(FoldingContext &context, ConditionalExpr<T> &&x) {
+  x.condition() = Fold(context, std::move(x.condition()));
+  // If the condition is a scalar logical constant, select the branch.
+  if (auto cst{GetScalarConstantValue<LogicalResult>(x.condition())}) {
+    return cst->IsTrue() ? Fold(context, std::move(x.thenValue()))
+                         : Fold(context, std::move(x.elseValue()));
+  }
+  return Expr<T>{std::move(x)};
+}
+
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
   if (auto array{ApplyElementwise(context, x,

diff  --git a/flang/lib/Evaluate/formatting.cpp b/flang/lib/Evaluate/formatting.cpp
index 5632015857ab3..09cb8b08dda81 100644
--- a/flang/lib/Evaluate/formatting.cpp
+++ b/flang/lib/Evaluate/formatting.cpp
@@ -587,6 +587,29 @@ llvm::raw_ostream &ArrayConstructor<SomeDerived>::AsFortran(
   return o << ']';
 }
 
+template <typename T>
+llvm::raw_ostream &ConditionalExpr<T>::AsFortran(llvm::raw_ostream &o) const {
+  // Iterate over chained else-branches to avoid adding extra parentheses for
+  // chained conditional expressions.
+  o << '(';
+  const ConditionalExpr<T> *node{this};
+  while (true) {
+    node->condition().AsFortran(o);
+    o << " ? ";
+    node->thenValue().AsFortran(o);
+    o << " : ";
+    // Continue chain for nested ConditionalExpr; else emit terminal value.
+    if (const auto *nested{
+            std::get_if<ConditionalExpr<T>>(&node->elseValue().u)}) {
+      node = nested;
+    } else {
+      node->elseValue().AsFortran(o);
+      break;
+    }
+  }
+  return o << ')';
+}
+
 template <typename RESULT>
 std::string ExpressionBase<RESULT>::AsFortran() const {
   std::string buf;

diff  --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index e931f93d91b38..8aff09aa84c13 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1195,6 +1195,9 @@ struct HasVectorSubscriptHelper
   bool operator()(const ProcedureRef &) const {
     return false; // don't descend into function call arguments
   }
+  template <typename T> bool operator()(const ConditionalExpr<T> &) const {
+    return false; // not a variable designator
+  }
 };
 
 bool HasVectorSubscript(const Expr<SomeType> &expr) {
@@ -1736,6 +1739,14 @@ struct ArgumentExtractor
     return {operation::OperationCode(x), {AsSomeExpr(x)}};
   }
 
+  template <typename T> Result operator()(const ConditionalExpr<T> &x) const {
+    // Return the condition and then/else branches as immediate operands;
+    // nested conditionals are not permitted in an OpenMP atomic context.
+    return {Operator::Conditional,
+        {AsSomeExpr(x.condition()), AsSomeExpr(x.thenValue()),
+            AsSomeExpr(x.elseValue())}};
+  }
+
   template <typename... Rs>
   Result Combine(Result &&result, Rs &&...results) const {
     // There shouldn't be any combining needed, since we're stopping the
@@ -1773,6 +1784,8 @@ std::string operation::ToString(operation::Operator op) {
     return "ASSOCIATED";
   case Operator::Call:
     return "function-call";
+  case Operator::Conditional:
+    return "conditional";
   case Operator::Constant:
     return "constant";
   case Operator::Convert:
@@ -1901,6 +1914,13 @@ struct ConvertCollector
     }
   }
 
+  template <typename T> Result operator()(const ConditionalExpr<T> &x) const {
+    // ConvertCollector tracks the typed-value conversion chain (for OMP ATOMIC
+    // validation); the condition is a LOGICAL(4) selector, not a value output,
+    // so only the value branches are collected.
+    return Combine((*this)(x.thenValue()), (*this)(x.elseValue()));
+  }
+
   template <typename... Rs>
   Result Combine(Result &&result, Rs &&...results) const {
     Result v(std::move(result));

diff  --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index a7e0239d335fd..32cd710e9b5b4 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -927,6 +927,11 @@ class ScalarExprLowering {
     return builder.createNullConstant(getLoc());
   }
 
+  template <typename A>
+  ExtValue genval(const Fortran::evaluate::ConditionalExpr<A> &) {
+    fir::emitFatalError(getLoc(), "ConditionalExpr should be lowered to HLFIR");
+  }
+
   static bool
   isDerivedTypeWithLenParameters(const Fortran::semantics::Symbol &sym) {
     if (const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType())
@@ -5366,6 +5371,11 @@ class ArrayExprLowering {
     };
   }
 
+  template <typename A>
+  CC genarr(const Fortran::evaluate::ConditionalExpr<A> &) {
+    fir::emitFatalError(getLoc(), "ConditionalExpr should be lowered to HLFIR");
+  }
+
   template <typename T>
   CC genarr(const Fortran::evaluate::Constant<T> &x) {
     if (x.Rank() == 0)

diff  --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index 0c015bc9a2f1b..7ddd09e59c262 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1788,6 +1788,12 @@ 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();

diff  --git a/flang/lib/Lower/IterationSpace.cpp b/flang/lib/Lower/IterationSpace.cpp
index 203fec508f795..52a15223bc1e6 100644
--- a/flang/lib/Lower/IterationSpace.cpp
+++ b/flang/lib/Lower/IterationSpace.cpp
@@ -212,6 +212,14 @@ class ArrayBaseFinder {
     (void)find(op.right());
     return false;
   }
+  template <typename T>
+  RT find(const Fortran::evaluate::ConditionalExpr<T> &x) {
+    // Find array bases in condition and values
+    (void)find(x.condition());
+    (void)find(x.thenValue());
+    (void)find(x.elseValue());
+    return {};
+  }
   RT find(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x) {
     (void)find(x.u);
     return {};

diff  --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 384636a659875..280968975ea96 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -158,6 +158,11 @@ class HashEvaluateExpr {
            static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) +
            static_cast<unsigned>(x.ordering) * 7u;
   }
+  template <typename T>
+  static unsigned getHashValue(const Fortran::evaluate::ConditionalExpr<T> &x) {
+    return getHashValue(x.condition()) * 151u -
+           getHashValue(x.thenValue()) * 3u + getHashValue(x.elseValue());
+  }
   template <Fortran::common::TypeCategory TC, int KIND>
   static unsigned getHashValue(
       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
@@ -416,6 +421,13 @@ class IsEqualEvaluateExpr {
                       const Fortran::evaluate::Extremum<A> &y) {
     return isBinaryEqual(x, y);
   }
+  template <typename T>
+  static bool isEqual(const Fortran::evaluate::ConditionalExpr<T> &x,
+                      const Fortran::evaluate::ConditionalExpr<T> &y) {
+    return isEqual(x.condition(), y.condition()) &&
+           isEqual(x.thenValue(), y.thenValue()) &&
+           isEqual(x.elseValue(), y.elseValue());
+  }
   template <typename A>
   static bool isEqual(const Fortran::evaluate::RealToIntPower<A> &x,
                       const Fortran::evaluate::RealToIntPower<A> &y) {

diff  --git a/flang/lib/Parser/expr-parsers.cpp b/flang/lib/Parser/expr-parsers.cpp
index b6832a7999c5b..db29b15a1fa05 100644
--- a/flang/lib/Parser/expr-parsers.cpp
+++ b/flang/lib/Parser/expr-parsers.cpp
@@ -61,15 +61,50 @@ TYPE_PARSER(parenthesized(
 TYPE_PARSER(construct<AcImpliedDoControl>(
     maybe(integerTypeSpec / "::"), loopBounds(scalarIntExpr)))
 
+// Conditional expression lookahead helper: checks if input starting with '('
+// contains '?' at nesting level 1. This avoids exponential backtracking when
+// parsing deeply nested parentheses that are not conditional expressions.
+struct ConditionalExprLookahead {
+  using resultType = Success;
+  constexpr ConditionalExprLookahead() {}
+  std::optional<Success> Parse(ParseState &state) const {
+    ParseState scan{state};
+    if (!attempt("("_tok).Parse(scan)) {
+      return std::nullopt;
+    }
+    int nestLevel{1};
+    while (!scan.IsAtEnd()) {
+      if (attempt(charLiteralConstant).Parse(scan)) {
+        // Skip character literals; don't check contents.
+      } else if (attempt("("_tok).Parse(scan)) {
+        ++nestLevel;
+      } else if (attempt(")"_tok).Parse(scan)) {
+        if (--nestLevel == 0) {
+          return std::nullopt;
+        }
+      } else if (attempt("?"_tok).Parse(scan)) {
+        if (nestLevel == 1) {
+          return {Success{}};
+        }
+      } else {
+        scan.UncheckedAdvance();
+      }
+    }
+    return std::nullopt;
+  }
+};
+
 // R1001 primary ->
 //         literal-constant | designator | array-constructor |
 //         structure-constructor | function-reference | type-param-inquiry |
-//         type-param-name | ( expr )
+//         type-param-name | ( expr ) | conditional-expr
 // type-param-inquiry is parsed as a structure component, except for
 // substring%KIND/LEN
 constexpr auto primary{instrumented("primary"_en_US,
     first(construct<Expr>(indirect(charLiteralConstantSubstring)),
         construct<Expr>(literalConstant),
+        construct<Expr>(ConditionalExprLookahead{} >>
+            parenthesized(Parser<ConditionalExpr>{})),
         construct<Expr>(construct<Expr::Parentheses>("(" >>
             expr / !","_tok / recovery(")"_tok, SkipPastNested<'(', ')'>{}))),
         construct<Expr>(indirect(functionReference) / !"("_tok / !"%"_tok),
@@ -94,6 +129,17 @@ constexpr auto level1Expr{sourced(
     primary || // must come before define op to resolve .TRUE._8 ambiguity
     construct<Expr>(construct<Expr::DefinedUnary>(definedOpName, primary)))};
 
+// F2023 R1002 conditional-expr ->
+//   ( scalar-logical-expr ? expr
+//     [ : scalar-logical-expr ? expr ]...
+//     : expr )
+// The chained list form is encoded as a right-associative tree: the else-expr
+// is either a chained conditional-expr (which need not be separately
+// parenthesized) or a terminal expr.
+TYPE_PARSER(
+    construct<ConditionalExpr>(scalarLogicalExpr / "?", indirect(expr) / ":",
+        indirect(construct<Expr>(Parser<ConditionalExpr>{}) || expr)))
+
 // R1004 mult-operand -> level-1-expr [power-op mult-operand]
 // R1007 power-op -> **
 // Exponentiation (**) is Fortran's only right-associative binary operation.

diff  --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index c31eac0b3ff68..5ddd0cfc3a1ef 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -900,6 +900,17 @@ class UnparseVisitor {
   void Unparse(const Expr::OR &x) { Walk(x.t, ".OR."); }
   void Unparse(const Expr::EQV &x) { Walk(x.t, ".EQV."); }
   void Unparse(const Expr::NEQV &x) { Walk(x.t, ".NEQV."); }
+  void Unparse(const ConditionalExpr &x) { // F2023 R1002
+    // Note: chained conditionals produce extra parentheses due to recursive
+    // else-expr unparsing; the result is still valid.
+    Put("( ");
+    Walk(std::get<0>(x.t)); // scalar-logical-expr
+    Put(" ? ");
+    Walk(std::get<1>(x.t)); // then-expr
+    Put(" : ");
+    Walk(std::get<2>(x.t)); // else-expr
+    Put(" )");
+  }
   void Unparse(const Expr::ComplexConstructor &x) {
     Put('('), Walk(x.t, ","), Put(')');
   }

diff  --git a/flang/lib/Semantics/definable.cpp b/flang/lib/Semantics/definable.cpp
index fdb4a2dcf8808..a82ca80ddd1b8 100644
--- a/flang/lib/Semantics/definable.cpp
+++ b/flang/lib/Semantics/definable.cpp
@@ -316,6 +316,10 @@ class DuplicatedSubscriptFinder
     }
     return anyVector ? false : (*this)(aRef.base());
   }
+  template <typename T> bool operator()(const evaluate::ConditionalExpr<T> &) {
+    // A conditional expression is not a variable and cannot be definable.
+    return false;
+  }
 
 private:
   evaluate::FoldingContext &foldingContext_;

diff  --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 97e9bd39f0630..bd4e2a44df8ae 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3878,6 +3878,111 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) {
   return MakeFunctionRef(loc, ActualArguments{std::move(*arg)});
 }
 
+MaybeExpr ExpressionAnalyzer::Analyze(const parser::ConditionalExpr &x) {
+  // Chained else-expressions recurse automatically through Analyze(Expr).
+  MaybeExpr condExpr{Analyze(std::get<0>(x.t))};
+  MaybeExpr thenExpr{Analyze(std::get<1>(x.t).value())};
+  MaybeExpr elseExpr{Analyze(std::get<2>(x.t).value())};
+  if (!condExpr || !thenExpr || !elseExpr) {
+    return std::nullopt;
+  }
+  if (std::holds_alternative<BOZLiteralConstant>(thenExpr->u) ||
+      std::holds_alternative<BOZLiteralConstant>(elseExpr->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 (IsNullPointerOrAllocatable(&*thenExpr) ||
+      IsNullPointerOrAllocatable(&*elseExpr)) {
+    Say("NULL() not allowed in a conditional expression"_err_en_US);
+    return std::nullopt;
+  }
+  if (semantics::IsAssumedRank(*thenExpr) ||
+      semantics::IsAssumedRank(*elseExpr)) {
+    Say("An assumed-rank dummy argument may not be used as a value in a conditional expression"_err_en_US);
+    return std::nullopt;
+  }
+  if ((ExtractDataRef(thenExpr) &&
+          ExtractCoarrayRef(*ExtractDataRef(thenExpr))) ||
+      (ExtractDataRef(elseExpr) &&
+          ExtractCoarrayRef(*ExtractDataRef(elseExpr)))) {
+    Say("Conditional expression values may not be coindexed"_err_en_US);
+    return std::nullopt;
+  }
+  // F2023 C1004: then-expr and else-expr must have the same declared type,
+  // kind type parameters, and rank.
+  if (thenExpr->Rank() != elseExpr->Rank()) {
+    Say("All values in conditional expression must have the same rank; have rank %d and %d"_err_en_US,
+        thenExpr->Rank(), elseExpr->Rank());
+    return std::nullopt;
+  }
+  const std::optional<DynamicType> thenType{thenExpr->GetType()};
+  const std::optional<DynamicType> elseType{elseExpr->GetType()};
+  if (!thenType || !elseType) {
+    Say("Cannot determine type of conditional expression"_err_en_US);
+    return std::nullopt;
+  }
+  const TypeCategory thenCat{thenType->category()};
+  const TypeCategory elseCat{elseType->category()};
+  if (thenCat != elseCat ||
+      (thenCat != TypeCategory::Derived &&
+          thenType->kind() != elseType->kind())) {
+    Say("All values in conditional expression must have the same type and kind; have %s and %s"_err_en_US,
+        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;
+  }
+
+  // Dispatch on the else-expr to recover the concrete kind type T.
+  return common::visit(
+      common::visitors{
+          [&](Expr<SomeDerived> &&elseVal) -> MaybeExpr {
+            Expr<LogicalResult> cond{ConvertToType<LogicalResult>(
+                std::move(std::get<Expr<SomeLogical>>(condExpr->u)))};
+            Expr<SomeDerived> thenVal{
+                std::move(std::get<Expr<SomeDerived>>(thenExpr->u))};
+            return AsGenericExpr(
+                Expr<SomeDerived>{evaluate::ConditionalExpr<SomeDerived>{
+                    std::move(cond), std::move(thenVal), std::move(elseVal)}});
+          },
+          [&](auto &&elseCatExpr) -> MaybeExpr {
+            using CategoryType = std::decay_t<decltype(elseCatExpr)>;
+            if constexpr (std::is_same_v<CategoryType, BOZLiteralConstant> ||
+                std::is_same_v<CategoryType, NullPointer> ||
+                std::is_same_v<CategoryType, ProcedureDesignator> ||
+                std::is_same_v<CategoryType, ProcedureRef>) {
+              DIE("Invalid expression type in conditional expression");
+            } else {
+              return common::visit(
+                  [&](auto &&elseKindExpr) -> MaybeExpr {
+                    using T =
+                        typename std::decay_t<decltype(elseKindExpr)>::Result;
+                    Expr<LogicalResult> cond{ConvertToType<LogicalResult>(
+                        std::move(std::get<Expr<SomeLogical>>(condExpr->u)))};
+                    Expr<T> thenVal{std::move(std::get<Expr<T>>(
+                        std::get<CategoryType>(thenExpr->u).u))};
+                    return AsGenericExpr(CategoryType{
+                        Expr<T>{evaluate::ConditionalExpr<T>{std::move(cond),
+                            std::move(thenVal), std::move(elseKindExpr)}}});
+                  },
+                  elseCatExpr.u);
+            }
+          },
+      },
+      std::move(elseExpr->u));
+}
+
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
   const auto &name{std::get<parser::DefinedOpName>(x.t).v};
   ArgumentAnalyzer analyzer{*this, name.source};
@@ -5142,6 +5247,12 @@ std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
     }
     context_.SayAt(expr.source,
         "TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
+  } else if (isProcedureCall_ &&
+      std::holds_alternative<parser::ConditionalExpr>(expr.u)) {
+    // Check parse tree before analysis to avoid wasted work
+    context_.SayAt(expr.source,
+        "Conditional expressions are not yet supported as actual arguments"_todo_en_US);
+    return std::nullopt;
   } else if (MaybeExpr argExpr{AnalyzeExprOrWholeAssumedSizeArray(expr)}) {
     if (isProcedureCall_ || !IsProcedureDesignator(*argExpr)) {
       // Pad Hollerith actual argument with spaces up to a multiple of 8

diff  --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index f183510668449..52bd66eae1dfa 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -329,6 +329,12 @@ struct LogicalConstantVistor : public evaluate::Traverse<LogicalConstantVistor,
       }
     }
   }
+
+  template <typename T>
+  Result operator()(const evaluate::ConditionalExpr<T> &) const {
+    // A conditional expression is not treated as a constant logical value.
+    return std::nullopt;
+  }
 };
 } // namespace
 

diff  --git a/flang/test/Evaluate/fold-conditional-expr.f90 b/flang/test/Evaluate/fold-conditional-expr.f90
new file mode 100644
index 0000000000000..0240859cae9ff
--- /dev/null
+++ b/flang/test/Evaluate/fold-conditional-expr.f90
@@ -0,0 +1,42 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Tests folding of conditional expressions (Fortran 2023)
+module m
+  ! Basic scalar folding: constant condition selects the chosen branch.
+  logical, parameter :: test_true_int  = (.true.  ? 1 : 2) == 1
+  logical, parameter :: test_false_int = (.false. ? 1 : 2) == 2
+  logical, parameter :: test_true_real  = (.true.  ? 1.0 : 2.0) == 1.0
+  logical, parameter :: test_false_real = (.false. ? 1.0 : 2.0) == 2.0
+  logical, parameter :: test_true_logical  = (.true.  ? .true.  : .false.)
+  logical, parameter :: test_false_logical = (.false. ? .false. : .true.)
+
+  ! Multi-branch: right-skewed tree folds correctly.
+  ! (.true. ? 10 : .false. ? 20 : 30) == 10
+  logical, parameter :: test_multi_first  = (.true.  ? 10 : .false. ? 20 : 30) == 10
+  ! (.false. ? 10 : .true.  ? 20 : 30) == 20
+  logical, parameter :: test_multi_second = (.false. ? 10 : .true.  ? 20 : 30) == 20
+  ! (.false. ? 10 : .false. ? 20 : 30) == 30
+  logical, parameter :: test_multi_third  = (.false. ? 10 : .false. ? 20 : 30) == 30
+
+  ! Named constant expressions in branches are folded.
+  integer, parameter :: x = 5
+  logical, parameter :: test_branch_fold = (.true. ? x + 1 : x + 2) == 6
+
+  ! Named constant as condition.
+  logical, parameter :: cond = .true.
+  logical, parameter :: test_named_cond = (cond ? 42 : 0) == 42
+
+  ! Character: constant condition selects the branch value.
+  logical, parameter :: test_char = (.true. ? 'yes' : 'no') == 'yes'
+
+  ! Non-constant branch: only the selected branch need be constant (F2023 10.1.12).
+  integer :: non_const = 99
+  logical, parameter :: test_true_const_else_nonconstant  = (.true.  ? 10 : non_const) == 10
+  logical, parameter :: test_false_const_then_nonconstant = (.false. ? non_const : 10) == 10
+
+  ! Named constant condition with a non-constant branch.
+  logical, parameter :: flag = .true.
+  logical, parameter :: test_named_cond_nonconstant = (flag ? 1 : non_const) == 1
+  logical, parameter :: flag_false = .false.
+  logical, parameter :: test_named_cond_false_nonconstant = (flag_false ? non_const : 1) == 1
+
+end module

diff  --git a/flang/test/Parser/conditional-expr.f90 b/flang/test/Parser/conditional-expr.f90
new file mode 100644
index 0000000000000..f16472f486d42
--- /dev/null
+++ b/flang/test/Parser/conditional-expr.f90
@@ -0,0 +1,286 @@
+! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s -check-prefix=TREE
+
+! Test parsing of conditional expressions (Fortran 2023 R1002)
+
+! Simple two-branch conditional
+subroutine simple_conditional(x, y, z)
+  integer :: x, y, z
+  ! CHECK-LABEL: simple_conditional
+  ! CHECK: z = ( x>5 ? y : 10 )
+  ! TREE: ConditionalExpr
+  ! TREE-NEXT: Scalar -> Logical -> Expr
+  ! TREE: Expr -> Designator -> DataRef -> Name = 'y'
+  ! TREE: Expr -> LiteralConstant -> IntLiteralConstant = '10'
+  z = (x > 5 ? y : 10)
+end subroutine
+
+! Three-branch conditional (multiple conditions)
+subroutine multi_branch_conditional(x, y, z)
+  integer :: x, y, z
+  ! CHECK-LABEL: multi_branch_conditional
+  ! CHECK: z = ( x>10 ? 100 : ( y<5 ? 50 : 0 ) )
+  ! TREE: ConditionalExpr
+  ! TREE-NEXT: Scalar -> Logical -> Expr
+  ! TREE: Expr -> LiteralConstant -> IntLiteralConstant = '100'
+  ! TREE: Expr -> ConditionalExpr
+  ! TREE: Scalar -> Logical -> Expr
+  ! TREE: Expr -> LiteralConstant -> IntLiteralConstant = '50'
+  ! TREE: Expr -> LiteralConstant -> IntLiteralConstant = '0'
+  z = (x > 10 ? 100 : y < 5 ? 50 : 0)
+end subroutine
+
+! Nested conditionals
+subroutine nested_conditionals(x, y, w, z, flag1, flag2)
+  integer :: x, y, w, z
+  logical :: flag1, flag2
+  ! CHECK-LABEL: nested_conditionals
+  ! Nested in value position
+  ! CHECK: z = ( flag1 ? ( x>y ? x : y ) : 0 )
+  ! TREE: ConditionalExpr
+  ! TREE-NEXT: Scalar -> Logical -> Expr
+  ! TREE: Expr -> ConditionalExpr
+  ! TREE: Scalar -> Logical -> Expr
+  ! TREE: Expr -> Designator -> DataRef -> Name = 'x'
+  ! TREE: Expr -> Designator -> DataRef -> Name = 'y'
+  ! TREE: Expr -> LiteralConstant -> IntLiteralConstant = '0'
+  z = (flag1 ? (x > y ? x : y) : 0)
+  ! Nested in condition
+  ! CHECK: z = ( ( x>5 ? flag1 : flag2 ) ? y : 10 )
+  z = ((x > 5 ? flag1 : flag2) ? y : 10)
+  ! Multiple nested
+  ! CHECK: z = ( x>10 ? ( y>20 ? 1 : 2 ) : ( w>30 ? 3 : 4 ) )
+  z = (x > 10 ? (y > 20 ? 1 : 2) : (w > 30 ? 3 : 4))
+end subroutine
+
+! Basic type conditionals
+subroutine basic_types(x, a, b, c, flag1, str1)
+  integer :: x
+  real :: a, b, c
+  logical :: flag1
+  character(len=10) :: str1
+  ! CHECK-LABEL: basic_types
+  ! Real type
+  ! CHECK: c = ( a>b ? a : b )
+  c = (a > b ? a : b)
+  ! Logical type
+  ! CHECK: flag1 = ( x>5 ? .TRUE. : .FALSE. )
+  flag1 = (x > 5 ? .true. : .false.)
+  ! Character type
+  ! CHECK: str1 = ( flag1 ? "HELLO" : "WORLD" )
+  str1 = (flag1 ? "HELLO" : "WORLD")
+end subroutine
+
+! Complex expressions in conditions and branches
+subroutine complex_expressions(x, y, z, flag1)
+  integer :: x, y, z
+  logical :: flag1
+  ! CHECK-LABEL: complex_expressions
+  ! Complex expressions in branches
+  ! CHECK: z = ( x>y ? x*2+1 : y*3-2 )
+  z = (x > y ? x*2+1 : y*3-2)
+  ! Complex logical condition
+  ! CHECK: z = ( x>5.AND.y<10 ? x+y : x-y )
+  z = (x > 5 .and. y < 10 ? x+y : x-y)
+  ! Logical NOT
+  ! CHECK: z = ( .NOT.flag1 ? x : y )
+  z = (.not. flag1 ? x : y)
+  ! Comparison chains
+  ! CHECK: z = ( x>5.AND.x<10 ? x : 0 )
+  z = (x > 5 .and. x < 10 ? x : 0)
+  ! Parenthesized expressions in branches
+  ! CHECK: z = ( x>5 ? (y+z) : (y-z) )
+  z = (x > 5 ? (y+z) : (y-z))
+end subroutine
+
+! Many-branch conditionals
+subroutine many_branches(x, z)
+  integer :: x, z
+  ! CHECK-LABEL: many_branches
+  ! Four branches
+  ! CHECK: z = ( x>10 ? 100 : ( x>5 ? 50 : ( x>0 ? 10 : 0 ) ) )
+  z = (x > 10 ? 100 : x > 5 ? 50 : x > 0 ? 10 : 0)
+  ! Five branches
+  ! CHECK: z = ( x>20 ? 1 : ( x>15 ? 2 : ( x>10 ? 3 : ( x>5 ? 4 : 5 ) ) ) )
+  z = (x > 20 ? 1 : x > 15 ? 2 : x > 10 ? 3 : x > 5 ? 4 : 5)
+end subroutine
+
+! Conditionals with arrays and functions
+subroutine arrays_and_functions(x, y, z, arr, flag1)
+  integer :: x, y, z, arr(5)
+  logical :: flag1
+  ! CHECK-LABEL: arrays_and_functions
+  ! Array element in conditional
+  ! CHECK: z = ( arr(1)>arr(2) ? arr(1) : arr(2) )
+  z = (arr(1) > arr(2) ? arr(1) : arr(2))
+  ! Function calls in conditional
+  ! CHECK: x = ( abs(y)>10 ? abs(y) : y )
+  x = (abs(y) > 10 ? abs(y) : y)
+  ! Array constructor elements
+  ! CHECK: arr(1:3) = [( flag1 ? x : y ), ( .NOT.flag1 ? x : y ), ( x>y ? x : y )]
+  arr(1:3) = [(flag1 ? x : y), (.not. flag1 ? x : y), (x > y ? x : y)]
+end subroutine
+
+! Literals in conditionals
+subroutine literals(x, z, a, c)
+  integer :: x, z
+  real :: a, c
+  ! CHECK-LABEL: literals
+  ! Real literals
+  ! CHECK: c = ( a>0.0 ? 1.5 : 2.5 )
+  c = (a > 0.0 ? 1.5 : 2.5)
+  ! Negative values
+  ! CHECK: z = ( x<0 ? -1 : 1 )
+  z = (x < 0 ? -1 : 1)
+end subroutine
+
+! Conditional in specification expression context
+function spec_expr_conditional(n, flag) result(res)
+  integer, intent(in) :: n
+  logical, intent(in) :: flag
+  integer :: res
+  ! CHECK-LABEL: spec_expr_conditional
+  ! CHECK: res = ( flag ? n*2 : n )
+  res = (flag ? n*2 : n)
+end function
+
+! Conditional with 
diff erent integer kinds
+subroutine integer_kinds(cond)
+  integer(kind=4) :: i4a, i4b, i4c
+  integer(kind=8) :: i8a, i8b, i8c
+  logical :: cond
+  ! CHECK-LABEL: integer_kinds
+  ! CHECK: i4c = ( cond ? i4a : i4b )
+  i4c = (cond ? i4a : i4b)
+  ! CHECK: i8c = ( cond ? i8a : i8b )
+  i8c = (cond ? i8a : i8b)
+end subroutine
+
+! Conditional with 
diff erent real kinds
+subroutine real_kinds(cond)
+  real(kind=4) :: r4a, r4b, r4c
+  real(kind=8) :: r8a, r8b, r8c
+  logical :: cond
+  ! CHECK-LABEL: real_kinds
+  ! CHECK: r4c = ( cond ? r4a : r4b )
+  r4c = (cond ? r4a : r4b)
+  ! CHECK: r8c = ( cond ? r8a : r8b )
+  r8c = (cond ? r8a : r8b)
+end subroutine
+
+! Conditional in various statement contexts
+subroutine statement_contexts(flag)
+  integer :: x, y, arr(10)
+  logical :: flag
+  ! CHECK-LABEL: statement_contexts
+  ! In array constructor
+  ! CHECK: arr(1:3) = [1, ( flag ? x : y ), 3]
+  arr(1:3) = [1, (flag ? x : y), 3]
+  ! In if statement condition
+  ! CHECK: IF (( flag ? x : y )>5) THEN
+  if ((flag ? x : y) > 5) then
+    x = 1
+  end if
+  ! In print statement
+  ! CHECK: PRINT *, ( flag ? x : y )
+  print *, (flag ? x : y)
+  ! In assignment to array element
+  ! CHECK: arr(5) = ( flag ? x : y )
+  arr(5) = (flag ? x : y)
+end subroutine
+
+! Complex type conditionals
+subroutine complex_type(flag)
+  complex :: c1, c2, c3
+  complex(kind=8) :: c8a, c8b, c8c
+  logical :: flag
+  ! CHECK-LABEL: complex_type
+  ! CHECK: c3 = ( flag ? c1 : c2 )
+  c3 = (flag ? c1 : c2)
+  ! CHECK: c8c = ( flag ? c8a : c8b )
+  c8c = (flag ? c8a : c8b)
+  ! With complex literals
+  ! CHECK: c3 = ( flag ? (1.0,2.0) : (3.0,4.0) )
+  c3 = (flag ? (1.0, 2.0) : (3.0, 4.0))
+end subroutine
+
+! Array-valued conditionals (F2023 10.1.4)
+subroutine array_valued(flag)
+  integer :: arr1(5), arr2(5), arr3(5)
+  real :: mat1(3,3), mat2(3,3), mat3(3,3)
+  logical :: flag
+  ! CHECK-LABEL: array_valued
+  ! Whole array conditional
+  ! CHECK: arr3 = ( flag ? arr1 : arr2 )
+  ! TREE: ConditionalExpr
+  ! TREE-NEXT: Scalar -> Logical -> Expr
+  ! TREE: Expr -> Designator -> DataRef -> Name = 'arr1'
+  ! TREE: Expr -> Designator -> DataRef -> Name = 'arr2'
+  arr3 = (flag ? arr1 : arr2)
+  ! Multidimensional array conditional
+  ! CHECK: mat3 = ( flag ? mat1 : mat2 )
+  mat3 = (flag ? mat1 : mat2)
+  ! Array section conditional
+  ! CHECK: arr3(1:3) = ( flag ? arr1(1:3) : arr2(1:3) )
+  arr3(1:3) = (flag ? arr1(1:3) : arr2(1:3))
+end subroutine
+
+! Derived type conditionals
+subroutine derived_types(flag)
+  type :: point
+    real :: x, y
+  end type
+  type(point) :: p1, p2, p3
+  logical :: flag
+  ! CHECK-LABEL: derived_types
+  ! CHECK: p3 = ( flag ? p1 : p2 )
+  p3 = (flag ? p1 : p2)
+end subroutine
+
+! Character with 
diff erent lengths
+subroutine character_lengths(flag)
+  character(len=5) :: short1, short2
+  character(len=10) :: medium1, medium2
+  character(len=20) :: long_result
+  logical :: flag
+  ! CHECK-LABEL: character_lengths
+  ! Same length characters
+  ! CHECK: short1 = ( flag ? "HELLO" : "WORLD" )
+  short1 = (flag ? "HELLO" : "WORLD")
+  ! Different length literals (type conformance rules apply)
+  ! CHECK: long_result = ( flag ? "SHORT" : "MUCH LONGER STRING" )
+  long_result = (flag ? "SHORT" : "MUCH LONGER STRING")
+  ! Mixed variables and literals
+  ! CHECK: medium1 = ( flag ? short1 : medium2 )
+  medium1 = (flag ? short1 : medium2)
+end subroutine
+
+! Verify that '?' inside string literals and comments does not interfere
+! with conditional expression parsing.
+subroutine question_mark_chars(flag, str1, str2, str3)
+  logical :: flag
+  character(len=20) :: str1, str2, str3
+  ! CHECK-LABEL: question_mark_chars
+  ! CHECK: str1 = "HELLO?"
+  str1 = "HELLO?"
+  ! CHECK: str2 = ( flag ? "YES?" : "NO?" )
+  str2 = (flag ? "YES?" : "NO?")
+  ! CHECK: str3 = "WHAT? WHY? HOW?"
+  str3 = "WHAT? WHY? HOW?"  ! ? in a comment
+  ! CHECK: str2 = ( flag ? "MAYBE?" : "NOPE" )
+  str2 = (flag ? "MAYBE?" : "NOPE")  ! ? in a trailing comment
+end subroutine
+
+! Verify that '(' and ')' inside character literals are handled correctly by
+! ConditionalExprLookahead.
+subroutine paren_in_char_literal(c, i)
+  character(*), intent(in) :: c
+  integer, intent(out) :: i
+  ! CHECK-LABEL: paren_in_char_literal
+  ! CHECK: i = ( c==")" ? 1 : 2 )
+  i = (c == ")" ? 1 : 2)
+  ! CHECK: i = ( c=="(" ? 1 : 2 )
+  i = (c == "(" ? 1 : 2)
+  ! CHECK: i = ( c=="()" ? 1 : 2 )
+  i = (c == "()" ? 1 : 2)
+end subroutine

diff  --git a/flang/test/Semantics/conditional-expr.f90 b/flang/test/Semantics/conditional-expr.f90
new file mode 100644
index 0000000000000..12fbea7e86488
--- /dev/null
+++ b/flang/test/Semantics/conditional-expr.f90
@@ -0,0 +1,429 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test semantic analysis of conditional expressions (Fortran 2023)
+
+! Valid cases with basic types
+subroutine valid_basic_types(flag)
+  logical :: flag
+  integer :: i1, i2, i3
+  real :: r1, r2, r3
+  complex :: c1, c2, c3
+  logical :: l1, l2, l3
+  character(len=5) :: ch1, ch2, ch3
+
+  ! INTEGER conditionals
+  i3 = (flag ? i1 : i2)
+
+  ! REAL conditionals
+  r3 = (flag ? r1 : r2)
+
+  ! COMPLEX conditionals
+  c3 = (flag ? c1 : c2)
+
+  ! LOGICAL conditionals
+  l3 = (flag ? l1 : l2)
+
+  ! CHARACTER conditionals
+  ch3 = (flag ? ch1 : ch2)
+end subroutine
+
+! Valid cases with same kind
+subroutine valid_same_kind(flag)
+  logical :: flag
+  integer(kind=4) :: i4a, i4b, i4c
+  integer(kind=8) :: i8a, i8b, i8c
+  real(kind=4) :: r4a, r4b, r4c
+  real(kind=8) :: r8a, r8b, r8c
+
+  ! Same kind - valid
+  i4c = (flag ? i4a : i4b)
+  i8c = (flag ? i8a : i8b)
+  r4c = (flag ? r4a : r4b)
+  r8c = (flag ? r8a : r8b)
+end subroutine
+
+! Valid cases with literals
+subroutine valid_literals(flag)
+  logical :: flag
+  integer :: i
+  real :: r
+  character(len=10) :: ch
+
+  i = (flag ? 10 : 20)
+  r = (flag ? 1.0 : 2.0)
+  ch = (flag ? "HELLO" : "WORLD")
+end subroutine
+
+! Valid cases with nested conditionals
+subroutine valid_nested(flag1, flag2, x, y, z, w)
+  logical :: flag1, flag2
+  integer :: x, y, z, w, result
+
+  ! Nested in value position
+  result = (flag1 ? (flag2 ? x : y) : z)
+
+  ! Nested in condition (condition is logical)
+  result = ((x > y ? flag1 : flag2) ? w : z)
+
+  ! Multi-branch
+  result = (x > 10 ? 100 : x > 5 ? 50 : 0)
+end subroutine
+
+! Valid cases with arrays
+subroutine valid_arrays(flag)
+  logical :: flag
+  integer :: arr1(10), arr2(10), arr3(10)
+  real :: mat1(3,3), mat2(3,3), mat3(3,3)
+
+  ! Whole array conditional
+  arr3 = (flag ? arr1 : arr2)
+
+  ! Multidimensional arrays
+  mat3 = (flag ? mat1 : mat2)
+
+  ! Array sections
+  arr3(1:5) = (flag ? arr1(1:5) : arr2(1:5))
+end subroutine
+
+! Valid cases with derived types
+subroutine valid_derived_types(flag)
+  type :: point
+    real :: x, y
+  end type
+
+  logical :: flag
+  type(point) :: p1, p2, p3
+
+  p3 = (flag ? p1 : p2)
+end subroutine
+
+! Valid cases with character lengths
+subroutine valid_character_lengths(flag)
+  logical :: flag
+  character(len=5) :: short1, short2, short3
+  character(len=10) :: medium
+  character(len=20) :: long
+
+  ! Same length
+  short3 = (flag ? short1 : short2)
+
+  ! Different lengths - padding/truncation applies
+  medium = (flag ? short1 : medium)
+  long = (flag ? short1 : "A LONGER STRING")
+end subroutine
+
+! Valid: deferred-length character scalars
+subroutine valid_deferred_length_character(flag)
+  logical :: flag
+  character(len=:), allocatable :: str1, str2, result
+
+  str1 = "SHORT"
+  str2 = "A MUCH LONGER STRING"
+  ! Result length is determined by selected branch
+  result = (flag ? str1 : str2)
+end subroutine
+
+! Valid: assumed-length character arguments
+subroutine valid_assumed_length_character(flag, str1, str2)
+  logical :: flag
+  character(len=*) :: str1, str2
+  character(len=100) :: result
+
+  result = (flag ? str1 : str2)
+end subroutine
+
+! Error: condition must be logical
+subroutine error_non_logical_condition()
+  integer :: i, x, y
+  real :: r
+  character :: ch
+
+  !ERROR: Must have LOGICAL type, but is INTEGER(4)
+  i = (i ? x : y)
+
+  !ERROR: Must have LOGICAL type, but is REAL(4)
+  i = (r ? x : y)
+
+  !ERROR: Must have LOGICAL type, but is CHARACTER(KIND=1,LEN=1_8)
+  i = (ch ? x : y)
+end subroutine
+
+! Error: type mismatch between branches
+subroutine error_type_mismatch(flag)
+  logical :: flag
+  integer :: i1, i2
+  real :: r
+  character :: ch
+  complex :: c
+
+  !ERROR: All values in conditional expression must have the same type and kind; have INTEGER(4) and REAL(4)
+  i1 = (flag ? i2 : r)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have INTEGER(4) and CHARACTER(KIND=1,LEN=1_8)
+  i1 = (flag ? i2 : ch)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have REAL(4) and COMPLEX(4)
+  r = (flag ? r : c)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have LOGICAL(4) and INTEGER(4)
+  flag = (flag ? flag : i1)
+end subroutine
+
+! Error: kind mismatch (F2023 C1004)
+subroutine error_kind_mismatch(flag)
+  logical :: flag
+  integer(kind=4) :: i4
+  integer(kind=8) :: i8
+  real(kind=4) :: r4
+  real(kind=8) :: r8
+  complex(kind=4) :: c4
+  complex(kind=8) :: c8
+
+  !ERROR: All values in conditional expression must have the same type and kind; have INTEGER(4) and INTEGER(8)
+  i4 = (flag ? i4 : i8)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have REAL(4) and REAL(8)
+  r4 = (flag ? r4 : r8)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have COMPLEX(4) and COMPLEX(8)
+  c4 = (flag ? c4 : c8)
+end subroutine
+
+! Error: derived type mismatch
+subroutine error_derived_type_mismatch(flag)
+  type :: type1
+    integer :: i
+  end type
+
+  type :: type2
+    integer :: i
+  end type
+
+  logical :: flag
+  type(type1) :: t1
+  type(type2) :: t2
+
+  !ERROR: All values in conditional expression must be the same derived type; have type1 and type2
+  t1 = (flag ? t1 : t2)
+end subroutine
+
+! Error: derived type vs intrinsic type mismatch
+subroutine error_derived_vs_intrinsic(flag)
+  type :: my_type
+    integer :: i
+  end type
+
+  logical :: flag
+  type(my_type) :: t
+  integer :: i
+  real :: r
+
+  !ERROR: All values in conditional expression must have the same type and kind; have my_type and INTEGER(4)
+  t = (flag ? t : i)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have INTEGER(4) and my_type
+  t = (flag ? i : t)
+
+  !ERROR: All values in conditional expression must have the same type and kind; have my_type and REAL(4)
+  t = (flag ? t : r)
+end subroutine
+
+! Error: array rank mismatch
+subroutine error_array_rank_mismatch(flag)
+  logical :: flag
+  integer :: arr1(10), mat1(3,3), result(10)
+
+  !ERROR: All values in conditional expression must have the same rank; have rank 1 and 2
+  result = (flag ? arr1 : mat1)
+end subroutine
+
+! Error: scalar vs array mismatch
+subroutine error_scalar_array_mismatch(flag)
+  logical :: flag
+  integer :: scalar, arr(10), result(10)
+
+  !ERROR: All values in conditional expression must have the same rank; have rank 0 and 1
+  result = (flag ? scalar : arr)
+end subroutine
+
+! Error: condition must be scalar
+subroutine error_array_condition()
+  logical :: flags(5)
+  integer :: x(5), y(5), result(5)
+
+  !ERROR: Must be a scalar value, but is a rank-1 array
+  result = (flags ? x : y)
+end subroutine
+
+! Valid cases with intrinsic functions
+subroutine valid_intrinsic_functions(x, y, flag)
+  integer :: x, y
+  logical :: flag
+  integer :: result
+
+  result = (flag ? abs(x) : abs(y))
+  result = (flag ? max(x, y) : min(x, y))
+end subroutine
+
+! Valid: conditional in array constructor
+subroutine valid_in_array_constructor(flag, x, y)
+  logical :: flag
+  integer :: x, y, arr(3)
+
+  arr = [(flag ? x : y), (flag ? x + 1 : y + 1), (flag ? x + 2 : y + 2)]
+end subroutine
+
+! Valid: conditional in expression context
+subroutine valid_in_expression(flag, x, y)
+  logical :: flag
+  integer :: x, y, z
+
+  z = (flag ? x : y) + 10
+  z = 2 * (flag ? x : y)
+
+  if ((flag ? x : y) > 5) then
+    z = 1
+  end if
+end subroutine
+
+! Note: allocatable/pointer 
diff erences are handled by assignment semantics
+! The conditional expression just requires matching types
+
+! Valid: both branches allocatable
+subroutine valid_both_allocatable(flag)
+  logical :: flag
+  integer, allocatable :: alloc1, alloc2, result
+
+  allocate(result)
+  result = (flag ? alloc1 : alloc2)
+end subroutine
+
+! Valid: both branches pointer
+subroutine valid_both_pointer(flag)
+  logical :: flag
+  integer, pointer :: ptr1, ptr2, result
+
+  result = (flag ? ptr1 : ptr2)
+end subroutine
+
+! Valid: elemental context
+elemental integer function conditional_elemental(flag, x, y)
+  logical, intent(in) :: flag
+  integer, intent(in) :: x, y
+
+  conditional_elemental = (flag ? x : y)
+end function
+
+! Valid: pure context
+pure integer function conditional_pure(flag, x, y)
+  logical, intent(in) :: flag
+  integer, intent(in) :: x, y
+
+  conditional_pure = (flag ? x : y)
+end function
+
+! Valid: recursive context
+recursive integer function conditional_recursive(n, flag, x, y) result(res)
+  integer, intent(in) :: n
+  logical, intent(in) :: flag
+  integer, intent(in) :: x, y
+
+  if (n <= 0) then
+    res = (flag ? x : y)
+  else
+    res = conditional_recursive(n - 1, flag, x, y)
+  end if
+end function
+
+! Valid: nested multi-branch
+subroutine valid_multi_branch(x)
+  integer :: x, result
+
+  ! Five-branch conditional
+  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)
+  type :: base_t
+    integer :: i
+  end type
+
+  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: mismatched character kinds
+subroutine error_character_kind_mismatch(flag)
+  logical :: flag
+  character(kind=1, len=5) :: ch1
+  character(kind=4, len=5) :: ch4
+
+  !ERROR: All values in conditional expression must have the same type and kind; have CHARACTER(KIND=1,LEN=5_8) and CHARACTER(KIND=4,LEN=5_8)
+  ch1 = (flag ? ch1 : ch4)
+end subroutine
+
+! Valid: optional arguments
+subroutine valid_optional_args(flag, opt_x, opt_y)
+  logical :: flag
+  integer, optional :: opt_x, opt_y
+  integer :: result
+
+  if (present(opt_x) .and. present(opt_y)) then
+    result = (flag ? opt_x : opt_y)
+  end if
+end subroutine
+
+! Valid: mix of expressions and designators
+subroutine valid_mixed_expressions(flag, x, y)
+  logical :: flag
+  integer :: x, y, result
+
+  result = (flag ? x + y : x - y)
+  result = (flag ? 2 * x : y / 2)
+end subroutine
+
+! Constant-folding: when the condition is a constant, only the selected
+! branch must be a constant expression (F2023 10.1.12).
+subroutine constant_folding_cases()
+  integer :: non_const = 99
+
+  ! Valid: .true. selects 10; non_const is in the unselected else-branch.
+  integer, parameter :: p_true_const  = (.true.  ? 10 : non_const)
+
+  ! Valid: .false. selects 10; non_const is in the unselected then-branch.
+  integer, parameter :: p_false_const = (.false. ? non_const : 10)
+
+  ! Error: .false. selects non_const — not a constant expression.
+  !ERROR: Must be a constant value
+  integer, parameter :: p_false_nconst = (.false. ? 10 : non_const)
+
+  ! Error: .true. selects non_const — not a constant expression.
+  !ERROR: Must be a constant value
+  integer, parameter :: p_true_nconst  = (.true.  ? non_const : 10)
+end subroutine
+
+! Module serialization: conditional expressions in a module must be correctly
+! written to and read back from the .mod file.
+module conditional_expr_mod
+  implicit none
+contains
+  subroutine mod_mixed_expressions(flag, x, y, result)
+    logical, intent(in) :: flag
+    integer, intent(in) :: x, y
+    integer, intent(out) :: result
+
+    result = (flag ? x + y : x - y)
+    result = (flag ? 2 * x : y / 2)
+  end subroutine
+end module
+
+subroutine valid_use_from_module(flag, x, y)
+  use conditional_expr_mod
+  logical :: flag
+  integer :: x, y, result
+
+  call mod_mixed_expressions(flag, x, y, result)
+end subroutine


        


More information about the flang-commits mailing list