Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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()));
Comment on lines +2221 to +2222
Copy link
Copy Markdown
Contributor

@akuhlens akuhlens Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this implementation, very metacircular interpreter.

}
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