[flang-commits] [flang] [llvm] [flang] Enumeration Type: (PR 3/5) Intrinsics + I/O + Modules (PR #193235)
via flang-commits
flang-commits at lists.llvm.org
Thu Aug 6 22:46:21 PDT 2026
================
@@ -1255,6 +1255,117 @@ template <int KIND>
Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
FoldingContext &context, FunctionRef<Type<TypeCategory::Logical, KIND>> &&);
+// Fold NEXT(enum) / PREVIOUS(enum) to a constant enumerator (scalar or array)
+// when the argument is a constant enumeration value. Returns the folded
+// constant, or the original reference left unfolded (e.g. at a boundary,
+// where error termination is deferred to run time).
+static inline Expr<SomeDerived> FoldEnumerationNextOrPrevious(
+ FoldingContext &context, FunctionRef<SomeDerived> &&funcRef, bool isNext) {
+ ActualArguments &args{funcRef.arguments()};
+ // Don't fold if STAT is present — STAT assignment is a side effect
+ if (args.size() >= 2 && args[1]) {
+ return Expr<SomeDerived>{std::move(funcRef)};
+ }
+ auto *expr{args.size() >= 1 && args[0]
+ ? UnwrapExpr<Expr<SomeDerived>>(args[0])
+ : nullptr};
+ if (!expr) {
+ return Expr<SomeDerived>{std::move(funcRef)};
+ }
+ const auto *derived{GetEnumerationTypeSpec(expr->GetType())};
+ const semantics::Scope *scope{derived ? derived->GetScope() : nullptr};
+ if (!scope) {
+ return Expr<SomeDerived>{std::move(funcRef)};
+ }
+ auto ordIter{scope->find(
+ semantics::SourceName{semantics::DerivedTypeDetails::ordinalComponentName,
+ sizeof(semantics::DerivedTypeDetails::ordinalComponentName) - 1})};
+ if (ordIter == scope->end()) {
+ return Expr<SomeDerived>{std::move(funcRef)};
+ }
+ const semantics::Symbol &ordSym{*ordIter->second};
+ int count{derived->typeSymbol()
+ .GetUltimate()
+ .get<semantics::DerivedTypeDetails>()
+ .enumeratorCount()};
+ auto *constant{UnwrapConstantValue<SomeDerived>(*expr)};
+ if (!constant) {
+ return Expr<SomeDerived>{std::move(funcRef)};
+ }
+ // A boundary hit (NEXT() of the last enumerator or PREVIOUS() of the first)
+ // without STAT= is, in the final design, a runtime error termination. In a
+ // required-constant context that value cannot be deferred, so it is
+ // diagnosed as out of range. Outside a constant context the reference would
+ // otherwise be left unfolded and deferred to run time — but lowering has no
+ // NEXT/PREVIOUS support yet (IntrinsicCall.cpp aborts), so a constant
+ // boundary argument is temporarily gated here, mirroring the STAT= and
+ // non-constant guards in intrinsics.cpp, until the lowering handler lands.
+ auto handleBoundary{[&]() -> Expr<SomeDerived> {
+ if (context.inConstantContext()) {
----------------
MattPD wrote:
Gating on `inConstantContext()` removes the abort, which is the right call while lowering is missing. It also makes the guarded branch unreachable.
Analysis folds every `parser::Expr` once, outside a constant context. That fold runs first and returns `MakeInvalidIntrinsic`. The later fold that runs when a constant is required then finds no matching intrinsic name. Both of these declarations report "not yet supported" rather than "out of range":
```fortran
logical, parameter :: nb = next(blue) == green
type(color) :: c = next(blue)
```
This PR also updates the out-of-range test to expect the temporary message. The out-of-range diagnostic is then left with no reachable caller and no coverage, and PR 4 of 5 would restore it untested.
Would it work to report the out-of-range error in both cases, and attach the "not yet supported" text as a note? Removing the attachment in PR 4 of 5 would then be the whole change, and the out-of-range test could stay as it was.
https://github.com/llvm/llvm-project/pull/193235
More information about the flang-commits
mailing list