Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
748c2a3
[flang] Implement conditional expressions (F2023)
cenewcombe Jan 26, 2026
75ce70c
[flang] Add lookahead for conditional expressions parsing
cenewcombe Feb 27, 2026
2b1c723
[flang] Simplify conditional expression argument dectection and fix G…
cenewcombe Feb 27, 2026
c4c3492
[flang] Add spacing for conditional expressions unparsing
cenewcombe Mar 10, 2026
7440608
[flang] Include F2023 for references to Fortran standard
cenewcombe Mar 12, 2026
4421182
[flang] Add ConditionalExpr traverse visitor support (new visitor req…
cenewcombe Mar 13, 2026
fc428ce
[flang] Update conditional expressions to tree representation
cenewcombe Mar 20, 2026
e94a694
[flang] Update conditional expressions to tree representation (loweri…
cenewcombe Mar 20, 2026
40c5bdc
[flang] Fix conditional expression handlers for IsConstantExprHelper …
cenewcombe Mar 23, 2026
f21c624
[flang] Fix ConditionalExpr lowering test for static array extents
cenewcombe Mar 23, 2026
3f6d881
Cleanup conditional expressions implementation based on review comments
cenewcombe Mar 24, 2026
9994f0b
[flang] Conditional expressions (lowering): used braced initializatio…
cenewcombe Mar 24, 2026
befc9a5
[flang] Refactor ConditionalExpr parser to right-recursive tree repre…
cenewcombe Mar 26, 2026
dc73e53
[flang] Update F2023 references for conditional expressions using cor…
cenewcombe Mar 27, 2026
25f788e
[flang] Fix selective evaluation in FoldOperation(ConditionalExpr) an…
cenewcombe Mar 30, 2026
80dc7fa
[flang] Fix IsContiguousHelper for ConditionalExpr to return true
cenewcombe Mar 31, 2026
2dab0ff
[flang] Fix and polish conditional expression (F2023) implementation
cenewcombe Mar 31, 2026
defda0e
[flang] Add lowering LIT test for non-contiguous array sections in co…
cenewcombe Mar 31, 2026
f0dd9c1
[flang] Add a clarifying comment for the AsFortran method for Conditi…
cenewcombe Mar 31, 2026
4f1f795
[flang] Update comments and add test case for conditional expressions
cenewcombe Apr 1, 2026
35420bc
[flang] Fix memory leak in ConditionalExpr lowering for allocatable t…
cenewcombe Apr 1, 2026
0b4374d
Polishing for conditional expressions parser and semantic checks
cenewcombe Apr 6, 2026
0e157d2
[flang] Fixes to conditional expressions lowering
cenewcombe Apr 6, 2026
18e5c05
[flang] Conditional expressions lowering and test updates
cenewcombe Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flang/examples/FeatureList/FeatureList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 39 additions & 8 deletions flang/include/flang/Evaluate/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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>>;

Expand All @@ -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>>;

Expand All @@ -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>>;

Expand All @@ -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;
};

Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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;
};

Expand Down Expand Up @@ -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_
9 changes: 9 additions & 0 deletions flang/include/flang/Evaluate/fold.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
15 changes: 15 additions & 0 deletions flang/include/flang/Evaluate/shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions flang/include/flang/Evaluate/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
Expand Down Expand Up @@ -1381,6 +1384,7 @@ enum class Operator {
Call,
Constant,
Convert,
Conditional,
Div,
Eq,
Eqv,
Expand Down
4 changes: 4 additions & 0 deletions flang/include/flang/Evaluate/traverse.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions flang/include/flang/Parser/characters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions flang/include/flang/Parser/dump-parse-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 17 additions & 5 deletions flang/include/flang/Parser/parse-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
};

Expand Down
13 changes: 13 additions & 0 deletions flang/include/flang/Semantics/dump-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions flang/include/flang/Semantics/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
29 changes: 29 additions & 0 deletions flang/lib/Evaluate/check-expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)}) {
Expand Down
16 changes: 16 additions & 0 deletions flang/lib/Evaluate/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(); },
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions flang/lib/Evaluate/fold-implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
Loading