[flang] Check discrepancies between local & available global subprograms
authorPeter Klausler <pklausler@nvidia.com>
Thu, 1 Dec 2022 00:11:20 +0000 (16:11 -0800)
committerPeter Klausler <pklausler@nvidia.com>
Fri, 2 Dec 2022 19:11:31 +0000 (11:11 -0800)
When a scope declares the name and perhaps some characteristics of
an external subprogram using any of the many means that Fortran supplies
for doing such a thing, and that external subprogram's definition is
available, check the local declaration against the external definition.
In particular, if the global definition's interface cannot be called
by means of an implicit interface, ensure that references are via an
explicit and compatible interface.

Further, extend call site checking so that when a local declaration
exists for a known global symbol and the arguments are valid for that
local declaration, the arguments are checked against the global's
interface, just are is already done when no local declaration exists.

Differential Revision: https://reviews.llvm.org/D139042

15 files changed:
flang/include/flang/Semantics/symbol.h
flang/include/flang/Semantics/tools.h
flang/lib/Evaluate/tools.cpp
flang/lib/Semantics/check-call.cpp
flang/lib/Semantics/check-call.h
flang/lib/Semantics/check-declarations.cpp
flang/lib/Semantics/expression.cpp
flang/lib/Semantics/symbol.cpp
flang/lib/Semantics/tools.cpp
flang/test/Semantics/global01.f90 [new file with mode: 0644]
flang/test/Semantics/local-vs-global.f90 [new file with mode: 0644]
flang/test/Semantics/procinterface01.f90
flang/test/Semantics/resolve102.f90
flang/test/Semantics/resolve53.f90
flang/test/Semantics/resolve62.f90

index ad01b12..bed4104 100644 (file)
@@ -698,7 +698,7 @@ private:
   Details details_;
 
   Symbol() {} // only created in class Symbols
-  const std::string GetDetailsName() const;
+  std::string GetDetailsName() const;
   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &);
   friend llvm::raw_ostream &DumpForUnparse(
       llvm::raw_ostream &, const Symbol &, bool);
index c1d58fa..7b2c4bf 100644 (file)
@@ -53,6 +53,7 @@ const Symbol *FindInterface(const Symbol &);
 const Symbol *FindSubprogram(const Symbol &);
 const Symbol *FindFunctionResult(const Symbol &);
 const Symbol *FindOverriddenBinding(const Symbol &);
+const Symbol *FindGlobal(const Symbol &);
 
 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &);
 const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &);
