[flang-commits] [flang] [flang] Enumeration Type: (PR 2/5) Name Resolution + Expression + Relational + SELECT CASE (PR #193028)

via flang-commits flang-commits at lists.llvm.org
Sat Jun 27 00:44:16 PDT 2026


================
@@ -236,6 +238,89 @@ template <TypeCategory CAT> struct TypeVisitor {
   const std::list<parser::CaseConstruct::Case> &caseList;
 };
 
+// Convert a single enumeration CASE value to its __ordinal integer.
+static bool ConvertEnumCaseValue(SemanticsContext &context,
+    const parser::CaseValue &caseValue,
+    const semantics::DerivedTypeSpec &enumType,
+    const semantics::Symbol &ordSym) {
+  const auto &expr{parser::UnwrapRef<parser::Expr>(caseValue)};
+  auto *x{expr.typedExpr.get()};
+  if (!x || !x->v) {
+    return false;
+  }
+  auto type{x->v->GetType()};
+  if (!type || type->category() != TypeCategory::Derived) {
+    std::string typeStr{type ? type->AsFortran() : "typeless"s};
+    context.Say(expr.source,
+        "CASE value has type '%s' which is not compatible with the SELECT CASE expression's type '%s'"_err_en_US,
+        typeStr, enumType.AsFortran());
+    return false;
+  }
+  const auto *caseDerived{evaluate::GetDerivedTypeSpec(*type)};
+  if (!caseDerived || !caseDerived->IsEnumerationType() ||
+      &caseDerived->typeSymbol() != &enumType.typeSymbol()) {
+    context.Say(expr.source,
+        "CASE value has type '%s' which is not compatible with the SELECT CASE expression's type '%s'"_err_en_US,
+        type->AsFortran(), enumType.AsFortran());
+    return false;
+  }
+  // Extract the ordinal integer from the constant enum value
+  parser::Messages buffer;
+  parser::ContextualMessages foldingMessages{expr.source, &buffer};
+  evaluate::FoldingContext foldingContext{
+      context.foldingContext(), foldingMessages};
+  auto folded{evaluate::Fold(foldingContext, SomeExpr{*x->v})};
+  if (auto sc{
+          evaluate::GetScalarConstantValue<evaluate::SomeDerived>(folded)}) {
+    if (auto ordExpr{sc->Find(ordSym)}) {
+      x->v = std::move(*ordExpr);
+      return true;
+    }
+  }
+  context.Say(expr.source,
+      "CASE value (%s) must be a constant scalar"_err_en_US, x->v->AsFortran());
+  return false;
+}
+
+// A parse-tree visitor that converts every enumeration CASE value it
+// encounters to its ordinal integer value, recording whether all
+// conversions succeeded.
+struct EnumCaseValueConverter {
+  template <typename A> bool Pre(const A &) { return true; }
+  template <typename A> void Post(const A &) {}
+  bool Pre(const parser::CaseValue &val) {
+    if (!ConvertEnumCaseValue(context, val, enumType, ordSym)) {
+      ok = false;
+    }
+    return false;
+  }
+  SemanticsContext &context;
+  const semantics::DerivedTypeSpec &enumType;
+  const semantics::Symbol &ordSym;
+  bool ok{true};
+};
+
+// Walk all CASE values in an enumeration SELECT CASE, check type
+// compatibility, and convert each to its ordinal integer value.
+static bool ConvertEnumCaseValues(SemanticsContext &context,
+    const std::list<parser::CaseConstruct::Case> &cases,
+    const semantics::DerivedTypeSpec &enumType) {
+  const auto *scope{enumType.GetScope()};
+  if (!scope) {
+    return false;
+  }
+  auto ordIter{scope->find(
+      semantics::SourceName{semantics::DerivedTypeDetails::ordinalComponentName,
+          sizeof(semantics::DerivedTypeDetails::ordinalComponentName) - 1})};
+  if (ordIter == scope->end()) {
+    return false;
+  }
+  const semantics::Symbol &ordSym{*ordIter->second};
+  EnumCaseValueConverter visitor{context, enumType, ordSym};
+  parser::Walk(cases, visitor);
----------------
MattPD wrote:

A `SELECT CASE` nested inside an arm of an enumeration `SELECT CASE` is rejected, even when the inner construct is valid. Built at this revision and ran:

```fortran
subroutine s
  enumeration type :: color
    enumerator :: red, green, blue
  end enumeration type
  type(color) :: c
  integer :: j
  c = red
  j = 5
  select case (c)
  case (red)
    select case (j)        ! ordinary integer SELECT CASE
    case (1)
      print *, "one"
    end select
  case (green)
  end select
end subroutine
```

```
error: CASE value has type 'INTEGER(4)' which is not compatible with the SELECT CASE expression's type 'ENUMERATION TYPE :: color'
      case (1)
            ^
```

The inner `case (1)` belongs to the integer `select case (j)`, but it is checked against the outer `color` selector. `ConvertEnumCaseValues` receives the outer case list, and `parser::Walk` recurses through each arm's body, so `Pre(const parser::CaseValue &)` fires on the case values of any nested `SELECT CASE` and converts them against the outer enumeration type.

The same happens for a nested `SELECT CASE` over a different enumeration type, and over the same enumeration type (where the inner enumerators are then reported as `INTEGER(4)`), at any nesting depth (the inner select can sit under a `DO` or `IF`). An integer or character outer `SELECT CASE` is unaffected, and the outer enum's own arms are still checked correctly. Reverting just this line back to a per-case loop over the `CaseSelector`s and rebuilding makes all of these compile cleanly, so the rejection comes specifically from walking the whole case list.

Would restricting the walk to each case's `CaseStmt`/`CaseSelector`, or having the visitor stop descent at a nested `parser::CaseConstruct`, be the right fix? A regression test with an integer, same-enum, and different-enum `SELECT CASE` nested inside an enumeration `SELECT CASE` would lock it down.

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


More information about the flang-commits mailing list