[flang] Resolve defined operators to specifics
authorTim Keith <tkeith@nvidia.com>
Tue, 22 Oct 2019 16:31:33 +0000 (09:31 -0700)
committerTim Keith <tkeith@nvidia.com>
Wed, 23 Oct 2019 13:25:51 +0000 (06:25 -0700)
Most of these changes involve moving code around so that it case be
used for `DefinedUnary` and `DefinedBinary`. The functional changes are
in the `Analyze` member functions for those cases where the arguments
are now analyzed, the generic is resolved, and a `FunctionRef` is
created.

Add `ArgumentAnalyzer` to handling building of the `ActualArguments`
of a call. This allows the code to be shared with the defined unary
and defined binary cases. Move `AnalyzeActualArgument` and
`AnalyzeActualArgument` into that class (renaming both to `Analyze`).

Create an overload of `GetCalleeAndArguments` for the `Name` case so it
can be used for defined ops where we don't have a `ProcedureDesignator`.

Move `IsGenericDefinedOp` to `tools.h` to make it available to the
new code.

We were using `semantics::CheckExplicitInterface` to resolve a generic
interface to a specific procedure based on actual arguments. The problem
with that is that it performs too many checks. We just want to get the
right specific; there may be errors reported later during call analysis.

To fix this, add a new function, `CheckInterfaceForGeneric`, to perform
this check. It shares code with `CheckExplicitInterface`, but it passes
in a null scope to indicate that the full set of checks aren't
necessary in `CheckExplicitInterfaceArg`. Instead we lift the call to
`TypeAndShape::IsCompatibleWith` out of `CheckExplicitDataArg`, and skip
the latter when there is no scope.

Original-commit: flang-compiler/f18@fff2d1580f26719e0c384c66576aa6620d04faff
Reviewed-on: https://github.com/flang-compiler/f18/pull/786

flang/lib/semantics/check-call.cc
flang/lib/semantics/check-call.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/lib/semantics/mod-file.cc
flang/lib/semantics/tools.cc
flang/lib/semantics/tools.h
flang/test/semantics/modfile32.f90
flang/test/semantics/resolve62.f90

index f91190c..239ae62 100644 (file)
@@ -102,19 +102,12 @@ static void PadShortCharacterActual(evaluate::Expr<evaluate::SomeType> &actual,
 
 static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
     const std::string &dummyName, evaluate::Expr<evaluate::SomeType> &actual,
-    const characteristics::TypeAndShape &actualType,
-    const characteristics::Procedure &proc, evaluate::FoldingContext &context,
-    const Scope &scope) {
+    const characteristics::TypeAndShape &actualType, bool isElemental,
+    evaluate::FoldingContext &context, const Scope &scope) {
 
   // Basic type & rank checking
   parser::ContextualMessages &messages{context.messages()};
-  int dummyRank{evaluate::GetRank(dummy.type.shape())};
-  bool isElemental{dummyRank == 0 &&
-      proc.attrs.test(characteristics::Procedure::Attr::Elemental)};
   PadShortCharacterActual(actual, dummy.type, actualType, messages);
-  dummy.type.IsCompatibleWith(
-      messages, actualType, "dummy argument", "actual argument", isElemental);
-
   bool actualIsPolymorphic{actualType.type().IsPolymorphic()};
   bool dummyIsPolymorphic{dummy.type.type().IsPolymorphic()};
   bool actualIsCoindexed{ExtractCoarrayRef(actual).has_value()};
@@ -235,7 +228,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
             "Declaration of assumed-size array actual argument"_en_US);
       }
     }
