[flang-commits] [flang] [flang] Implement conditional expressions parser/semantics (F2023) (PR #186489)
via flang-commits
flang-commits at lists.llvm.org
Fri Mar 13 11:47:27 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
@llvm/pr-subscribers-flang-fir-hlfir
Author: Caroline Newcombe (cenewcombe)
<details>
<summary>Changes</summary>
## Implement Fortran 2023 Conditional Expressions (R1002)
***This PR contains the implementation for parsing and semantic analysis. Lowering is implemented in a separate PR (to be posted soon)***
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 (PR to be posted soon)
**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.
---
Patch is 74.61 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/186489.diff
31 Files Affected:
- (modified) flang/examples/FeatureList/FeatureList.cpp (+1)
- (modified) flang/include/flang/Evaluate/expression.h (+41-8)
- (modified) flang/include/flang/Evaluate/shape.h (+10)
- (modified) flang/include/flang/Evaluate/tools.h (+28)
- (modified) flang/include/flang/Evaluate/traverse.h (+4)
- (modified) flang/include/flang/Parser/characters.h (+1)
- (modified) flang/include/flang/Parser/dump-parse-tree.h (+2)
- (modified) flang/include/flang/Parser/parse-tree.h (+19-5)
- (modified) flang/include/flang/Semantics/dump-expr.h (+22)
- (modified) flang/include/flang/Semantics/expression.h (+1)
- (modified) flang/lib/Evaluate/check-expression.cpp (+139)
- (modified) flang/lib/Evaluate/expression.cpp (+23)
- (modified) flang/lib/Evaluate/formatting.cpp (+14)
- (modified) flang/lib/Evaluate/shape.cpp (+56-7)
- (modified) flang/lib/Evaluate/tools.cpp (+121)
- (modified) flang/lib/Lower/ConvertExpr.cpp (+10)
- (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+6)
- (modified) flang/lib/Lower/IterationSpace.cpp (+11)
- (modified) flang/lib/Lower/Support/Utils.cpp (+32)
- (modified) flang/lib/Parser/basic-parsers.h (+32)
- (modified) flang/lib/Parser/expr-parsers.cpp (+12)
- (modified) flang/lib/Parser/unparse.cpp (+12)
- (modified) flang/lib/Semantics/check-cuda.cpp (+30)
- (modified) flang/lib/Semantics/check-data.cpp (+15)
- (modified) flang/lib/Semantics/check-do-forall.cpp (+11)
- (modified) flang/lib/Semantics/definable.cpp (+14)
- (modified) flang/lib/Semantics/expression.cpp (+216)
- (modified) flang/lib/Semantics/openmp-utils.cpp (+25)
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+14)
- (added) flang/test/Parser/conditional-expr.f90 (+261)
- (added) flang/test/Semantics/conditional-expr.f90 (+365)
``````````diff
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..d46699cb7ac2c 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -390,6 +390,36 @@ 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(
+ std::vector<Expr<SomeLogical>> &&conds, std::vector<Expr<Result>> &&vals)
+ : conditions_{std::move(conds)}, values_{std::move(vals)} {
+ CHECK(values_.size() == conditions_.size() + 1);
+ }
+ bool operator==(const ConditionalExpr &) const;
+ const std::vector<Expr<SomeLogical>> &conditions() const {
+ return conditions_;
+ }
+ std::vector<Expr<SomeLogical>> &conditions() { return conditions_; }
+ const std::vector<Expr<Result>> &values() const { return values_; }
+ std::vector<Expr<Result>> &values() { return values_; }
+ int Rank() const { return values_.empty() ? 0 : values_.front().Rank(); }
+ std::optional<DynamicType> GetType() const {
+ return values_.empty() ? std::nullopt : values_.front().GetType();
+ }
+ static constexpr int Corank() { return 0; }
+ llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
+
+private:
+ std::vector<Expr<SomeLogical>> conditions_; // size N
+ std::vector<Expr<Result>> values_; // size N+1 (includes else)
+};
+
// Array constructors
template <typename RESULT> class ArrayConstructorValues;
@@ -536,7 +566,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 +598,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 +624,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 +643,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 +669,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 +741,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 +819,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 +961,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/shape.h b/flang/include/flang/Evaluate/shape.h
index f0505cfcdf2d7..3af78820f6c66 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -189,6 +189,16 @@ 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 determined by the selected branch,
+ // so return unknown extents for the rank.
+ if (!conditional.values().empty()) {
+ int rank{conditional.values().front().Rank()};
+ return Shape(rank, std::nullopt);
+ }
+ return ScalarShape();
+ }
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 0fded08456bcf..9ba29a5a2879c 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>) {
@@ -1071,6 +1074,16 @@ struct GetSymbolVectorHelper
Result operator()(const Component &) const;
Result operator()(const ArrayRef &) const;
Result operator()(const CoarrayRef &) const;
+ template <typename T> Result operator()(const ConditionalExpr<T> &x) {
+ Result result;
+ for (const auto &cond : x.conditions()) {
+ result = Combine(std::move(result), (*this)(cond));
+ }
+ for (const auto &val : x.values()) {
+ result = Combine(std::move(result), (*this)(val));
+ }
+ return result;
+ }
};
template <typename A> SymbolVector GetSymbolVector(const A &x) {
return GetSymbolVectorHelper{}(x);
@@ -1159,6 +1172,20 @@ class UnsafeToCopyVisitor : public AnyTraverse<UnsafeToCopyVisitor> {
return !admitPureCall_ || !procRef.proc().IsPure();
}
bool operator()(const CoarrayRef &) { return true; }
+ template <typename T> bool operator()(const ConditionalExpr<T> &x) {
+ // A conditional expression is unsafe to copy if any of its parts are unsafe
+ for (const auto &condition : x.conditions()) {
+ if ((*this)(condition)) {
+ return true;
+ }
+ }
+ for (const auto &value : x.values()) {
+ if ((*this)(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
private:
bool admitPureCall_{false};
@@ -1381,6 +1408,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..306337274bf1f 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.conditions(), x.values());
+ }
// 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..4f6142af7eaf6 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -252,6 +252,8 @@ class ParseTreeDumper {
NODE(parser, ComputedGotoStmt)
NODE(parser, ConcurrentControl)
NODE(parser, ConcurrentHeader)
+ NODE(parser, ConditionalExpr)
+ NODE(ConditionalExpr, Branch)
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 4aec99c80bdae..706d31dca3331 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1678,6 +1678,19 @@ 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);
+ struct Branch {
+ TUPLE_CLASS_BOILERPLATE(Branch);
+ std::tuple<ScalarLogicalExpr, common::Indirection<Expr>> t;
+ };
+ std::tuple<std::list<Branch>, common::Indirection<Expr>> t;
+};
+
// R1001 - R1022 expressions
struct Expr {
UNION_CLASS_BOILERPLATE(Expr);
@@ -1776,11 +1789,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..5fbed77139958 100644
--- a/flang/include/flang/Semantics/dump-expr.h
+++ b/flang/include/flang/Semantics/dump-expr.h
@@ -201,6 +201,28 @@ 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));
+ const auto &conds = x.conditions();
+ const auto &vals = x.values();
+ // Show condition-value pairs
+ for (const auto &[cond, val] : llvm::zip(conds, vals)) {
+ Indent("branch");
+ Indent("condition");
+ Show(cond);
+ Outdent();
+ Indent("value");
+ Show(val);
+ Outdent();
+ Outdent();
+ }
+ if (!vals.empty()) {
+ Indent("default value");
+ Show(vals.back());
+ 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 490399aa03ff8..0054a86486e79 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..7c5e7c129765c 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -92,6 +92,23 @@ class IsConstantExprHelper
!sym.attrs().test(semantics::Attr::VALUE)));
}
+ template <typename T>
+ bool operator()(const ConditionalExpr<T> &conditional) const {
+ // A conditional expression is constant if all its conditions and values are
+ // constant
+ for (const auto &condition : conditional.conditions()) {
+ if (!(*this)(condition)) {
+ return false;
+ }
+ }
+ for (const auto &value : conditional.values()) {
+ if (!(*this)(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
bool operator()(const ImpliedDoIndex &ido) const {
return acImpliedDos_.find(ido.name) != acImpliedDos_.end() || !context_ ||
context_->GetImpliedDo(ido.name).has_value();
@@ -229,6 +246,20 @@ struct IsActuallyConstantHelper {
template <typename T> bool operator()(const Parentheses<T> &x) {
return (*this)(x.left());
}
+ template <typename T> bool operator()(const ConditionalExpr<T> &x) {
+ // A conditional expression is actually constant if all its parts are
+ for (const auto &condition : x.conditions()) {
+ if (!(*this)(condition)) {
+ return false;
+ }
+ }
+ for (const auto &value : x.values()) {
+ if (!(*this)(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
template <typename T> bool operator()(const Expr<T> &x) {
return common::visit([=](const auto &y) { return (*this)(y); }, x.u);
}
@@ -358,6 +389,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());
}
@@ -492,6 +527,21 @@ class SuspiciousRealLiteralFinder
}
return (*this)(x.left());
}
+ template <typename T> bool operator()(const ConditionalExpr<T> &x) const {
+ // Check all conditions and values in the conditional expression for
+ // suspicious literals
+ for (const auto &cond : x.conditions()) {
+ if ((*this)(cond)) {
+ return true;
+ }
+ }
+ for (const auto &value : x.values()) {
+ if ((*this)(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
private:
int kind_;
@@ -531,6 +581,16 @@ class InexactLiteralConversionFlagClearer
mut.set_isFromInexactLiteralConversion(false);
return false;
}
+ template <typename T> bool operator()(const ConditionalExpr<T> &x) const {
+ // Clear flags in all conditions and values of the conditional expression
+ for (const auto &cond : x.conditions()) {
+ (*this)(cond);
+ }
+ for (const auto &value : x.values()) {
+ (*this)(value);
+ }
+ return false;
+ }
};
// Converts, folds, and then checks type, rank, and shape of an
@@ -798,6 +858,20 @@ class CheckSpecificationExprHelper
return std::nullopt;
}
+ template <typename T> Result operator()(const ConditionalExpr<T> &x) const {
+ for (const auto &cond : x.conditions()) {
+ if (auto result{(*this)(cond)}) {
+ return result;
+ }
+ }
+ for (const auto &val : x.values()) {
+ if (auto result{(*this)(val)}) {
+ return result;
+ }
+ }
+ return std::nullopt;
+ }
+
Result operator()(const ProcedureRef &x) const {
if (const auto *symbol{x.proc().GetSymbol()}) {
const Symbol &ultimate{symbol->GetUltimate()};
@@ -1193,6 +1267,31 @@ class IsContiguousHelper
Result operator()(const NullPointer &) const { return true; }
+ template <typename T> Result operator()(const ConditionalExpr<T> &x) {
+ // Track contiguity across all possible runtime branches
+ bool hasContiguous{false};
+ bool hasNonContiguous{false};
+ bool hasUnknown{false};
+ for (const auto &val : x.values()) {
+ auto result{(*this)(val)};
+ if (!result) {
+ hasUnknown = true;
+ } else if (*result) {
+ hasContiguous = true;
+ } else {
+ hasNonContiguous = true;
+ }
+ }
+ // Return definite result only if all values have uniform contiguity
+ if (hasUnknown || (hasContiguous && hasNonContiguous)) {
+ return std::nullopt;
+ } else if (hasContiguous) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private:
// Returns "true" for a provably empty or simply contiguous array section;
// return "false" for a provably nonempty discontiguous section or for use
@@ -1386,6 +1485,20 @@ struct IsErrorExprHelper : public AnyTraverse<IsErrorExprHelper, bool> {
bool operator()(const SpecificIntrinsic &x) {
return x.name == IntrinsicProcTable::InvalidName;
}
+
+ template <typename T> bool operator()(const ConditionalExpr<T> &x) {
+ for (const auto &cond : x.conditions()) {
+ if ((*this)(cond)) {
+ return true;
+ }
+ }
+ for (const auto &val : x.values()) {
+ if ((*this)(val)) {
+ return true;
+ }
+ }
+ return false;
+ }
};
template <typename A> bool IsErrorExpr(const A &x) {
@@ -1501,6 +1614,20 @@ class StmtFunctionChecker
return std::nullopt;
}
+ template <typename T> Result operator()(const ConditionalExpr<T> &x) {
+ for (const auto &cond : x.conditions()) {
+ if (auto result{(*this)(cond)}) {
+ return result;
+ }
+ }
+ for (const auto &val : x.values()) {
+ if (auto result{(*this)(val)}) {
+ return result;
+ }
+ }
+ return std::nullopt;
+ }
+
private:
const Symbol &sf_;
FoldingContext &context_;
@@ -1760,6 +1887,18 @@ class CollectUsedSymbolValuesHelper
...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/186489
More information about the flang-commits
mailing list