[flang-commits] [flang] [flang] Enumeration Type: (PR 4/5) Lowering (PR #193571)

via flang-commits flang-commits at lists.llvm.org
Thu Oct 1 19:49:39 PDT 2026


================
@@ -1727,12 +1728,173 @@ class HlfirBuilder {
   gen(const Fortran::evaluate::FunctionRef<T> &expr) {
     mlir::Type resType =
         Fortran::lower::TypeBuilder<T>::genType(getConverter(), expr);
+
+    if constexpr (std::is_same_v<T, Fortran::evaluate::SomeDerived>) {
+      if (const auto *intrinsic = expr.proc().GetSpecificIntrinsic();
+          intrinsic && intrinsic->name == "huge")
+        return genEnumerationHuge(expr, resType);
+    }
+
+    if constexpr (T::category == Fortran::common::TypeCategory::Integer) {
+      if (const auto *intrinsic = expr.proc().GetSpecificIntrinsic();
+          intrinsic && intrinsic->name == "int" && !expr.arguments().empty() &&
+          expr.arguments()[0])
+        if (const auto *arg = expr.arguments()[0]->UnwrapExpr())
+          if (const auto *spec =
+                  Fortran::evaluate::GetDerivedTypeSpec(arg->GetType());
+              spec && Fortran::semantics::IsEnumerationType(spec->typeSymbol()))
+            return genEnumerationInt(*arg, resType);
+    }
+
     auto result = Fortran::lower::convertCallToHLFIR(
         getLoc(), getConverter(), expr, resType, getSymMap(), getStmtCtx());
     assert(result.has_value());
     return *result;
   }
 
+  // Number of enumerators of the enumeration type returned by expr.
+  int getEnumerationEnumeratorCount(
+      const Fortran::evaluate::FunctionRef<Fortran::evaluate::SomeDerived>
+          &expr,
+      llvm::StringRef name) {
+    auto resultDynType = expr.proc().GetType();
+    assert(resultDynType && (name + " must have a result type").str().c_str());
+    const auto *derived = Fortran::evaluate::GetDerivedTypeSpec(*resultDynType);
+    assert(derived &&
+           derived->typeSymbol()
+               .detailsIf<Fortran::semantics::DerivedTypeDetails>() &&
+           derived->typeSymbol()
+               .detailsIf<Fortran::semantics::DerivedTypeDetails>()
+               ->isEnumerationType() &&
+           (name + " result must be enumeration type").str().c_str());
+    return derived->typeSymbol()
+        .GetUltimate()
+        .get<Fortran::semantics::DerivedTypeDetails>()
+        .enumeratorCount();
+  }
+
+  // Lower HUGE(enumVar) for non-constant enumeration arguments.
+  // Should always be folded, but handle as a constant just in case.
+  hlfir::EntityWithAttributes genEnumerationHuge(
+      const Fortran::evaluate::FunctionRef<Fortran::evaluate::SomeDerived>
+          &expr,
+      mlir::Type resType) {
+    mlir::Location loc = getLoc();
+    fir::FirOpBuilder &builder = getBuilder();
+    int count = getEnumerationEnumeratorCount(expr, "HUGE");
+    mlir::Value last =
+        builder.createIntegerConstant(loc, builder.getI32Type(), count);
+    return Fortran::lower::genEnumerationTemp(
+        loc, builder, mlir::cast<fir::RecordType>(resType), last, "ctor.temp");
+  }
+
+  // Build an enumeration value in a temporary. A non-constant ordinal is range
+  // checked (F2023 7.6.2 p5) in its original kind, before narrowing.
+  hlfir::EntityWithAttributes
+  genEnumerationConstructor(const Fortran::evaluate::StructureConstructor &ctor,
+                            fir::RecordType recTy) {
+    using Int4 =
+        Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>;
+    mlir::Location loc = getLoc();
+    fir::FirOpBuilder &builder = getBuilder();
+    assert(ctor.values().size() == 1 &&
+           "enumeration constructor must have exactly one value");
+    const auto &[ordSym, ordValue] = *ctor.values().begin();
+    const Fortran::lower::SomeExpr &ordExpr = ordValue.value();
+    std::string fieldName = converter.getRecordTypeFieldName(*ordSym);
+    mlir::Type ordTy = recTy.getType(fieldName);
+    assert(ordTy && "enumeration type must have an ordinal component");
+
+    mlir::Value ordinal;
+    if (std::optional<std::int64_t> constOrdinal =
+            Fortran::evaluate::ToInt64(ordExpr)) {
+      ordinal = builder.createIntegerConstant(loc, ordTy, *constOrdinal);
+    } else {
+      const Fortran::evaluate::Convert<Int4> *narrowing = nullptr;
+      if (const auto *intExpr = std::get_if<
+              Fortran::evaluate::Expr<Fortran::evaluate::SomeInteger>>(
+              &ordExpr.u))
+        if (const auto *int4Expr =
+                std::get_if<Fortran::evaluate::Expr<Int4>>(&intExpr->u))
+          narrowing =
+              std::get_if<Fortran::evaluate::Convert<Int4>>(&int4Expr->u);
+      hlfir::Entity original =
+          narrowing ? gen(narrowing->left()) : gen(ordExpr);
+      mlir::Value value = hlfir::loadTrivialScalar(loc, builder, original);
+      mlir::Type valueTy = value.getType();
+      int count = ctor.derivedTypeSpec()
+                      .typeSymbol()
+                      .GetUltimate()
+                      .get<Fortran::semantics::DerivedTypeDetails>()
+                      .enumeratorCount();
+      mlir::Value one = builder.createIntegerConstant(loc, valueTy, 1);
+      mlir::Value maxVal = builder.createIntegerConstant(loc, valueTy, count);
----------------
MattPD wrote:

With 128 enumerators and a variable `integer(1)` argument holding 1, the constructor now initiates error termination. The upper bound becomes `-128 : i8`, so the signed comparison rejects that valid ordinal. The same program completes at [095d1945](https://github.com/llvm/llvm-project/commit/095d1945f7700776c0f8f0ed00640133a8c285f2). Could the comparison use a signed type representing both the original argument and the enumerator count, while preserving the INTEGER(8) check before narrowing?

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


More information about the flang-commits mailing list