-  } else if (actualRank == 0 && dummyRank > 0) {
+  } else if (actualRank == 0 && dummy.type.Rank() > 0) {
     // Actual is scalar, dummy is an array.  15.5.2.4(14), 15.5.2.11
     if (actualIsCoindexed) {
       messages.Say(
@@ -329,7 +322,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
 static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
     const characteristics::DummyArgument &dummy,
     const characteristics::Procedure &proc, evaluate::FoldingContext &context,
-    const Scope &scope) {
+    const Scope *scope) {
   auto &messages{context.messages()};
   std::string dummyName{"dummy argument"};
   if (!dummy.name.empty()) {
@@ -341,8 +334,13 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
             if (auto *expr{arg.UnwrapExpr()}) {
               if (auto type{characteristics::TypeAndShape::Characterize(
                       *expr, context)}) {
-                CheckExplicitDataArg(
-                    object, dummyName, *expr, *type, proc, context, scope);
+                bool isElemental{object.type.Rank() == 0 && proc.IsElemental()};
+                object.type.IsCompatibleWith(context.messages(), *type,
+                    "dummy argument", "actual argument", isElemental);
+                if (scope) {
+                  CheckExplicitDataArg(object, dummyName, *expr, *type,
+                      isElemental, context, *scope);
+                }
               } else if (object.type.type().IsTypelessIntrinsicArgument() &&
                   std::holds_alternative<evaluate::BOZLiteralConstant>(
                       expr->u)) {
@@ -424,9 +422,9 @@ static void RearrangeArguments(const characteristics::Procedure &proc,
   }
 }
 
-parser::Messages CheckExplicitInterface(const characteristics::Procedure &proc,
-    evaluate::ActualArguments &actuals, const evaluate::FoldingContext &context,
-    const Scope &scope) {
+static parser::Messages CheckExplicitInterface(
+    const characteristics::Procedure &proc, evaluate::ActualArguments &actuals,
+    const evaluate::FoldingContext &context, const Scope *scope) {
   parser::Messages buffer;
   parser::ContextualMessages messages{context.messages().at(), &buffer};
   evaluate::FoldingContext localContext{context, messages};
@@ -455,6 +453,18 @@ parser::Messages CheckExplicitInterface(const characteristics::Procedure &proc,
   return buffer;
 }
 
+parser::Messages CheckExplicitInterface(const characteristics::Procedure &proc,
+    evaluate::ActualArguments &actuals, const evaluate::FoldingContext &context,
+    const Scope &scope) {
+  return CheckExplicitInterface(proc, actuals, context, &scope);
+}
+
+bool CheckInterfaceForGeneric(const characteristics::Procedure &proc,
+    evaluate::ActualArguments &actuals,
+    const evaluate::FoldingContext &context) {
+  return CheckExplicitInterface(proc, actuals, context, nullptr).empty();
+}
+
 void CheckArguments(const characteristics::Procedure &proc,
     evaluate::ActualArguments &actuals, evaluate::FoldingContext &context,
     const Scope &scope, bool treatingExternalAsImplicit) {
index 4d40787..6edb720 100644 (file)
@@ -46,5 +46,8 @@ void CheckArguments(const evaluate::characteristics::Procedure &,
 parser::Messages CheckExplicitInterface(
     const evaluate::characteristics::Procedure &, evaluate::ActualArguments &,
     const evaluate::FoldingContext &, const Scope &);
+// Check actual arguments for the purpose of resolving a generic interface.
+bool CheckInterfaceForGeneric(const evaluate::characteristics::Procedure &,
+    evaluate::ActualArguments &, const evaluate::FoldingContext &);
 }
 #endif
index 417aab2..aa6c1a9 100644 (file)
@@ -133,6 +133,28 @@ common::IfNoLvalue<MaybeExpr, WRAPPED> TypedWrapper(
   }
 }
 
+class ArgumentAnalyzer {
+public:
+  explicit ArgumentAnalyzer(ExpressionAnalyzer &context) : context_{context} {}
+  bool success() const { return success_; }
+  ActualArguments &&GetActuals() {
+    CHECK(success_);
+    return std::move(actuals_);
+  }
+  template<typename T> void Analyze(const T &x) {
+    actuals_.emplace_back(context_.Analyze(x));
+    success_ &= actuals_.back().has_value();
+  }
+  void Analyze(const parser::ActualArgSpec &, bool isSubroutine);
+
+private:
+  std::optional<ActualArgument> Analyze(const parser::Expr &);
+
+  ExpressionAnalyzer &context_;
+  ActualArguments actuals_;
+  bool success_{true};
+};
+
 // Wraps a data reference in a typed Designator<>, and a procedure
 // or procedure pointer reference in a ProcedureDesignator.
 MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
@@ -1559,69 +1581,47 @@ static bool CheckCompatibleArguments(
   return true;
 }
 
-const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
-    ActualArguments &actuals, const semantics::Scope &scope) {
+const Symbol *ExpressionAnalyzer::ResolveGeneric(
+    const Symbol &symbol, ActualArguments &actuals) {
   const Symbol *elemental{nullptr};  // matching elemental specific proc
-  const auto &details{symbol.get<semantics::GenericDetails>()};
+  const auto &details{symbol.GetUltimate().get<semantics::GenericDetails>()};
   for (const Symbol *specific : details.specificProcs()) {
     if (std::optional<characteristics::Procedure> procedure{
             characteristics::Procedure::Characterize(
                 ProcedureDesignator{*specific}, context_.intrinsics())}) {
       ActualArguments localActuals{actuals};
-      auto messages{semantics::CheckExplicitInterface(
-          *procedure, localActuals, GetFoldingContext(), scope)};
-      if (messages.empty() &&
-          CheckCompatibleArguments(*procedure, localActuals)) {
-        if (!procedure->IsElemental()) {
-          return specific;  // takes priority over elemental match
+      if (semantics::CheckInterfaceForGeneric(
+              *procedure, localActuals, GetFoldingContext())) {
+        if (CheckCompatibleArguments(*procedure, localActuals)) {
+          if (!procedure->IsElemental()) {
+            return specific;  // takes priority over elemental match
+          }
+          elemental = specific;
         }
-        elemental = specific;
       }
     }
   }
   if (elemental) {
     return elemental;
+  }
+  if (semantics::IsGenericDefinedOp(symbol)) {
+    Say("No specific procedure of generic operator '%s' matches the actual arguments"_err_en_US,
+        symbol.name());
   } else {
     Say("No specific procedure of generic '%s' matches the actual arguments"_err_en_US,
         symbol.name());
-    return nullptr;
   }
+  return nullptr;
 }
 
 auto ExpressionAnalyzer::GetCalleeAndArguments(
     const parser::ProcedureDesignator &pd, ActualArguments &&arguments,
-    bool isSubroutine, const semantics::Scope &scope)
-    -> std::optional<CalleeAndArguments> {
+    bool isSubroutine) -> std::optional<CalleeAndArguments> {
   return std::visit(
       common::visitors{
-          [&](const parser::Name &n) -> std::optional<CalleeAndArguments> {
-            const Symbol *symbol{n.symbol};
-            if (context_.HasError(symbol)) {
-              return std::nullopt;
-            }
-            const Symbol &ultimate{symbol->GetUltimate()};
-            if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) {
-              if (std::optional<SpecificCall> specificCall{
-                      context_.intrinsics().Probe(
-                          CallCharacteristics{n.source, isSubroutine},
-                          arguments, GetFoldingContext())}) {
-                return CalleeAndArguments{ProcedureDesignator{std::move(
-                                              specificCall->specificIntrinsic)},
-                    std::move(specificCall->arguments)};
-              } else {
-                return std::nullopt;
-              }
-            }
-            CheckForBadRecursion(n.source, ultimate);
-            if (ultimate.has<semantics::GenericDetails>()) {
-              symbol = ResolveGeneric(ultimate, arguments, scope);
-            }
-            if (symbol) {
-              return CalleeAndArguments{
-                  ProcedureDesignator{*symbol}, std::move(arguments)};
-            } else {
-              return std::nullopt;
-            }
+          [&](const parser::Name &name) {
+            return GetCalleeAndArguments(
+                name, std::move(arguments), isSubroutine);
           },
           [&](const parser::ProcComponentRef &pcr) {
             return AnalyzeProcedureComponentRef(pcr, std::move(arguments));
@@ -1630,6 +1630,38 @@ auto ExpressionAnalyzer::GetCalleeAndArguments(
       pd.u);
 }
 
+auto ExpressionAnalyzer::GetCalleeAndArguments(
+    const parser::Name &name, ActualArguments &&arguments, bool isSubroutine)
+    -> std::optional<CalleeAndArguments> {
+  const Symbol *symbol{name.symbol};
+  if (context_.HasError(symbol)) {
+    return std::nullopt;
+  }
+  const Symbol &ultimate{symbol->GetUltimate()};
+  if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) {
+    if (std::optional<SpecificCall> specificCall{context_.intrinsics().Probe(
+            CallCharacteristics{name.source, isSubroutine}, arguments,
+            GetFoldingContext())}) {
+      return CalleeAndArguments{
+          ProcedureDesignator{std::move(specificCall->specificIntrinsic)},
+          std::move(specificCall->arguments)};
+    } else {
+      return std::nullopt;
+    }
+  } else {
+    CheckForBadRecursion(name.source, ultimate);
+    if (ultimate.has<semantics::GenericDetails>()) {
+      symbol = ResolveGeneric(*symbol, arguments);
+    }
+    if (symbol) {
+      return CalleeAndArguments{
+          ProcedureDesignator{*symbol}, std::move(arguments)};
+    } else {
+      return std::nullopt;
+    }
+  }
+}
+
 void ExpressionAnalyzer::CheckForBadRecursion(
     parser::CharBlock callSite, const semantics::Symbol &proc) {
   if (const auto *scope{proc.scope()}) {
@@ -1669,41 +1701,6 @@ template<typename A> static const Symbol *AssumedTypeDummy(const A &x) {
   return nullptr;
 }
 
-std::optional<ActualArgument> ExpressionAnalyzer::AnalyzeActualArgument(
-    const parser::Expr &expr) {
-  if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) {
-    return ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}};
-  } else if (MaybeExpr argExpr{Analyze(expr)}) {
-    Expr<SomeType> x{Fold(GetFoldingContext(), std::move(*argExpr))};
-    if (const auto *proc{std::get_if<ProcedureDesignator>(&x.u)}) {
-      if (!std::holds_alternative<SpecificIntrinsic>(proc->u) &&
-          proc->IsElemental()) {  // C1533
-        Say(expr.source,
-            "Non-intrinsic ELEMENTAL procedure cannot be passed as argument"_err_en_US);
-      }
-    }
-    if (auto coarrayRef{ExtractCoarrayRef(x)}) {
-      const Symbol &coarray{coarrayRef->GetLastSymbol()};
-      if (const semantics::DeclTypeSpec * type{coarray.GetType()}) {
-        if (const semantics::DerivedTypeSpec * derived{type->AsDerived()}) {
-          if (auto ptr{semantics::FindPointerUltimateComponent(*derived)}) {
-            if (auto *msg{Say(expr.source,
-                    "Coindexed object '%s' with POINTER ultimate component '%s' cannot be passed as argument"_err_en_US,
-                    coarray.name(), (*ptr)->name())}) {
-              msg->Attach((*ptr)->name(),
-                  "Declaration of POINTER '%s' component of %s"_en_US,
-                  (*ptr)->name(), type->AsFortran());
-            }
-          }
-        }
-      }
-    }
-    return ActualArgument{std::move(x)};
-  } else {
-    return std::nullopt;
-  }
-}
-
 MaybeExpr ExpressionAnalyzer::Analyze(
     const parser::FunctionReference &funcRef) {
   return AnalyzeCall(funcRef.v, false);
@@ -1716,12 +1713,15 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &call) {
 MaybeExpr ExpressionAnalyzer::AnalyzeCall(
     const parser::Call &call, bool isSubroutine) {
   auto save{GetContextualMessages().SetLocation(call.source)};
-  if (auto arguments{AnalyzeArguments(call, isSubroutine)}) {
+  ArgumentAnalyzer analyzer{*this};
+  for (const auto &arg : std::get<std::list<parser::ActualArgSpec>>(call.t)) {
+    analyzer.Analyze(arg, isSubroutine);
+  }
+  if (analyzer.success()) {
     // TODO: map non-intrinsic generic procedure to specific procedure
     if (std::optional<CalleeAndArguments> callee{
             GetCalleeAndArguments(std::get<parser::ProcedureDesignator>(call.t),
-                std::move(*arguments), isSubroutine,
-                context_.FindScope(call.source))}) {
+                analyzer.GetActuals(), isSubroutine)}) {
       if (isSubroutine) {
         CheckCall(call.source, callee->procedureDesignator, callee->arguments);
         // TODO: Package the subroutine call as an expr in the parse tree
@@ -1735,50 +1735,6 @@ MaybeExpr ExpressionAnalyzer::AnalyzeCall(
   return std::nullopt;
 }
 
-std::optional<ActualArguments> ExpressionAnalyzer::AnalyzeArguments(
-    const parser::Call &call, bool isSubroutine) {
-  evaluate::ActualArguments arguments;
-  // TODO: C1002: Allow a whole assumed-size array to appear if the dummy
-  // argument would accept it.  Handle by special-casing the context
-  // ActualArg -> Variable -> Designator.
-  // TODO: Actual arguments that are procedures and procedure pointers need to
-  // be detected and represented (they're not expressions).
-  // TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
-  // TODO: map non-intrinsic generic procedure to specific procedure
-  for (const auto &arg : std::get<std::list<parser::ActualArgSpec>>(call.t)) {
-    std::optional<evaluate::ActualArgument> actual;
-    std::visit(
-        common::visitors{
-            [&](const common::Indirection<parser::Expr> &x) {
-              // TODO: Distinguish & handle procedure name and
-              // proc-component-ref
-              actual = AnalyzeActualArgument(x.value());
-            },
-            [&](const parser::AltReturnSpec &) {
-              if (!isSubroutine) {
-                Say("alternate return specification may not appear on function reference"_err_en_US);
-              }
-            },
-            [&](const parser::ActualArg::PercentRef &) {
-              Say("TODO: %REF() argument"_err_en_US);
-            },
-            [&](const parser::ActualArg::PercentVal &) {
-              Say("TODO: %VAL() argument"_err_en_US);
-            },
-        },
-        std::get<parser::ActualArg>(arg.t).u);
-    if (actual.has_value()) {
-      arguments.emplace_back(std::move(actual));
-      if (const auto &argKW{std::get<std::optional<parser::Keyword>>(arg.t)}) {
-        arguments.back()->keyword = argKW->v.source;
-      }
-    } else {
-      return std::nullopt;
-    }
-  }
-  return arguments;
-}
-
 static bool IsExternalCalledImplicitly(
     parser::CharBlock callSite, const ProcedureDesignator &proc) {
   if (const auto *symbol{proc.GetSymbol()}) {
@@ -1907,11 +1863,18 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) {
   return MakeFunctionRef(loc, ActualArguments{std::move(*arg)});
 }
 
-MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &) {
-  Say("TODO: DefinedUnary unimplemented"_err_en_US);
+MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
+  const auto &name{std::get<parser::DefinedOpName>(x.t).v};
+  ArgumentAnalyzer analyzer{*this};
+  analyzer.Analyze(std::get<1>(x.t));
+  if (analyzer.success()) {
+    if (auto callee{GetCalleeAndArguments(name, analyzer.GetActuals())}) {
+      return MakeFunctionRef(name.source,
+          std::move(callee->procedureDesignator), std::move(callee->arguments));
+    }
+  }
   return std::nullopt;
 }
-
 // Binary (dyadic) operations
 
 // TODO: check defined operators for illegal intrinsic operator cases
@@ -2076,8 +2039,17 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::XOR &x) {
   return LogicalHelper(*this, LogicalOperator::Neqv, x);
 }
 
-MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
-  Say("TODO: DefinedBinary unimplemented"_err_en_US);
+MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedBinary &x) {
+  const auto &name{std::get<parser::DefinedOpName>(x.t).v};
+  ArgumentAnalyzer analyzer{*this};
+  analyzer.Analyze(std::get<1>(x.t));
+  analyzer.Analyze(std::get<2>(x.t));
+  if (analyzer.success()) {
+    if (auto callee{GetCalleeAndArguments(name, analyzer.GetActuals())}) {
+      return MakeFunctionRef(name.source,
+          std::move(callee->procedureDesignator), std::move(callee->arguments));
+    }
+  }
   return std::nullopt;
 }
 
@@ -2390,8 +2362,84 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(
     return std::nullopt;
   }
 }
+
+void ArgumentAnalyzer::Analyze(
+    const parser::ActualArgSpec &arg, bool isSubroutine) {
+  // TODO: C1002: Allow a whole assumed-size array to appear if the dummy
+  // argument would accept it.  Handle by special-casing the context
+  // ActualArg -> Variable -> Designator.
+  // TODO: Actual arguments that are procedures and procedure pointers need to
+  // be detected and represented (they're not expressions).
+  // TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
+  std::optional<ActualArgument> actual;
+  std::visit(
+      common::visitors{
+          [&](const common::Indirection<parser::Expr> &x) {
+            // TODO: Distinguish & handle procedure name and
+            // proc-component-ref
+            actual = Analyze(x.value());
+          },
+          [&](const parser::AltReturnSpec &) {
+            if (!isSubroutine) {
+              context_.Say("alternate return specification may not appear on"
+                           " function reference"_err_en_US);
+            }
+          },
+          [&](const parser::ActualArg::PercentRef &) {
+            context_.Say("TODO: %REF() argument"_err_en_US);
+          },
+          [&](const parser::ActualArg::PercentVal &) {
+            context_.Say("TODO: %VAL() argument"_err_en_US);
+          },
+      },
+      std::get<parser::ActualArg>(arg.t).u);
+  if (actual.has_value()) {
+    if (const auto &argKW{std::get<std::optional<parser::Keyword>>(arg.t)}) {
+      actual->keyword = argKW->v.source;
+    }
+    actuals_.emplace_back(std::move(*actual));
+  } else {
+    success_ = false;
+  }
+}
+
+std::optional<ActualArgument> ArgumentAnalyzer::Analyze(
+    const parser::Expr &expr) {
+  if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) {
+    return ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}};
+  } else if (MaybeExpr argExpr{context_.Analyze(expr)}) {
+    Expr<SomeType> x{Fold(context_.GetFoldingContext(), std::move(*argExpr))};
+    if (const auto *proc{std::get_if<ProcedureDesignator>(&x.u)}) {
+      if (!std::holds_alternative<SpecificIntrinsic>(proc->u) &&
+          proc->IsElemental()) {  // C1533
+        context_.Say(expr.source,
+            "Non-intrinsic ELEMENTAL procedure cannot be passed as argument"_err_en_US);
+      }
+    }
+    if (auto coarrayRef{ExtractCoarrayRef(x)}) {
+      const Symbol &coarray{coarrayRef->GetLastSymbol()};
+      if (const semantics::DeclTypeSpec * type{coarray.GetType()}) {
+        if (const semantics::DerivedTypeSpec * derived{type->AsDerived()}) {
+          if (auto ptr{semantics::FindPointerUltimateComponent(*derived)}) {
+            if (auto *msg{context_.Say(expr.source,
+                    "Coindexed object '%s' with POINTER ultimate component '%s' cannot be passed as argument"_err_en_US,
+                    coarray.name(), (*ptr)->name())}) {
+              msg->Attach((*ptr)->name(),
+                  "Declaration of POINTER '%s' component of %s"_en_US,
+                  (*ptr)->name(), type->AsFortran());
+            }
+          }
+        }
+      }
+    }
+    return ActualArgument{std::move(x)};
+  } else {
+    return std::nullopt;
+  }
 }
 
+}  // namespace Fortran::evaluate
+
 namespace Fortran::semantics {
 evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
     SemanticsContext &context, common::TypeCategory category,
index 25b702b..dfbbcc7 100644 (file)
@@ -326,11 +326,12 @@ private:
       const parser::Call &, bool isSubroutine);
   std::optional<characteristics::Procedure> CheckCall(
       parser::CharBlock, const ProcedureDesignator &, ActualArguments &);
-  const Symbol *ResolveGeneric(
-      const Symbol &, ActualArguments &, const semantics::Scope &);
+  const Symbol *ResolveGeneric(const Symbol &, ActualArguments &);
+  std::optional<CalleeAndArguments> GetCalleeAndArguments(
+      const parser::Name &, ActualArguments &&, bool isSubroutine = false);
   std::optional<CalleeAndArguments> GetCalleeAndArguments(
       const parser::ProcedureDesignator &, ActualArguments &&,
-      bool isSubroutine, const semantics::Scope &);
+      bool isSubroutine);
 
   void CheckForBadRecursion(parser::CharBlock, const semantics::Symbol &);
   bool EnforceTypeConstraint(parser::CharBlock, const MaybeExpr &, TypeCategory,
index 6b84e41..504aabc 100644 (file)
@@ -17,6 +17,7 @@
 #include "scope.h"
 #include "semantics.h"
 #include "symbol.h"
+#include "tools.h"
 #include "../evaluate/tools.h"
 #include "../parser/message.h"
 #include "../parser/parsing.h"
@@ -356,11 +357,6 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) {
   }
 }
 
-static bool IsDefinedOp(const Symbol &symbol) {
-  const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()};
-  return details && details->kind() == GenericKind::DefinedOp;
-}
-
 static bool IsIntrinsicOp(const Symbol &symbol) {
   if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
     GenericKind kind{details->kind()};
@@ -371,7 +367,7 @@ static bool IsIntrinsicOp(const Symbol &symbol) {
 }
 
 static std::ostream &PutGenericName(std::ostream &os, const Symbol &symbol) {
-  if (IsDefinedOp(symbol)) {
+  if (IsGenericDefinedOp(symbol)) {
     return os << "operator(" << symbol.name() << ')';
   } else {
     return os << symbol.name();
index 5189f87..c6dab3c 100644 (file)
@@ -80,6 +80,11 @@ const Scope *FindPureProcedureContaining(const Scope *scope) {
   return nullptr;
 }
 
+bool IsGenericDefinedOp(const Symbol &symbol) {
+  const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()};
+  return details && details->kind() == GenericKind::DefinedOp;
+}
+
 bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
   const auto &objects{block.get<CommonBlockDetails>().objects()};
   auto found{std::find(objects.begin(), objects.end(), &object)};
index 378c6c8..b8ed73d 100644 (file)
@@ -50,6 +50,7 @@ const Symbol *FindFunctionResult(const Symbol &);
 // Return the Symbol of the variable of a construct association, if it exists
 const Symbol *GetAssociationRoot(const Symbol &);
 
+bool IsGenericDefinedOp(const Symbol &);
 bool IsCommonBlockContaining(const Symbol &block, const Symbol &object);
 bool DoesScopeContain(const Scope *maybeAncestor, const Scope &maybeDescendent);
 bool DoesScopeContain(const Scope *, const Symbol &);
index c904cbb..86cf643 100644 (file)
@@ -233,3 +233,105 @@ end
 !  real(4) :: y(1_8:ubound(f_elem(x), 1_4))
 ! end
 !end
+
+! Resolve defined unary operator based on type
+module m4
+  interface operator(.foo.)
+    pure integer(8) function f_real(x)
+      real, intent(in) :: x
+    end
+    pure integer(8) function f_integer(x)
+      integer, intent(in) :: x
+    end
+  end interface
+contains
+  subroutine s1(x, y)
+    real :: x
+    real :: y(.foo. x)  ! resolves to f_real
+  end
+  subroutine s2(x, y)
+    integer :: x
+    real :: y(.foo. x)  ! resolves to f_integer
+  end
+end
+!Expect: m4.mod
+!module m4
+! interface operator(.foo.)
+!  procedure :: f_real
+!  procedure :: f_integer
+! end interface
+! interface
+!  pure function f_real(x)
+!   real(4), intent(in) :: x
+!   integer(8) :: f_real
+!  end
+! end interface
+! interface
+!  pure function f_integer(x)
+!   integer(4), intent(in) :: x
+!   integer(8) :: f_integer
+!  end
+! end interface
+!contains
+! subroutine s1(x, y)
+!  real(4) :: x
+!  real(4) :: y(1_8:f_real(x))
+! end
+! subroutine s2(x, y)
+!  integer(4) :: x
+!  real(4) :: y(1_8:f_integer(x))
+! end
+!end
+
+! Resolve defined binary operator based on type
+module m5
+  interface operator(.foo.)
+    pure integer(8) function f1(x, y)
+      real, intent(in) :: x
+      real, intent(in) :: y
+    end
+    pure integer(8) function f2(x, y)
+      real, intent(in) :: x
+      complex, intent(in) :: y
+    end
+  end interface
+contains
+  subroutine s1(x, y)
+    complex :: x
+    real :: y(1.0 .foo. x)  ! resolves to f2
+  end
+  subroutine s2(x, y)
+    real :: x
+    real :: y(1.0 .foo. x)  ! resolves to f1
+  end
+end
+!Expect: m5.mod
+!module m5
+! interface operator(.foo.)
+!  procedure :: f1
+!  procedure :: f2
+! end interface
+! interface
+!  pure function f1(x, y)
+!   real(4), intent(in) :: x
+!   real(4), intent(in) :: y
+!   integer(8) :: f1
+!  end
+! end interface
+! interface
+!  pure function f2(x, y)
+!   real(4), intent(in) :: x
+!   complex(4), intent(in) :: y
+!   integer(8) :: f2
+!  end
+! end interface
+!contains
+! subroutine s1(x, y)
+!  complex(4) :: x
+!  real(4) :: y(1_8:f2(1._4, x))
+! end
+! subroutine s2(x, y)
+!  real(4) :: x
+!  real(4) :: y(1_8:f1(1._4, x))
+! end
+!end
index c8d9c2e..9930fee 100644 (file)
@@ -42,3 +42,50 @@ subroutine s2
   a = f(1.0)
   a = f(y)  !TODO: this should resolve to f2 -- should get error here
 end
+
+! Resolve named operator
+subroutine s3
+  interface operator(.foo.)
+    pure integer(8) function f_real(x, y)
+      real, intent(in) :: x, y
+    end
+    pure integer(8) function f_integer(x, y)
+      integer, intent(in) :: x, y
+    end
+  end interface
+  logical :: a, b, c
+  x = y .foo. z  ! OK: f_real
+  i = j .foo. k  ! OK: f_integer
+  !ERROR: No specific procedure of generic operator '.foo.' matches the actual arguments
+  a = b .foo. c
+end
+
+! Generic resolves successfully but error analyzing call
+module m4
+  real, protected :: x
+  real :: y
+  interface s
+    subroutine s1(x)
+      real, intent(out) :: x
+    end
+    subroutine s2(x, y)
+      real :: x, y
+    end
+  end interface
+end
+subroutine s4a
+  use m4
+  real :: z
+  !OK
+  call s(z)
+end
+subroutine s4b
+  use m4
+  !ERROR: Actual argument associated with INTENT(OUT) dummy argument 'x=' must be definable
+  call s(x)
+end
+pure subroutine s4c
+  use m4
+  !ERROR: Actual argument associated with INTENT(OUT) dummy argument 'x=' must be definable
+  call s(y)
+end