[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:32 PDT 2026


================
@@ -1255,6 +1255,110 @@ 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 a runtime error termination, so in an ordinary expression
+  // it is left unfolded and deferred to run time.  In a required-constant
----------------
MattPD wrote:

Confirmed: Both boundary cases now diagnose instead of aborting, with no regression elsewhere. I left a separate comment on the `inConstantContext()` guard.

https://github.com/llvm/llvm-project/pull/193235


More information about the flang-commits mailing list