index dce5dda..1dd6414 100644 (file)
@@ -1338,7 +1338,12 @@ bool IsFunction(const Scope &scope) {
 
 bool IsProcedure(const Symbol &symbol) {
   return common::visit(common::visitors{
-                           [](const SubprogramDetails &) { return true; },
+                           [&symbol](const SubprogramDetails &) {
+                             const Scope *scope{symbol.scope()};
+                             // Main programs & BLOCK DATA are not procedures.
+                             return !scope ||
+                                 scope->kind() == Scope::Kind::Subprogram;
+                           },
                            [](const SubprogramNameDetails &) { return true; },
                            [](const ProcEntityDetails &) { return true; },
                            [](const GenericDetails &) { return true; },
index 45b5c29..37db60f 100644 (file)
@@ -960,7 +960,7 @@ bool CheckInterfaceForGeneric(const characteristics::Procedure &proc,
            .AnyFatalError();
 }
 
-void CheckArguments(const characteristics::Procedure &proc,
+bool CheckArguments(const characteristics::Procedure &proc,
     evaluate::ActualArguments &actuals, evaluate::FoldingContext &context,
     const Scope &scope, bool treatingExternalAsImplicit,
     const evaluate::SpecificIntrinsic *intrinsic) {
@@ -980,21 +980,25 @@ void CheckArguments(const characteristics::Procedure &proc,
       if (auto *msgs{messages.messages()}) {
         msgs->Annex(std::move(buffer));
       }
-      return; // don't pile on
+      return false; // don't pile on
     }
   }
   if (explicitInterface) {
     auto buffer{
         CheckExplicitInterface(proc, actuals, context, scope, intrinsic)};
-    if (treatingExternalAsImplicit && !buffer.empty()) {
-      if (auto *msg{messages.Say(
-              "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) {
-        buffer.AttachTo(*msg, parser::Severity::Because);
+    if (!buffer.empty()) {
+      if (treatingExternalAsImplicit && !buffer.empty()) {
+        if (auto *msg{messages.Say(
+                "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) {
+          buffer.AttachTo(*msg, parser::Severity::Because);
+        }
       }
-    }
-    if (auto *msgs{messages.messages()}) {
-      msgs->Annex(std::move(buffer));
+      if (auto *msgs{messages.messages()}) {
+        msgs->Annex(std::move(buffer));
+      }
+      return false;
     }
   }
+  return true;
 }
 } // namespace Fortran::semantics
index f3a26f5..cef77f3 100644 (file)
@@ -30,8 +30,9 @@ class Scope;
 // Argument treatingExternalAsImplicit should be true when the called procedure
 // does not actually have an explicit interface at the call site, but
 // its characteristics are known because it is a subroutine or function
-// defined at the top level in the same source file.
-void CheckArguments(const evaluate::characteristics::Procedure &,
+// defined at the top level in the same source file.  Returns false if
+// messages were created, true if all is well.
+bool CheckArguments(const evaluate::characteristics::Procedure &,
     evaluate::ActualArguments &, evaluate::FoldingContext &, const Scope &,
     bool treatingExternalAsImplicit,
     const evaluate::SpecificIntrinsic *intrinsic);
index 85dbbb1..11367c4 100644 (file)
@@ -65,6 +65,7 @@ private:
   void CheckArraySpec(const Symbol &, const ArraySpec &);
   void CheckProcEntity(const Symbol &, const ProcEntityDetails &);
   void CheckSubprogram(const Symbol &, const SubprogramDetails &);
+  void CheckLocalVsGlobal(const Symbol &);
   void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &);
   void CheckDerivedType(const Symbol &, const DerivedTypeDetails &);
   bool CheckFinal(
@@ -103,12 +104,12 @@ private:
     return subp && subp->isInterface();
   }
   template <typename... A>
-  void SayWithDeclaration(const Symbol &symbol, A &&...x) {
-    if (parser::Message * msg{messages_.Say(std::forward<A>(x)...)}) {
-      if (messages_.at().begin() != symbol.name().begin()) {
-        evaluate::AttachDeclaration(*msg, symbol);
-      }
+  parser::Message *SayWithDeclaration(const Symbol &symbol, A &&...x) {
+    parser::Message *msg{messages_.Say(std::forward<A>(x)...)};
+    if (msg && messages_.at().begin() != symbol.name().begin()) {
+      evaluate::AttachDeclaration(*msg, symbol);
     }
+    return msg;
   }
   bool IsResultOkToDiffer(const FunctionResult &);
   void CheckBindC(const Symbol &);
@@ -199,7 +200,7 @@ void CheckHelper::Check(
     const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) {
   if (type.category() == DeclTypeSpec::Character) {
     Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters);
-  } else if (const DerivedTypeSpec * derived{type.AsDerived()}) {
+  } else if (const DerivedTypeSpec *derived{type.AsDerived()}) {
     for (auto &parm : derived->parameters()) {
       Check(parm.second, canHaveAssumedTypeParameters);
     }
@@ -346,7 +347,7 @@ void CheckHelper::Check(const Symbol &symbol) {
       messages_.Say(
           "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US);
     }
-    if (const Symbol * result{FindFunctionResult(symbol)}) {
+    if (const Symbol *result{FindFunctionResult(symbol)}) {
       if (IsPointer(*result)) {
         messages_.Say(
             "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US);
@@ -449,7 +450,7 @@ void CheckHelper::CheckValue(
 
 void CheckHelper::CheckAssumedTypeEntity( // C709
     const Symbol &symbol, const ObjectEntityDetails &details) {
-  if (const DeclTypeSpec * type{symbol.GetType()};
+  if (const DeclTypeSpec *type{symbol.GetType()};
       type && type->category() == DeclTypeSpec::TypeStar) {
     if (!IsDummy(symbol)) {
       messages_.Say(
@@ -539,7 +540,7 @@ void CheckHelper::CheckObjectEntity(
             symbol.name());
       }
     }
-    if (const DeclTypeSpec * type{details.type()}) {
+    if (const DeclTypeSpec *type{details.type()}) {
       if (IsBadCoarrayType(type->AsDerived())) { // C747 & C824
         messages_.Say(
             "Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US,
@@ -567,11 +568,11 @@ void CheckHelper::CheckObjectEntity(
         messages_.Say(
             "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US);
       } else if (IsIntentOut(symbol)) {
-        if (const DeclTypeSpec * type{details.type()}) {
+        if (const DeclTypeSpec *type{details.type()}) {
           if (type && type->IsPolymorphic()) { // C1588
             messages_.Say(
                 "An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US);
-          } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
+          } else if (const DerivedTypeSpec *derived{type->AsDerived()}) {
             if (FindUltimateComponent(*derived, [](const Symbol &x) {
                   const DeclTypeSpec *type{x.GetType()};
                   return type && type->IsPolymorphic();
@@ -661,7 +662,7 @@ void CheckHelper::CheckObjectEntity(
           "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US);
     }
   }
-  if (const DeclTypeSpec * type{details.type()}) { // C708
+  if (const DeclTypeSpec *type{details.type()}) { // C708
     if (type->IsPolymorphic() &&
         !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) ||
             IsDummy(symbol))) {
@@ -812,7 +813,9 @@ void CheckHelper::CheckProcEntity(
       messages_.Say(
           "An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US);
     }
-    const Symbol *interface { details.interface().symbol() };
+    const Symbol *interface {
+      details.interface().symbol()
+    };
     if (!symbol.attrs().test(Attr::INTRINSIC) &&
         (IsElementalProcedure(symbol) ||
             (interface && !interface->attrs().test(Attr::INTRINSIC) &&
@@ -844,7 +847,7 @@ void CheckHelper::CheckProcEntity(
   }
   if (symbol.attrs().test(Attr::POINTER)) {
     CheckPointerInitialization(symbol);
-    if (const Symbol * interface{details.interface().symbol()}) {
+    if (const Symbol *interface{details.interface().symbol()}) {
       const Symbol &ultimate{interface->GetUltimate()};
       if (ultimate.attrs().test(Attr::INTRINSIC)) {
         if (const auto intrinsic{
@@ -867,6 +870,7 @@ void CheckHelper::CheckProcEntity(
         "Procedure '%s' with SAVE attribute must also have POINTER attribute"_err_en_US,
         symbol.name());
   }
+  CheckLocalVsGlobal(symbol);
 }
 
 // When a module subprogram has the MODULE prefix the following must match
@@ -931,10 +935,10 @@ bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) {
 
 void CheckHelper::CheckSubprogram(
     const Symbol &symbol, const SubprogramDetails &details) {
-  if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
+  if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
     SubprogramMatchHelper{*this}.Check(symbol, *iface);
   }
-  if (const Scope * entryScope{details.entryScope()}) {
+  if (const Scope *entryScope{details.entryScope()}) {
     // ENTRY 15.6.2.6, esp. C1571
     std::optional<parser::MessageFixedText> error;
     const Symbol *subprogram{entryScope->symbol()};
@@ -980,10 +984,56 @@ void CheckHelper::CheckSubprogram(
       }
     }
   }
-  if (details.isInterface() && !details.isDummy() && details.isFunction() &&
-      IsAssumedLengthCharacter(details.result())) { // C721
-    messages_.Say(details.result().name(),
-        "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US);
+  if (details.isInterface()) {
+    if (!details.isDummy() && details.isFunction() &&
+        IsAssumedLengthCharacter(details.result())) { // C721
+      messages_.Say(details.result().name(),
+          "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US);
+    }
+  }
+  CheckLocalVsGlobal(symbol);
+}
+
+void CheckHelper::CheckLocalVsGlobal(const Symbol &symbol) {
+  if (IsProcedure(symbol) && IsExternal(symbol)) {
+    if (const Symbol *global{FindGlobal(symbol)}; global && global != &symbol) {
+      std::string interfaceName{symbol.name().ToString()};
+      if (const auto *bind{symbol.GetBindName()}) {
+        interfaceName = *bind;
+      }
+      std::string definitionName{global->name().ToString()};
+      if (const auto *bind{global->GetBindName()}) {
+        definitionName = *bind;
+      }
+      if (interfaceName == definitionName) {
+        parser::Message *msg{nullptr};
+        if (!IsProcedure(*global)) {
+          if (symbol.flags().test(Symbol::Flag::Function) ||
+              symbol.flags().test(Symbol::Flag::Subroutine)) {
+            msg = messages_.Say(
+                "The global entity '%s' corresponding to the local procedure '%s' is not a callable subprogram"_err_en_US,
+                global->name(), symbol.name());
+          }
+        } else if (auto chars{Characterize(symbol)}) {
+          if (auto globalChars{Characterize(*global)}) {
+            if (chars->HasExplicitInterface()) {
+              std::string whyNot;
+              if (!chars->IsCompatibleWith(*globalChars, &whyNot)) {
+                msg = messages_.Say(
+                    "The global subprogram '%s' is not compatible with its local procedure declaration (%s)"_warn_en_US,
+                    global->name(), whyNot);
+              }
+            } else if (!globalChars->CanBeCalledViaImplicitInterface()) {
+              msg = messages_.Say(
+                  "The global subprogram '%s' may not be referenced via the implicit interface '%s'"_err_en_US,
+                  global->name(), symbol.name());
+            }
+          }
+        }
+        evaluate::AttachDeclaration(msg, *global);
+        evaluate::AttachDeclaration(msg, symbol);
+      }
+    }
   }
 }
 
@@ -1004,7 +1054,7 @@ void CheckHelper::CheckDerivedType(
       (derivedType.attrs().test(Attr::BIND_C) || details.sequence())) {
     messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US);
   }
-  if (const DeclTypeSpec * parent{FindParentTypeSpec(derivedType)}) {
+  if (const DeclTypeSpec *parent{FindParentTypeSpec(derivedType)}) {
     const DerivedTypeSpec *parentDerived{parent->AsDerived()};
     if (!IsExtensibleType(parentDerived)) { // C705
       messages_.Say("The parent type is not extensible"_err_en_US);
@@ -1091,7 +1141,7 @@ bool CheckHelper::CheckFinal(
   const Symbol *errSym{&subroutine};
   if (const auto *details{subroutine.detailsIf<SubprogramDetails>()}) {
     if (!details->dummyArgs().empty()) {
-      if (const Symbol * argSym{details->dummyArgs()[0]}) {
+      if (const Symbol *argSym{details->dummyArgs()[0]}) {
         errSym = argSym;
       }
     }
@@ -1230,7 +1280,7 @@ void CheckHelper::CheckSpecificsAreDistinguishable(
   }
   DistinguishabilityHelper helper{context_};
   for (const Symbol &specific : details.specificProcs()) {
-    if (const Procedure * procedure{Characterize(specific)}) {
+    if (const Procedure *procedure{Characterize(specific)}) {
       if (procedure->HasExplicitInterface()) {
         helper.Add(generic, kind, specific, *procedure);
       } else {
@@ -1573,7 +1623,9 @@ void CheckHelper::CheckPassArg(
     return;
   }
   const auto &name{proc.name()};
-  const Symbol *interface { interface0 ? FindInterface(*interface0) : nullptr };
+  const Symbol *interface {
+    interface0 ? FindInterface(*interface0) : nullptr
+  };
   if (!interface) {
     messages_.Say(name,
         "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US,
@@ -1683,7 +1735,7 @@ void CheckHelper::CheckProcBinding(
   const Scope &dtScope{symbol.owner()};
   CHECK(dtScope.kind() == Scope::Kind::DerivedType);
   if (symbol.attrs().test(Attr::DEFERRED)) {
-    if (const Symbol * dtSymbol{dtScope.symbol()}) {
+    if (const Symbol *dtSymbol{dtScope.symbol()}) {
       if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733
         SayWithDeclaration(*dtSymbol,
             "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US,
@@ -1703,7 +1755,7 @@ void CheckHelper::CheckProcBinding(
         "Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US,
         binding.symbol().name(), symbol.name());
   }
-  if (const Symbol * overridden{FindOverriddenBinding(symbol)}) {
+  if (const Symbol *overridden{FindOverriddenBinding(symbol)}) {
     if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) {
       SayWithDeclaration(*overridden,
           "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US,
@@ -1768,7 +1820,7 @@ void CheckHelper::CheckProcBinding(
 void CheckHelper::Check(const Scope &scope) {
   scope_ = &scope;
   common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_};
-  if (const Symbol * symbol{scope.symbol()}) {
+  if (const Symbol *symbol{scope.symbol()}) {
     innermostSymbol_ = symbol;
   }
   if (scope.IsParameterizedDerivedTypeInstantiation()) {
@@ -1877,7 +1929,7 @@ void CheckHelper::CheckGenericOps(const Scope &scope) {
       // Not a generic; ensure characteristics are defined if a function.
       auto restorer{messages_.SetLocation(generic.name())};
       if (IsFunction(generic) && !context_.HasError(generic)) {
-        if (const Symbol * result{FindFunctionResult(generic)};
+        if (const Symbol *result{FindFunctionResult(generic)};
             result && !context_.HasError(*result)) {
           Characterize(generic);
         }
@@ -1893,7 +1945,7 @@ void CheckHelper::CheckGenericOps(const Scope &scope) {
     for (std::size_t i{0}; i < specifics.size(); ++i) {
       const Symbol &specific{*specifics[i]};
       auto restorer{messages_.SetLocation(bindingNames[i])};
-      if (const Procedure * proc{Characterize(specific)}) {
+      if (const Procedure *proc{Characterize(specific)}) {
         if (kind.IsAssignment()) {
           if (!CheckDefinedAssignment(specific, *proc)) {
             continue;
@@ -1912,7 +1964,7 @@ void CheckHelper::CheckGenericOps(const Scope &scope) {
     addSpecifics(symbol);
     const Symbol &ultimate{symbol.GetUltimate()};
     if (ultimate.has<DerivedTypeDetails>()) {
-      if (const Scope * typeScope{ultimate.scope()}) {
+      if (const Scope *typeScope{ultimate.scope()}) {
         for (const auto &pair2 : *typeScope) {
           addSpecifics(*pair2.second);
         }
@@ -1944,7 +1996,7 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
         "A variable with BIND(C) attribute may only appear in the specification part of a module"_err_en_US);
     context_.SetError(symbol);
   }
-  if (const std::string * name{DefinesBindCName(symbol)}) {
+  if (const std::string *name{DefinesBindCName(symbol)}) {
     auto pair{bindC_.emplace(*name, symbol)};
     if (!pair.second) {
       const Symbol &other{*pair.first->second};
@@ -2056,8 +2108,8 @@ void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType,
 
 void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg,
     GenericKind::DefinedIo ioKind, const Symbol &generic) {
-  if (const DeclTypeSpec * type{arg.GetType()}) {
-    if (const DerivedTypeSpec * derivedType{type->AsDerived()}) {
+  if (const DeclTypeSpec *type{arg.GetType()}) {
+    if (const DerivedTypeSpec *derivedType{type->AsDerived()}) {
       CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic);
       bool isPolymorphic{type->IsPolymorphic()};
       if (isPolymorphic != IsExtensibleType(derivedType)) {
@@ -2077,7 +2129,7 @@ void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg,
 
 void CheckHelper::CheckDioDummyIsDefaultInteger(
     const Symbol &subp, const Symbol &arg) {
-  if (const DeclTypeSpec * type{arg.GetType()};
+  if (const DeclTypeSpec *type{arg.GetType()};
       type && type->IsNumeric(TypeCategory::Integer)) {
     if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
         kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) {
index 95e4a74..9e53d30 100644 (file)
@@ -64,12 +64,12 @@ std::optional<Expr<SubscriptInteger>> DynamicTypeWithLength::LEN() const {
 static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
     const std::optional<parser::TypeSpec> &spec) {
   if (spec) {
-    if (const semantics::DeclTypeSpec * typeSpec{spec->declTypeSpec}) {
+    if (const semantics::DeclTypeSpec *typeSpec{spec->declTypeSpec}) {
       // Name resolution sets TypeSpec::declTypeSpec only when it's valid
       // (viz., an intrinsic type with valid known kind or a non-polymorphic
       // & non-ABSTRACT derived type).
-      if (const semantics::IntrinsicTypeSpec *
-          intrinsic{typeSpec->AsIntrinsic()}) {
+      if (const semantics::IntrinsicTypeSpec *intrinsic{
+              typeSpec->AsIntrinsic()}) {
         TypeCategory category{intrinsic->category()};
         if (auto optKind{ToInt64(intrinsic->kind())}) {
           int kind{static_cast<int>(*optKind)};
@@ -84,8 +84,8 @@ static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
             return DynamicTypeWithLength{DynamicType{category, kind}};
           }
         }
-      } else if (const semantics::DerivedTypeSpec *
-          derived{typeSpec->AsDerived()}) {
+      } else if (const semantics::DerivedTypeSpec *derived{
+                     typeSpec->AsDerived()}) {
         return DynamicTypeWithLength{DynamicType{*derived}};
       }
     }
@@ -257,7 +257,7 @@ MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
   } else if (const auto *object{
                  symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
     // C928 & C1002
-    if (Triplet * last{std::get_if<Triplet>(&ref.subscript().back().u)}) {
+    if (Triplet *last{std::get_if<Triplet>(&ref.subscript().back().u)}) {
       if (!last->upper() && object->IsAssumedSize()) {
         Say("Assumed-size array '%s' must have explicit final "
             "subscript upper bound value"_err_en_US,
@@ -379,10 +379,10 @@ static std::optional<parser::Substring> FixMisparsedSubstringDataRef(
       if (auto *triplet{std::get_if<parser::SubscriptTriplet>(
               &arrElement.subscripts.front().u)}) {
         if (!std::get<2 /*stride*/>(triplet->t).has_value()) {
-          if (const Symbol *
-              symbol{parser::GetLastName(arrElement.base).symbol}) {
+          if (const Symbol *symbol{
+                  parser::GetLastName(arrElement.base).symbol}) {
             const Symbol &ultimate{symbol->GetUltimate()};
-            if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) {
+            if (const semantics::DeclTypeSpec *type{ultimate.GetType()}) {
               if (!ultimate.IsObjectArray() &&
                   type->category() == semantics::DeclTypeSpec::Character) {
                 // The ambiguous S(j:k) was parsed as an array section
@@ -805,8 +805,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
           ultimate, AsGenericExpr(TypeParamInquiry{std::nullopt, ultimate})));
     } else {
       if (n.symbol->attrs().test(semantics::Attr::VOLATILE)) {
-        if (const semantics::Scope *
-            pure{semantics::FindPureProcedureContaining(
+        if (const semantics::Scope *pure{semantics::FindPureProcedureContaining(
                 context_.FindScope(n.source))}) {
           SayAt(n,
               "VOLATILE variable '%s' may not be referenced in pure subprogram '%s'"_err_en_US,
@@ -1068,7 +1067,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayElement &ae) {
     if (ae.subscripts.empty()) {
       // will be converted to function call later or error reported
     } else if (baseExpr->Rank() == 0) {
-      if (const Symbol * symbol{GetLastSymbol(*baseExpr)}) {
+      if (const Symbol *symbol{GetLastSymbol(*baseExpr)}) {
         if (!context_.HasError(symbol)) {
           if (inDataStmtConstant_) {
             // Better error for NULL(X) with a MOLD= argument
@@ -1120,14 +1119,13 @@ std::optional<Component> ExpressionAnalyzer::CreateComponent(
   if (&component.owner() == &scope) {
     return Component{std::move(base), component};
   }
-  if (const Symbol * typeSymbol{scope.GetSymbol()}) {
-    if (const Symbol *
-        parentComponent{typeSymbol->GetParentComponent(&scope)}) {
+  if (const Symbol *typeSymbol{scope.GetSymbol()}) {
+    if (const Symbol *parentComponent{typeSymbol->GetParentComponent(&scope)}) {
       if (const auto *object{
               parentComponent->detailsIf<semantics::ObjectEntityDetails>()}) {
         if (const auto *parentType{object->type()}) {
-          if (const semantics::Scope *
-              parentScope{parentType->derivedTypeSpec().scope()}) {
+          if (const semantics::Scope *parentScope{
+                  parentType->derivedTypeSpec().scope()}) {
             return CreateComponent(
                 DataRef{Component{std::move(base), *parentComponent}},
                 component, *parentScope);
@@ -1227,7 +1225,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
     if (auto *aRef{std::get_if<ArrayRef>(&dataRef->u)}) {
       subscripts = std::move(aRef->subscript());
       reversed.push_back(aRef->GetLastSymbol());
-      if (Component * component{aRef->base().UnwrapComponent()}) {
+      if (Component *component{aRef->base().UnwrapComponent()}) {
         dataRef = &component->base();
       } else {
         dataRef = nullptr;
@@ -1669,7 +1667,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
   auto &parsedType{std::get<parser::DerivedTypeSpec>(structure.t)};
   parser::Name structureType{std::get<parser::Name>(parsedType.t)};
   parser::CharBlock &typeName{structureType.source};
-  if (semantics::Symbol * typeSymbol{structureType.symbol}) {
+  if (semantics::Symbol *typeSymbol{structureType.symbol}) {
     if (typeSymbol->has<semantics::DerivedTypeDetails>()) {
       semantics::DerivedTypeSpec dtSpec{typeName, typeSymbol->GetUltimate()};
       if (!CheckIsValidForwardReference(dtSpec)) {
@@ -1814,9 +1812,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(
         } else if (symbol->has<semantics::ObjectEntityDetails>()) {
           // C1594(4)
           if (const auto *pureProc{FindPureProcedureContaining(innermost)}) {
-            if (const Symbol * pointer{FindPointerComponent(*symbol)}) {
-              if (const Symbol *
-                  object{FindExternallyVisibleObject(*value, *pureProc)}) {
+            if (const Symbol *pointer{FindPointerComponent(*symbol)}) {
+              if (const Symbol *object{
+                      FindExternallyVisibleObject(*value, *pureProc)}) {
                 if (auto *msg{Say(expr.source,
                         "Externally visible object '%s' may not be "
                         "associated with pointer component '%s' in a "
@@ -1954,7 +1952,9 @@ static std::optional<parser::CharBlock> GetPassName(
 static int GetPassIndex(const Symbol &proc) {
   CHECK(!proc.attrs().test(semantics::Attr::NOPASS));
   std::optional<parser::CharBlock> passName{GetPassName(proc)};
-  const auto *interface { semantics::FindInterface(proc) };
+  const auto *interface {
+    semantics::FindInterface(proc)
+  };
   if (!passName || !interface) {
     return 0; // first argument is passed-object
   }
@@ -2019,7 +2019,7 @@ auto ExpressionAnalyzer::AnalyzeProcedureComponentRef(
     bool isSubroutine) -> std::optional<CalleeAndArguments> {
   const parser::StructureComponent &sc{pcr.v.thing};
   if (MaybeExpr base{Analyze(sc.base)}) {
-    if (const Symbol * sym{sc.component.symbol}) {
+    if (const Symbol *sym{sc.component.symbol}) {
       if (context_.HasError(sym)) {
         return std::nullopt;
       }
@@ -2053,8 +2053,8 @@ auto ExpressionAnalyzer::AnalyzeProcedureComponentRef(
         if (dataRef && !CheckDataRef(*dataRef)) {
           return std::nullopt;
         }
-        if (const Symbol *
-            resolution{GetBindingResolution(dtExpr->GetType(), *sym)}) {
+        if (const Symbol *resolution{
+                GetBindingResolution(dtExpr->GetType(), *sym)}) {
           AddPassArg(arguments, std::move(*dtExpr), *sym, false);
           return CalleeAndArguments{
               ProcedureDesignator{*resolution}, std::move(arguments)};
@@ -2231,7 +2231,7 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
     }
     // Check parent derived type
     if (const auto *parentScope{symbol.owner().GetDerivedTypeParent()}) {
-      if (const Symbol * extended{parentScope->FindComponent(symbol.name())}) {
+      if (const Symbol *extended{parentScope->FindComponent(symbol.name())}) {
         auto pair{ResolveGeneric(
             *extended, actuals, adjustActuals, isSubroutine, false)};
         if (pair.first) {
@@ -2247,7 +2247,7 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
   // See 15.5.5.2 for details.
   if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) {
     for (const std::string &n : GetAllNames(context_, symbol.name())) {
-      if (const Symbol * outer{symbol.owner().parent().FindSymbol(n)}) {
+      if (const Symbol *outer{symbol.owner().parent().FindSymbol(n)}) {
         auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
             mightBeStructureConstructor)};
         if (pair.first) {
@@ -2451,7 +2451,7 @@ template <typename A> static const Symbol *AssumedTypeDummy(const A &x) {
 }
 template <>
 const Symbol *AssumedTypeDummy<parser::Name>(const parser::Name &name) {
-  if (const Symbol * symbol{name.symbol}) {
+  if (const Symbol *symbol{name.symbol}) {
     if (const auto *type{symbol->GetType()}) {
       if (type->category() == semantics::DeclTypeSpec::TypeStar) {
         return symbol;
@@ -2670,21 +2670,22 @@ static bool IsExternalCalledImplicitly(
 std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
     parser::CharBlock callSite, const ProcedureDesignator &proc,
     ActualArguments &arguments) {
+  bool treatExternalAsImplicit{IsExternalCalledImplicitly(callSite, proc)};
+  const Symbol *procSymbol{proc.GetSymbol()};
   auto chars{characteristics::Procedure::Characterize(
       proc, context_.foldingContext())};
+  bool ok{true};
   if (chars) {
-    bool treatExternalAsImplicit{IsExternalCalledImplicitly(callSite, proc)};
     if (treatExternalAsImplicit && !chars->CanBeCalledViaImplicitInterface()) {
       Say(callSite,
           "References to the procedure '%s' require an explicit interface"_err_en_US,
-          DEREF(proc.GetSymbol()).name());
+          DEREF(procSymbol).name());
     }
     // Checks for ASSOCIATED() are done in intrinsic table processing
     const SpecificIntrinsic *specificIntrinsic{proc.GetSpecificIntrinsic()};
     bool procIsAssociated{
         specificIntrinsic && specificIntrinsic->name == "associated"};
     if (!procIsAssociated) {
-      const Symbol *procSymbol{proc.GetSymbol()};
       bool procIsDummy{procSymbol && IsDummy(*procSymbol)};
       if (chars->functionResult &&
           chars->functionResult->IsAssumedLengthCharacter() &&
@@ -2692,12 +2693,11 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
         Say(callSite,
             "Assumed-length character function must be defined with a length to be called"_err_en_US);
       }
-      semantics::CheckArguments(*chars, arguments, GetFoldingContext(),
+      ok &= semantics::CheckArguments(*chars, arguments, GetFoldingContext(),
           context_.FindScope(callSite), treatExternalAsImplicit,
           specificIntrinsic);
       if (procSymbol && !IsPureProcedure(*procSymbol)) {
-        if (const semantics::Scope *
-            pure{semantics::FindPureProcedureContaining(
+        if (const semantics::Scope *pure{semantics::FindPureProcedureContaining(
                 context_.FindScope(callSite))}) {
           Say(callSite,
               "Procedure '%s' referenced in pure subprogram '%s' must be pure too"_err_en_US,
@@ -2706,6 +2706,19 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
       }
     }
   }
+  if (ok && !treatExternalAsImplicit && procSymbol &&
+      !(chars && chars->HasExplicitInterface())) {
+    if (const Symbol *global{FindGlobal(*procSymbol)};
+        global && global != procSymbol && IsProcedure(*global)) {
+      // Check a known global definition behind a local interface
+      if (auto globalChars{characteristics::Procedure::Characterize(
+              *global, context_.foldingContext())}) {
+        semantics::CheckArguments(*globalChars, arguments, GetFoldingContext(),
+            context_.FindScope(callSite), true,
+            nullptr /*not specific intrinsic*/);
+      }
+    }
+  }
   return chars;
 }
 
@@ -2713,8 +2726,8 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
 
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
   if (MaybeExpr operand{Analyze(x.v.value())}) {
-    if (const semantics::Symbol * symbol{GetLastSymbol(*operand)}) {
-      if (const semantics::Symbol * result{FindFunctionResult(*symbol)}) {
+    if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) {
+      if (const semantics::Symbol *result{FindFunctionResult(*symbol)}) {
         if (semantics::IsProcedurePointer(*result)) {
           Say("A function reference that returns a procedure "
               "pointer may not be parenthesized"_err_en_US); // C1003
@@ -2782,7 +2795,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) {
   // intrinsic function.
   // Use the actual source for the name of the call for error reporting.
   std::optional<ActualArgument> arg;
-  if (const Symbol * assumedTypeDummy{AssumedTypeDummy(x.v.value())}) {
+  if (const Symbol *assumedTypeDummy{AssumedTypeDummy(x.v.value())}) {
     arg = ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}};
   } else if (MaybeExpr argExpr{Analyze(x.v.value())}) {
     arg = ActualArgument{std::move(*argExpr)};
@@ -3018,8 +3031,7 @@ static bool CheckFuncRefToArrayElement(semantics::SemanticsContext &context,
   if (!name->symbol) {
     return false;
   } else if (name->symbol->Rank() == 0) {
-    if (const Symbol *
-        function{
+    if (const Symbol *function{
             semantics::IsFunctionResultWithSameNameAsFunction(*name->symbol)}) {
       auto &msg{context.Say(funcRef.v.source,
           "Recursive call to '%s' requires a distinct RESULT in its declaration"_err_en_US,
@@ -3055,8 +3067,7 @@ static void FixMisparsedFunctionReference(
           std::get_if<common::Indirection<parser::FunctionReference>>(&u)}) {
     parser::FunctionReference &funcRef{func->value()};
     auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)};
-    if (Symbol *
-        origSymbol{
+    if (Symbol *origSymbol{
             common::visit(common::visitors{
                               [&](parser::Name &name) { return name.symbol; },
                               [&](parser::ProcComponentRef &pcr) {
@@ -3343,7 +3354,7 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite,
       return Expr<SomeType>{NullPointer{}};
     }
   }
-  if (const Symbol * symbol{proc.GetSymbol()}) {
+  if (const Symbol *symbol{proc.GetSymbol()}) {
     if (!ResolveForward(*symbol)) {
       return std::nullopt;
     }
@@ -3578,7 +3589,7 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(const char *opr,
         isUserOp ? std::string{opr} : "operator("s + opr + ')'};
     parser::CharBlock oprName{oprNameString};
     const auto &scope{context_.context().FindScope(source_)};
-    if (Symbol * symbol{scope.FindSymbol(oprName)}) {
+    if (Symbol *symbol{scope.FindSymbol(oprName)}) {
       *definedOpSymbolPtr = symbol;
       parser::Name name{symbol->name(), symbol};
       if (auto result{context_.AnalyzeDefinedOp(name, GetActuals())}) {
@@ -3586,8 +3597,8 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(const char *opr,
       }
     }
     for (std::size_t passIndex{0}; passIndex < actuals_.size(); ++passIndex) {
-      if (const Symbol *
-          symbol{FindBoundOp(oprName, passIndex, *definedOpSymbolPtr)}) {
+      if (const Symbol *symbol{
+              FindBoundOp(oprName, passIndex, *definedOpSymbolPtr)}) {
         if (MaybeExpr result{TryBoundOp(*symbol, passIndex)}) {
           return result;
         }
@@ -3699,7 +3710,7 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
   parser::CharBlock oprName{oprNameString};
   const Symbol *proc{nullptr};
   const auto &scope{context_.context().FindScope(source_)};
-  if (const Symbol * symbol{scope.FindSymbol(oprName)}) {
+  if (const Symbol *symbol{scope.FindSymbol(oprName)}) {
     ExpressionAnalyzer::AdjustActuals noAdjustment;
     auto pair{context_.ResolveGeneric(*symbol, actuals_, noAdjustment, true)};
     if (pair.first) {
@@ -3711,9 +3722,9 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
   int passedObjectIndex{-1};
   const Symbol *definedOpSymbol{nullptr};
   for (std::size_t i{0}; i < actuals_.size(); ++i) {
-    if (const Symbol * specific{FindBoundOp(oprName, i, definedOpSymbol)}) {
-      if (const Symbol *
-          resolution{GetBindingResolution(GetType(i), *specific)}) {
+    if (const Symbol *specific{FindBoundOp(oprName, i, definedOpSymbol)}) {
+      if (const Symbol *resolution{
+              GetBindingResolution(GetType(i), *specific)}) {
         proc = resolution;
       } else {
         proc = specific;
@@ -3737,7 +3748,7 @@ void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) {
   for (const auto &actual : actuals_) {
     if (!actual.has_value()) {
       os << "- error\n";
-    } else if (const Symbol * symbol{actual->GetAssumedTypeDummy()}) {
+    } else if (const Symbol *symbol{actual->GetAssumedTypeDummy()}) {
       os << "- assumed type: " << symbol->name().ToString() << '\n';
     } else if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
       expr->AsFortran(os << "- expr: ") << '\n';
@@ -3750,7 +3761,7 @@ void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) {
 std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
     const parser::Expr &expr) {
   source_.ExtendToCover(expr.source);
-  if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) {
+  if (const Symbol *assumedTypeDummy{AssumedTypeDummy(expr)}) {
     expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter);
     if (isProcedureCall_) {
       ActualArgument arg{ActualArgument::AssumedType{*assumedTypeDummy}};
index 67acf24..9c9fa37 100644 (file)
@@ -253,9 +253,7 @@ std::string DetailsToString(const Details &details) {
       details);
 }
 
-const std::string Symbol::GetDetailsName() const {
-  return DetailsToString(details_);
-}
+std::string Symbol::GetDetailsName() const { return DetailsToString(details_); }
 
 void Symbol::set_details(Details &&details) {
   CHECK(CanReplaceDetails(details));
index 7484993..dbe50df 100644 (file)
@@ -520,6 +520,36 @@ const Symbol *FindOverriddenBinding(const Symbol &symbol) {
   return nullptr;
 }
 
+const Symbol *FindGlobal(const Symbol &original) {
+  const Symbol &ultimate{original.GetUltimate()};
+  if (ultimate.owner().IsGlobal()) {
+    return &ultimate;
+  }
+  bool isLocal{false};
+  if (IsDummy(ultimate)) {
+  } else if (IsPointer(ultimate)) {
+  } else if (ultimate.has<ProcEntityDetails>()) {
+    isLocal = IsExternal(ultimate);
+  } else if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) {
+    isLocal = subp->isInterface();
+  }
+  if (isLocal) {
+    const std::string *bind{ultimate.GetBindName()};
+    if (!bind || ultimate.name() == *bind) {
+      const Scope &globalScope{ultimate.owner().context().globalScope()};
+      if (auto iter{globalScope.find(ultimate.name())};
+          iter != globalScope.end()) {
+        const Symbol &global{*iter->second};
+        const std::string *globalBind{global.GetBindName()};
+        if (!globalBind || global.name() == *globalBind) {
+          return &global;
+        }
+      }
+    }
+  }
+  return nullptr;
+}
+
 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
   return FindParentTypeSpec(derived.typeSymbol());
 }
diff --git a/flang/test/Semantics/global01.f90 b/flang/test/Semantics/global01.f90
new file mode 100644 (file)
index 0000000..5dfa6f6
--- /dev/null
@@ -0,0 +1,45 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror
+! Catch discrepancies between a local interface and a global definition
+
+subroutine global1(x)
+  integer, intent(in) :: x
+end subroutine
+
+subroutine global2(x) bind(c,name="xyz")
+  integer, intent(in) :: x
+end subroutine
+
+subroutine global3(x)
+  integer, intent(in) :: x
+end subroutine
+
+pure subroutine global4(x)
+  integer, intent(in) :: x
+end subroutine
+
+subroutine global5(x)
+  integer, intent(in) :: x
+end subroutine
+
+program test
+  interface
+    !WARNING: The global subprogram 'global1' is not compatible with its local procedure declaration (incompatible dummy argument #1: incompatible dummy data object types: REAL(4) vs INTEGER(4))
+    subroutine global1(x)
+      real, intent(in) :: x
+    end subroutine
+    subroutine global2(x)
+      real, intent(in) :: x
+    end subroutine
+    subroutine global3(x) bind(c,name="abc")
+      real, intent(in) :: x
+    end subroutine
+    subroutine global4(x) ! not PURE, but that's ok
+      integer, intent(in) :: x
+    end subroutine
+    !WARNING: The global subprogram 'global5' is not compatible with its local procedure declaration (incompatible procedure attributes: Pure)
+    pure subroutine global5(x)
+      integer, intent(in) :: x
+    end subroutine
+  end interface
+end
+
diff --git a/flang/test/Semantics/local-vs-global.f90 b/flang/test/Semantics/local-vs-global.f90
new file mode 100644 (file)
index 0000000..d903e43
--- /dev/null
@@ -0,0 +1,164 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+module module_before_1
+end
+
+module module_before_2
+end
+
+block data block_data_before_1
+end
+
+block data block_data_before_2
+end
+
+subroutine explicit_before_1(a)
+  real, optional :: a
+end
+
+subroutine explicit_before_2(a)
+  real, optional :: a
+end
+
+subroutine implicit_before_1(a)
+  real :: a
+end
+
+subroutine implicit_before_2(a)
+  real :: a
+end
+
+function explicit_func_before_1(a)
+  real, optional :: a
+end
+
+function explicit_func_before_2(a)
+  real, optional :: a
+end
+
+function implicit_func_before_1(a)
+  real :: a
+end
+
+function implicit_func_before_2(a)
+  real :: a
+end
+
+program test
+  external justfine ! OK to name a BLOCK DATA if not called
+  !ERROR: The global entity 'module_before_1' corresponding to the local procedure 'module_before_1' is not a callable subprogram
+  external module_before_1
+  !ERROR: The global entity 'block_data_before_1' corresponding to the local procedure 'block_data_before_1' is not a callable subprogram
+  external block_data_before_1
+  !ERROR: The global subprogram 'explicit_before_1' may not be referenced via the implicit interface 'explicit_before_1'
+  external explicit_before_1
+  external implicit_before_1
+  !ERROR: The global subprogram 'explicit_func_before_1' may not be referenced via the implicit interface 'explicit_func_before_1'
+  external explicit_func_before_1
+  external implicit_func_before_1
+  !ERROR: The global entity 'module_after_1' corresponding to the local procedure 'module_after_1' is not a callable subprogram
+  external module_after_1
+  !ERROR: The global entity 'block_data_after_1' corresponding to the local procedure 'block_data_after_1' is not a callable subprogram
+  external block_data_after_1
+  !ERROR: The global subprogram 'explicit_after_1' may not be referenced via the implicit interface 'explicit_after_1'
+  external explicit_after_1
+  external implicit_after_1
+  !ERROR: The global subprogram 'explicit_func_after_1' may not be referenced via the implicit interface 'explicit_func_after_1'
+  external explicit_func_after_1
+  external implicit_func_after_1
+  call module_before_1
+  !ERROR: 'module_before_2' is not a callable procedure
+  call module_before_2
+  call block_data_before_1
+  !ERROR: 'block_data_before_2' is not a callable procedure
+  call block_data_before_2
+  call explicit_before_1(1.)
+  !ERROR: References to the procedure 'explicit_before_2' require an explicit interface
+  call explicit_before_2(1.)
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  call implicit_before_1
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  call implicit_before_2
+  print *, explicit_func_before_1(1.)
+  !ERROR: References to the procedure 'explicit_func_before_2' require an explicit interface
+  print *, explicit_func_before_2(1.)
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  print *, implicit_func_before_1()
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  print *, implicit_func_before_2()
+  call module_after_1
+  call module_after_2
+  call block_data_after_1
+  call block_data_after_2
+  call explicit_after_1(1.)
+  !ERROR: References to the procedure 'explicit_after_2' require an explicit interface
+  call explicit_after_2(1.)
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  call implicit_after_1
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  call implicit_after_2
+  print *, explicit_func_after_1(1.)
+  !ERROR: References to the procedure 'explicit_func_after_2' require an explicit interface
+  print *, explicit_func_after_2(1.)
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  print *, implicit_func_after_1()
+  !WARNING: If the procedure's interface were explicit, this reference would be in error
+  !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference
+  print *, implicit_func_after_2()
+end program
+
+block data justfine
+end
+
+module module_after_1
+end
+
+!ERROR: 'module_after_2' is already declared in this scoping unit
+module module_after_2
+end
+
+block data block_data_after_1
+end
+
+!ERROR: BLOCK DATA 'block_data_after_2' has been called
+block data block_data_after_2
+end
+
+subroutine explicit_after_1(a)
+  real, optional :: a
+end
+
+subroutine explicit_after_2(a)
+  real, optional :: a
+end
+
+subroutine implicit_after_1(a)
+  real :: a
+end
+
+subroutine implicit_after_2(a)
+  real :: a
+end
+
+function explicit_func_after_1(a)
+  real, optional :: a
+end
+
+function explicit_func_after_2(a)
+  real, optional :: a
+end
+
+function implicit_func_after_1(a)
+  real :: a
+end
+
+function implicit_func_after_2(a)
+  real :: a
+end
index ab8f93c..3363fbc 100644 (file)
@@ -130,9 +130,9 @@ contains
  end function nested5
 end module module1
 
-!DEF: /explicit1 ELEMENTAL (Function) Subprogram REAL(4)
+!DEF: /explicit1 (Function) Subprogram REAL(4)
 !DEF: /explicit1/x INTENT(IN) ObjectEntity REAL(4)
-real elemental function explicit1(x)
+real function explicit1(x)
  !REF: /explicit1/x
  real, intent(in) :: x
  !DEF: /explicit1/explicit1 ObjectEntity REAL(4)
@@ -150,14 +150,13 @@ integer function logical(x)
  logical = x+3.
 end function logical
 
-!DEF: /tan (Function) Subprogram REAL(4)
+!DEF: /tan (Function) Subprogram CHARACTER(1_8,1)
 !DEF: /tan/x INTENT(IN) ObjectEntity REAL(4)
-real function tan(x)
+character*1 function tan(x)
  !REF: /tan/x
  real, intent(in) :: x
- !DEF: /tan/tan ObjectEntity REAL(4)
- !REF: /tan/x
- tan = x+5.
+ !DEF: /tan/tan ObjectEntity CHARACTER(1_8,1)
+ tan = "?"
 end function tan
 
 !DEF: /main MainProgram
index 8d34d17..11f2ce9 100644 (file)
@@ -30,6 +30,7 @@ subroutine iface
   !ERROR: Procedure 'p' is recursively defined.  Procedures in the cycle: 'p', 'sub', 'p2'
   procedure(sub) :: p
   interface
+    !ERROR: Procedure 'sub' is recursively defined.  Procedures in the cycle: 'p', 'sub', 'p2'
     subroutine sub(p2)
       import p
       procedure(p) :: p2
index 4a5e51f..23babfe 100644 (file)
@@ -97,7 +97,6 @@ module m7
     end subroutine
   end interface
 end
-    
 
 ! Two procedures that differ only by attributes are not distinguishable
 module m8
@@ -468,7 +467,7 @@ module m20
   end interface
 end module
 
-subroutine s1()
+subroutine subr1()
   use m20
   interface operator(.not.)
     !ERROR: Procedure 'f' from module 'm20' is already specified in generic 'OPERATOR(.NOT.)'
@@ -478,7 +477,7 @@ subroutine s1()
     !ERROR: Procedure 'f' from module 'm20' is already specified in generic 'OPERATOR(+)'
     procedure f
   end interface
-end subroutine s1
+end subroutine subr1
 
 ! Extensions for distinguishable allocatable arguments; these should not
 ! elicit errors from f18
index e7d5cd9..ee4049f 100644 (file)
@@ -1,6 +1,6 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1
 ! Resolve generic based on number of arguments
-subroutine s1
+subroutine subr1
   interface f
     real function f1(x)
       optional :: x
@@ -15,7 +15,7 @@ subroutine s1
 end
 
 ! Elemental and non-element function both match: non-elemental one should be used
-subroutine s2
+subroutine subr2
   interface f
     logical elemental function f1(x)
       intent(in) :: x
@@ -53,10 +53,10 @@ module m4
   real, protected :: x
   real :: y
   interface s
-    pure subroutine s1(x)
+    pure subroutine s101(x)
       real, intent(out) :: x
     end
-    subroutine s2(x, y)
+    subroutine s102(x, y)
       real :: x, y
     end
   end interface