[flang-commits] [flang] [flang][Semantics] Enforce F2023 C1545 only for generic references (PR #219581)

Caroline Newcombe via flang-commits flang-commits at lists.llvm.org
Wed Sep 30 11:46:36 PDT 2026


================
@@ -1479,54 +1480,69 @@ static void CheckConditionalArg(
           "Each consequent-arg in conditional argument associated with a coarray %s must be a coarray"_err_en_US,
           dummyName);
     }
-    // C1544: the requirement that each consequent-arg match the dummy's
-    // ALLOCATABLE/POINTER attribute is enforced by the standard
-    // explicit-interface check (checkOneExpr) run on each non-.NIL.
-    // consequent below.
   }};
   condArg.ForEachConsequent(checkOneConsequent);
-  // C1545: in a reference to a generic procedure, each consequent-arg shall
-  // have the same corank, and if any has the ALLOCATABLE or POINTER attribute,
-  // each shall have it.  Strictly, this requirement applies only to references
-  // to generic procedures, where it avoids ambiguity when resolving the generic
-  // to a specific procedure; for a specific procedure reference these
-  // combinations are otherwise allowed.  For now it is enforced unconditionally
-  // here.
-  // TODO: move this check into generic resolution (ResolveGeneric) and enforce
-  // it precisely, i.e. only for references to generic procedures, where the
-  // ambiguity it guards against can actually arise.
-  std::optional<int> firstCorank;
-  std::optional<bool> firstIsAllocatable;
-  std::optional<bool> firstIsPointer;
-  auto checkConsistency{[&](const evaluate::ActualArgument::ConditionalArg::
-                                Consequent &cons) {
-    if (!cons) {
-      return;
+}
+
+// F2023 C1545: in a reference to a generic procedure, each consequent-arg
+// shall have the same corank, and if any consequent-arg has the ALLOCATABLE
+// or POINTER attribute, each consequent-arg shall have that attribute.
+// These properties decide which specific procedures an actual argument may be
+// associated with, so letting them differ would make generic resolution depend
+// on a condition whose value is not known until run time.  A reference to a
+// specific procedure has nothing to resolve, and these combinations are
+// allowed there.
+bool CheckConditionalArgsInGenericReference(
+    const evaluate::ActualArguments &actuals,
+    parser::ContextualMessages &messages) {
+  bool anyReported{false};
+  for (const std::optional<evaluate::ActualArgument> &arg : actuals) {
+    const auto *condArg{arg ? arg->GetConditionalArg() : nullptr};
+    if (!condArg) {
+      continue;
+    }
+    const auto *first{condArg->FirstNonNilConsequent()};
+    if (!first) { // every consequent-arg is .NIL.
+      continue;
+    }
+    const int firstCorank{evaluate::GetCorank(*first)};
+    const bool firstIsAllocatable{evaluate::IsAllocatableDesignator(*first)};
+    // Use the same POINTER test as generic resolution, which accepts a
+    // pointer-valued function reference.
+    const bool firstIsPointer{evaluate::IsObjectPointer(*first)};
+    bool corankDiffers{false};
+    bool allocatableDiffers{false};
+    bool pointerDiffers{false};
+    // Comparing the first consequent-arg against itself is a no-op, so it needs
+    // no special case here.
+    condArg->ForEachConsequent(
+        [&](const evaluate::ActualArgument::ConditionalArg::Consequent &cons) {
+          if (!cons) { // .NIL. has no corank or attributes to compare
+            return;
+          }
+          const auto &consExpr{cons->value()};
+          corankDiffers |= evaluate::GetCorank(consExpr) != firstCorank;
+          allocatableDiffers |=
+              evaluate::IsAllocatableDesignator(consExpr) != firstIsAllocatable;
+          pointerDiffers |=
+              evaluate::IsObjectPointer(consExpr) != firstIsPointer;
+        });
+    // Report each violated requirement once, however long the chain is.
+    if (corankDiffers) {
+      messages.Say(
+          "In a reference to a generic procedure, all consequent-args in a conditional argument must have the same corank"_err_en_US);
     }
-    auto &consExpr{cons->value()};
-    int corank{evaluate::GetCorank(consExpr)};
-    bool isAlloc{evaluate::IsAllocatableDesignator(consExpr)};
-    bool isPtr{evaluate::IsObjectPointer(consExpr)};
-    if (!firstCorank) {
-      firstCorank = corank;
-      firstIsAllocatable = isAlloc;
-      firstIsPointer = isPtr;
-    } else {
-      if (corank != *firstCorank) {
-        messages.Say(
-            "All consequent-args in a conditional argument must have the same corank"_err_en_US);
-      }
-      if (isAlloc != *firstIsAllocatable) {
-        messages.Say(
-            "If any consequent-arg in a conditional argument has the ALLOCATABLE attribute, each must have it"_err_en_US);
-      }
-      if (isPtr != *firstIsPointer) {
-        messages.Say(
-            "If any consequent-arg in a conditional argument has the POINTER attribute, each must have it"_err_en_US);
-      }
+    if (allocatableDiffers) {
+      messages.Say(
----------------
cenewcombe wrote:

`arg->sourceLocation()`

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


More information about the flang-commits mailing list