[flang-commits] [flang] 86f59de - [flang] Fix bugs related to merging generics during USE

Tim Keith via flang-commits flang-commits at lists.llvm.org
Wed Dec 2 15:14:31 PST 2020


Author: Tim Keith
Date: 2020-12-02T15:13:50-08:00
New Revision: 86f59de13b8058280d468984727bd1e6727f2112

URL: https://github.com/llvm/llvm-project/commit/86f59de13b8058280d468984727bd1e6727f2112
DIFF: https://github.com/llvm/llvm-project/commit/86f59de13b8058280d468984727bd1e6727f2112.diff

LOG: [flang] Fix bugs related to merging generics during USE

When the same generic name is use-associated from two modules, the
generics are merged into a single one in the current scope. This change
fixes some bugs in that process.

When a generic is merged, it can have two specific procedures with the
same name as the generic (c.f. module m7c in modfile07.f90). We were
disallowing that by checking for duplicate names in the generic rather
than duplicate symbols. Changing `namesSeen` to `symbolsSeen` in
`ResolveSpecificsInGeneric` fixes that.

We weren't including each USE of those generics in the .mod file so in
some cases they were incorrect. Extend GenericDetails to specify all
use-associated symbols that are merged into the generic. This is used to
write out .mod files correctly.

The distinguishability check for specific procedures of a generic
sometimes have to refer to procedures from a use-associated generic in
error messages. In that case we don't have the source location of the
procedure so adapt the message to say where is was use-associated from.
This requires passing the scope through the checks to make that
determination.

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

Added: 
    

Modified: 
    flang/include/flang/Semantics/symbol.h
    flang/lib/Semantics/check-declarations.cpp
    flang/lib/Semantics/mod-file.cpp
    flang/lib/Semantics/resolve-names.cpp
    flang/lib/Semantics/symbol.cpp
    flang/test/Semantics/getsymbols03-a.f90
    flang/test/Semantics/modfile07.f90
    flang/test/Semantics/resolve17.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 246bf2d0b338..833e3ab77720 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -155,6 +155,7 @@ class AssocEntityDetails : public EntityDetails {
   MaybeExpr expr_;
   std::optional<int> rank_;
 };
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const AssocEntityDetails &);
 
 // An entity known to be an object.
 class ObjectEntityDetails : public EntityDetails {
@@ -432,6 +433,7 @@ class GenericDetails {
   const SymbolVector &specificProcs() const { return specificProcs_; }
   const std::vector<SourceName> &bindingNames() const { return bindingNames_; }
   void AddSpecificProc(const Symbol &, SourceName bindingName);
+  const SymbolVector &uses() const { return uses_; }
 
   // specific and derivedType indicate a specific procedure or derived type
   // with the same name as this generic. Only one of them may be set.
@@ -441,6 +443,7 @@ class GenericDetails {
   Symbol *derivedType() { return derivedType_; }
   const Symbol *derivedType() const { return derivedType_; }
   void set_derivedType(Symbol &derivedType);
+  void AddUse(const Symbol &);
 
   // Copy in specificProcs, specific, and derivedType from another generic
   void CopyFrom(const GenericDetails &);
@@ -450,22 +453,19 @@ class GenericDetails {
   const Symbol *CheckSpecific() const;
   Symbol *CheckSpecific();
 
-  const std::optional<UseDetails> &useDetails() const { return useDetails_; }
-  void set_useDetails(const UseDetails &details) { useDetails_ = details; }
-
 private:
   GenericKind kind_;
   // all of the specific procedures for this generic
   SymbolVector specificProcs_;
   std::vector<SourceName> bindingNames_;
+  // Symbols used from other modules merged into this one
+  SymbolVector uses_;
   // a specific procedure with the same name as this generic, if any
   Symbol *specific_{nullptr};
   // a derived type with the same name as this generic, if any
   Symbol *derivedType_{nullptr};
-  // If two USEs of generics were merged to form this one, this is the
-  // UseDetails for one of them. Used for reporting USE errors.
-  std::optional<UseDetails> useDetails_;
 };
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const GenericDetails &);
 
 class UnknownDetails {};
 

diff  --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index a0b445b8d046..782e4c864421 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -120,11 +120,12 @@ class DistinguishabilityHelper {
 public:
   DistinguishabilityHelper(SemanticsContext &context) : context_{context} {}
   void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &);
-  void Check();
+  void Check(const Scope &);
 
 private:
-  void SayNotDistinguishable(
-      const SourceName &, GenericKind, const Symbol &, const Symbol &);
+  void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind,
+      const Symbol &, const Symbol &);
+  void AttachDeclaration(parser::Message &, const Scope &, const Symbol &);
 
   SemanticsContext &context_;
   struct ProcedureInfo {
@@ -1020,7 +1021,7 @@ void CheckHelper::CheckSpecificsAreDistinguishable(
       helper.Add(generic, kind, specific, *procedure);
     }
   }
-  helper.Check();
+  helper.Check(generic.owner());
 }
 
 static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
@@ -1637,7 +1638,7 @@ void CheckHelper::CheckGenericOps(const Scope &scope) {
       }
     }
   }
-  helper.Check();
+  helper.Check(scope);
 }
 
 void SubprogramMatchHelper::Check(
@@ -1859,7 +1860,7 @@ void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind,
   }
 }
 
-void DistinguishabilityHelper::Check() {
+void DistinguishabilityHelper::Check(const Scope &scope) {
   for (const auto &[name, info] : nameToInfo_) {
     auto count{info.size()};
     for (std::size_t i1{0}; i1 < count - 1; ++i1) {
@@ -1870,15 +1871,17 @@ void DistinguishabilityHelper::Check() {
                 ? evaluate::characteristics::Distinguishable
                 : evaluate::characteristics::DistinguishableOpOrAssign};
         if (!distinguishable(proc1, proc2)) {
-          SayNotDistinguishable(name, kind1, symbol1, symbol2);
+          SayNotDistinguishable(
+              GetTopLevelUnitContaining(scope), name, kind1, symbol1, symbol2);
         }
       }
     }
   }
 }
 
-void DistinguishabilityHelper::SayNotDistinguishable(const SourceName &name,
-    GenericKind kind, const Symbol &proc1, const Symbol &proc2) {
+void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
+    const SourceName &name, GenericKind kind, const Symbol &proc1,
+    const Symbol &proc2) {
   std::string name1{proc1.name().ToString()};
   std::string name2{proc2.name().ToString()};
   if (kind.IsOperator() || kind.IsAssignment()) {
@@ -1890,12 +1893,34 @@ void DistinguishabilityHelper::SayNotDistinguishable(const SourceName &name,
       name2 = proc2.owner().GetName()->ToString() + '%' + name2;
     }
   }
-  auto &msg{context_.Say(name,
-      "Generic '%s' may not have specific procedures '%s' and '%s'"
-      " as their interfaces are not distinguishable"_err_en_US,
-      MakeOpName(name), name1, name2)};
-  evaluate::AttachDeclaration(msg, proc1);
-  evaluate::AttachDeclaration(msg, proc2);
+  parser::Message *msg;
+  if (scope.sourceRange().Contains(name)) {
+    msg = &context_.Say(name,
+        "Generic '%s' may not have specific procedures '%s' and"
+        " '%s' as their interfaces are not distinguishable"_err_en_US,
+        MakeOpName(name), name1, name2);
+  } else {
+    msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(),
+        "USE-associated generic '%s' may not have specific procedures '%s' and"
+        " '%s' as their interfaces are not distinguishable"_err_en_US,
+        MakeOpName(name), name1, name2);
+  }
+  AttachDeclaration(*msg, scope, proc1);
+  AttachDeclaration(*msg, scope, proc2);
+}
+
+// `evaluate::AttachDeclaration` doesn't handle the generic case where `proc`
+// comes from a 
diff erent module but is not necessarily use-associated.
+void DistinguishabilityHelper::AttachDeclaration(
+    parser::Message &msg, const Scope &scope, const Symbol &proc) {
+  const Scope &unit{GetTopLevelUnitContaining(proc)};
+  if (unit == scope) {
+    evaluate::AttachDeclaration(msg, proc);
+  } else {
+    msg.Attach(unit.GetName().value(),
+        "'%s' is USE-associated from module '%s'"_en_US, proc.name(),
+        unit.GetName().value());
+  }
 }
 
 void CheckDeclarations(SemanticsContext &context) {

diff  --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index f714a3b1f9bf..99ea65b753a6 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -43,7 +43,7 @@ struct ModHeader {
 };
 
 static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
-static SymbolVector CollectSymbols(const Scope &);
+static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &);
 static void PutEntity(llvm::raw_ostream &, const Symbol &);
 static void PutObjectEntity(llvm::raw_ostream &, const Symbol &);
 static void PutProcEntity(llvm::raw_ostream &, const Symbol &);
@@ -178,12 +178,17 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) {
 
 // Put out the visible symbols from scope.
 bool ModFileWriter::PutSymbols(const Scope &scope) {
-  std::string buf;
-  llvm::raw_string_ostream typeBindings{
-      buf}; // stuff after CONTAINS in derived type
-  for (const Symbol &symbol : CollectSymbols(scope)) {
+  SymbolVector sorted;
+  SymbolVector uses;
+  CollectSymbols(scope, sorted, uses);
+  std::string buf; // stuff after CONTAINS in derived type
+  llvm::raw_string_ostream typeBindings{buf};
+  for (const Symbol &symbol : sorted) {
     PutSymbol(typeBindings, symbol);
   }
+  for (const Symbol &symbol : uses) {
+    PutUse(symbol);
+  }
   if (auto str{typeBindings.str()}; !str.empty()) {
     CHECK(scope.IsDerivedType());
     decls_ << "contains\n" << str;
@@ -393,10 +398,13 @@ static llvm::raw_ostream &PutGenericName(
 }
 
 void ModFileWriter::PutGeneric(const Symbol &symbol) {
+  const auto &genericOwner{symbol.owner()};
   auto &details{symbol.get<GenericDetails>()};
   PutGenericName(decls_ << "interface ", symbol) << '\n';
   for (const Symbol &specific : details.specificProcs()) {
-    decls_ << "procedure::" << specific.name() << '\n';
+    if (specific.owner() == genericOwner) {
+      decls_ << "procedure::" << specific.name() << '\n';
+    }
   }
   decls_ << "end interface\n";
   if (symbol.attrs().test(Attr::PRIVATE)) {
@@ -431,8 +439,8 @@ void ModFileWriter::PutUseExtraAttr(
 
 // Collect the symbols of this scope sorted by their original order, not name.
 // Namelists are an exception: they are sorted after other symbols.
-SymbolVector CollectSymbols(const Scope &scope) {
-  SymbolVector sorted;
+void CollectSymbols(
+    const Scope &scope, SymbolVector &sorted, SymbolVector &uses) {
   SymbolVector namelist;
   std::size_t commonSize{scope.commonBlocks().size()};
   auto symbols{scope.GetSymbols()};
@@ -444,6 +452,9 @@ SymbolVector CollectSymbols(const Scope &scope) {
       } else {
         sorted.push_back(symbol);
       }
+      if (const auto *details{symbol->detailsIf<GenericDetails>()}) {
+        uses.insert(uses.end(), details->uses().begin(), details->uses().end());
+      }
     }
   }
   sorted.insert(sorted.end(), namelist.begin(), namelist.end());
@@ -451,7 +462,6 @@ SymbolVector CollectSymbols(const Scope &scope) {
     sorted.push_back(*pair.second);
   }
   std::sort(sorted.end() - commonSize, sorted.end());
-  return sorted;
 }
 
 void PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {

diff  --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index fd994b9d2827..ad5fc734c776 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -623,6 +623,7 @@ class ModuleVisitor : public virtual ScopeHandler {
   void BeginModule(const parser::Name &, bool isSubmodule);
   bool BeginSubmodule(const parser::Name &, const parser::ParentIdentifier &);
   void ApplyDefaultAccess();
+  void AddGenericUse(GenericDetails &, const SourceName &, const Symbol &);
 
 private:
   // The default access spec for this module.
@@ -630,7 +631,7 @@ class ModuleVisitor : public virtual ScopeHandler {
   // The location of the last AccessStmt without access-ids, if any.
   std::optional<SourceName> prevAccessStmt_;
   // The scope of the module during a UseStmt
-  const Scope *useModuleScope_{nullptr};
+  Scope *useModuleScope_{nullptr};
 
   Symbol &SetAccess(const SourceName &, Attr attr, Symbol * = nullptr);
   // A rename in a USE statement: local => use
@@ -641,7 +642,8 @@ class ModuleVisitor : public virtual ScopeHandler {
   // Record a use from useModuleScope_ of use Name/Symbol as local Name/Symbol
   SymbolRename AddUse(const SourceName &localName, const SourceName &useName);
   SymbolRename AddUse(const SourceName &, const SourceName &, Symbol *);
-  void AddUse(const SourceName &, Symbol &localSymbol, const Symbol &useSymbol);
+  void DoAddUse(const SourceName &, const SourceName &, Symbol &localSymbol,
+      const Symbol &useSymbol);
   void AddUse(const GenericSpecInfo &);
   Scope *FindModule(const parser::Name &, Scope *ancestor = nullptr);
 };
@@ -2300,8 +2302,14 @@ bool ModuleVisitor::Pre(const parser::Rename::Operators &x) {
 // Set useModuleScope_ to the Scope of the module being used.
 bool ModuleVisitor::Pre(const parser::UseStmt &x) {
   useModuleScope_ = FindModule(x.moduleName);
-  return useModuleScope_ != nullptr;
+  if (!useModuleScope_) {
+    return false;
+  }
+  // use the name from this source file
+  useModuleScope_->symbol()->ReplaceName(x.moduleName.source);
+  return true;
 }
+
 void ModuleVisitor::Post(const parser::UseStmt &x) {
   if (const auto *list{std::get_if<std::list<parser::Rename>>(&x.u)}) {
     // Not a use-only: collect the names that were used in renames,
@@ -2321,13 +2329,12 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
     for (const auto &[name, symbol] : *useModuleScope_) {
       if (symbol->attrs().test(Attr::PUBLIC) &&
           !symbol->attrs().test(Attr::INTRINSIC) &&
-          !symbol->detailsIf<MiscDetails>()) {
-        if (useNames.count(name) == 0) {
-          auto *localSymbol{FindInScope(currScope(), name)};
-          if (!localSymbol) {
-            localSymbol = &CopySymbol(name, *symbol);
-          }
-          AddUse(x.moduleName.source, *localSymbol, *symbol);
+          !symbol->has<MiscDetails>() && useNames.count(name) == 0) {
+        SourceName location{x.moduleName.source};
+        if (auto *localSymbol{FindInScope(currScope(), name)}) {
+          DoAddUse(location, localSymbol->name(), *localSymbol, *symbol);
+        } else {
+          DoAddUse(location, location, CopySymbol(name, *symbol), *symbol);
         }
       }
     }
@@ -2356,7 +2363,7 @@ ModuleVisitor::SymbolRename ModuleVisitor::AddUse(
     return {};
   }
   auto &localSymbol{MakeSymbol(localName)};
-  AddUse(useName, localSymbol, *useSymbol);
+  DoAddUse(useName, localName, localSymbol, *useSymbol);
   return {&localSymbol, useSymbol};
 }
 
@@ -2367,14 +2374,14 @@ static void ConvertToUseError(
   const auto *useDetails{symbol.detailsIf<UseDetails>()};
   if (!useDetails) {
     auto &genericDetails{symbol.get<GenericDetails>()};
-    useDetails = &genericDetails.useDetails().value();
+    useDetails = &genericDetails.uses().at(0)->get<UseDetails>();
   }
   symbol.set_details(
       UseErrorDetails{*useDetails}.add_occurrence(location, module));
 }
 
-void ModuleVisitor::AddUse(
-    const SourceName &location, Symbol &localSymbol, const Symbol &useSymbol) {
+void ModuleVisitor::DoAddUse(const SourceName &location,
+    const SourceName &localName, Symbol &localSymbol, const Symbol &useSymbol) {
   localSymbol.attrs() = useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE};
   localSymbol.flags() = useSymbol.flags();
   if (auto *useDetails{localSymbol.detailsIf<UseDetails>()}) {
@@ -2386,23 +2393,18 @@ void ModuleVisitor::AddUse(
       // use-associating generics with the same names: merge them into a
       // new generic in this scope
       auto generic1{ultimate.get<GenericDetails>()};
-      generic1.set_useDetails(*useDetails);
+      AddGenericUse(generic1, localName, useSymbol);
+      generic1.AddUse(localSymbol);
       // useSymbol has specific g and so does generic1
       auto &generic2{useSymbol.get<GenericDetails>()};
-      if (generic1.specific() && generic2.specific() &&
-          generic1.specific() != generic2.specific()) {
-        Say(location,
-            "Generic interface '%s' has ambiguous specific procedures"
-            " from modules '%s' and '%s'"_err_en_US,
-            localSymbol.name(), GetUsedModule(*useDetails).name(),
-            useSymbol.owner().GetName().value());
-      } else if (generic1.derivedType() && generic2.derivedType() &&
+      if (generic1.derivedType() && generic2.derivedType() &&
           generic1.derivedType() != generic2.derivedType()) {
         Say(location,
             "Generic interface '%s' has ambiguous derived types"
             " from modules '%s' and '%s'"_err_en_US,
             localSymbol.name(), GetUsedModule(*useDetails).name(),
             useSymbol.owner().GetName().value());
+        context().SetError(localSymbol);
       } else {
         generic1.CopyFrom(generic2);
       }
@@ -2411,26 +2413,33 @@ void ModuleVisitor::AddUse(
     } else {
       ConvertToUseError(localSymbol, location, *useModuleScope_);
     }
-  } else {
-    auto *genericDetails{localSymbol.detailsIf<GenericDetails>()};
-    if (genericDetails && genericDetails->useDetails()) {
-      // localSymbol came from merging two use-associated generics
-      if (auto *useDetails{useSymbol.detailsIf<GenericDetails>()}) {
-        genericDetails->CopyFrom(*useDetails);
+  } else if (auto *genericDetails{localSymbol.detailsIf<GenericDetails>()}) {
+    if (const auto *useDetails{useSymbol.detailsIf<GenericDetails>()}) {
+      AddGenericUse(*genericDetails, localName, useSymbol);
+      if (genericDetails->derivedType() && useDetails->derivedType() &&
+          genericDetails->derivedType() != useDetails->derivedType()) {
+        Say(location,
+            "Generic interface '%s' has ambiguous derived types"
+            " from modules '%s' and '%s'"_err_en_US,
+            localSymbol.name(),
+            genericDetails->derivedType()->owner().GetName().value(),
+            useDetails->derivedType()->owner().GetName().value());
       } else {
-        ConvertToUseError(localSymbol, location, *useModuleScope_);
+        genericDetails->CopyFrom(*useDetails);
       }
-    } else if (auto *details{localSymbol.detailsIf<UseErrorDetails>()}) {
-      details->add_occurrence(location, *useModuleScope_);
-    } else if (!localSymbol.has<UnknownDetails>()) {
-      Say(location,
-          "Cannot use-associate '%s'; it is already declared in this scope"_err_en_US,
-          localSymbol.name())
-          .Attach(localSymbol.name(), "Previous declaration of '%s'"_en_US,
-              localSymbol.name());
     } else {
-      localSymbol.set_details(UseDetails{location, useSymbol});
+      ConvertToUseError(localSymbol, location, *useModuleScope_);
     }
+  } else if (auto *details{localSymbol.detailsIf<UseErrorDetails>()}) {
+    details->add_occurrence(location, *useModuleScope_);
+  } else if (!localSymbol.has<UnknownDetails>()) {
+    Say(location,
+        "Cannot use-associate '%s'; it is already declared in this scope"_err_en_US,
+        localName)
+        .Attach(localSymbol.name(), "Previous declaration of '%s'"_en_US,
+            localName);
+  } else {
+    localSymbol.set_details(UseDetails{localName, useSymbol});
   }
 }
 
@@ -2443,6 +2452,12 @@ void ModuleVisitor::AddUse(const GenericSpecInfo &info) {
   }
 }
 
+// Create a UseDetails symbol for this USE and add it to generic
+void ModuleVisitor::AddGenericUse(
+    GenericDetails &generic, const SourceName &name, const Symbol &useSymbol) {
+  generic.AddUse(currScope().MakeSymbol(name, {}, UseDetails{name, useSymbol}));
+}
+
 bool ModuleVisitor::BeginSubmodule(
     const parser::Name &name, const parser::ParentIdentifier &parentId) {
   auto &ancestorName{std::get<parser::Name>(parentId.t)};
@@ -2574,9 +2589,9 @@ void InterfaceVisitor::AddSpecificProcs(
 // this generic interface. Resolve those names to symbols.
 void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
   auto &details{generic.get<GenericDetails>()};
-  std::set<SourceName> namesSeen; // to check for duplicate names
+  SymbolSet symbolsSeen;
   for (const Symbol &symbol : details.specificProcs()) {
-    namesSeen.insert(symbol.name());
+    symbolsSeen.insert(symbol);
   }
   auto range{specificProcs_.equal_range(&generic)};
   for (auto it{range.first}; it != range.second; ++it) {
@@ -2613,7 +2628,7 @@ void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
         }
       }
     }
-    if (!namesSeen.insert(name->source).second) {
+    if (!symbolsSeen.insert(*symbol).second) {
       Say(name->source,
           "Procedure '%s' is already specified in generic '%s'"_err_en_US,
           name->source, MakeOpName(generic.name()));
@@ -6127,7 +6142,10 @@ void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
     }
     Symbol &ultimate{existing->GetUltimate()};
     if (auto *ultimateDetails{ultimate.detailsIf<GenericDetails>()}) {
+      // convert a use-associated generic into a local generic
       genericDetails.CopyFrom(*ultimateDetails);
+      AddGenericUse(genericDetails, existing->name(),
+          existing->get<UseDetails>().symbol());
     } else if (ultimate.has<SubprogramDetails>() ||
         ultimate.has<SubprogramNameDetails>()) {
       genericDetails.set_specific(ultimate);

diff  --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index 93c2ac3b163d..ee6a4a15de83 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -168,6 +168,10 @@ void GenericDetails::set_derivedType(Symbol &derivedType) {
   CHECK(!derivedType_);
   derivedType_ = &derivedType;
 }
+void GenericDetails::AddUse(const Symbol &use) {
+  CHECK(use.has<UseDetails>());
+  uses_.push_back(use);
+}
 
 const Symbol *GenericDetails::CheckSpecific() const {
   return const_cast<GenericDetails *>(this)->CheckSpecific();
@@ -188,10 +192,7 @@ Symbol *GenericDetails::CheckSpecific() {
 void GenericDetails::CopyFrom(const GenericDetails &from) {
   CHECK(specificProcs_.size() == bindingNames_.size());
   CHECK(from.specificProcs_.size() == from.bindingNames_.size());
-  if (from.specific_) {
-    CHECK(!specific_ || specific_ == from.specific_);
-    specific_ = from.specific_;
-  }
+  kind_ = from.kind_;
   if (from.derivedType_) {
     CHECK(!derivedType_ || derivedType_ == from.derivedType_);
     derivedType_ = from.derivedType_;
@@ -257,9 +258,13 @@ bool Symbol::CanReplaceDetails(const Details &details) const {
               return has<SubprogramNameDetails>() || has<EntityDetails>();
             },
             [&](const DerivedTypeDetails &) {
-              auto *derived{this->detailsIf<DerivedTypeDetails>()};
+              const auto *derived{detailsIf<DerivedTypeDetails>()};
               return derived && derived->isForwardReferenced();
             },
+            [&](const UseDetails &x) {
+              const auto *use{detailsIf<UseDetails>()};
+              return use && use->symbol() == x.symbol();
+            },
             [](const auto &) { return false; },
         },
         details);
@@ -375,6 +380,26 @@ llvm::raw_ostream &operator<<(
   return os;
 }
 
+llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) {
+  os << ' ' << x.kind().ToString();
+  DumpBool(os, "(specific)", x.specific() != nullptr);
+  DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
+  if (const auto &uses{x.uses()}; !uses.empty()) {
+    os << " (uses:";
+    char sep{' '};
+    for (const Symbol &use : uses) {
+      const Symbol &ultimate{use.GetUltimate()};
+      os << sep << ultimate.name() << "->"
+         << ultimate.owner().GetName().value();
+      sep = ',';
+    }
+    os << ')';
+  }
+  os << " procs:";
+  DumpSymbolVector(os, x.specificProcs());
+  return os;
+}
+
 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
   os << DetailsToString(details);
   std::visit( //
@@ -411,13 +436,6 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
             }
           },
           [](const HostAssocDetails &) {},
-          [&](const GenericDetails &x) {
-            os << ' ' << x.kind().ToString();
-            DumpBool(os, "(specific)", x.specific() != nullptr);
-            DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
-            os << " procs:";
-            DumpSymbolVector(os, x.specificProcs());
-          },
           [&](const ProcBindingDetails &x) {
             os << " => " << x.symbol().name();
             DumpOptional(os, "passName", x.passName());

diff  --git a/flang/test/Semantics/getsymbols03-a.f90 b/flang/test/Semantics/getsymbols03-a.f90
index 980d6bc58c1a..3cbba425d875 100644
--- a/flang/test/Semantics/getsymbols03-a.f90
+++ b/flang/test/Semantics/getsymbols03-a.f90
@@ -10,5 +10,5 @@ program main
 ! RUN: %f18 -fget-symbols-sources -fparse-only %s 2>&1 | FileCheck %s
 ! CHECK:f:{{.*}}getsymbols03-b.f90, 2, 12-13
 ! CHECK:main:{{.*}}getsymbols03-a.f90, 4, 9-13
-! CHECK:mm3:{{.*}}getsymbols03-b.f90, 1, 8-11
+! CHECK:mm3:{{.*}}getsymbols03-a.f90, 5, 6-9
 ! CHECK:x:{{.*}}getsymbols03-a.f90, 6, 13-14

diff  --git a/flang/test/Semantics/modfile07.f90 b/flang/test/Semantics/modfile07.f90
index 69fee6bcd326..0809da471a60 100644
--- a/flang/test/Semantics/modfile07.f90
+++ b/flang/test/Semantics/modfile07.f90
@@ -332,3 +332,220 @@ module m6b
 !module m6b
 ! use m6a,only:operator(.lt.)
 !end
+
+module m7a
+  interface g_integer
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    integer :: x
+  end
+end
+!Expect: m7a.mod
+!module m7a
+! interface g_integer
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  integer(4) :: x
+! end
+!end
+
+module m7b
+  interface g_real
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    real :: x
+  end subroutine
+end
+!Expect: m7b.mod
+!module m7b
+! interface g_real
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  real(4) :: x
+! end
+!end
+
+module m7c
+  use m7a, only: g => g_integer
+  use m7b, only: g => g_real
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    complex :: x
+  end subroutine
+  subroutine test()
+    real :: x
+    integer :: y
+    complex :: z
+    call g(x)
+    call g(y)
+    call g(z)
+  end
+end
+!Expect: m7c.mod
+!module m7c
+! use m7b, only: g => g_real
+! use m7a, only: g => g_integer
+! interface g
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  complex(4) :: x
+! end
+! subroutine test()
+! end
+!end
+
+! Test m8 is like m7 but without renaming.
+
+module m8a
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    integer :: x
+  end
+end
+!Expect: m8a.mod
+!module m8a
+! interface g
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  integer(4) :: x
+! end
+!end
+
+module m8b
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    real :: x
+  end subroutine
+end
+!Expect: m8b.mod
+!module m8b
+! interface g
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  real(4) :: x
+! end
+!end
+
+module m8c
+  use m8a
+  use m8b
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    complex :: x
+  end subroutine
+  subroutine test()
+    real :: x
+    integer :: y
+    complex :: z
+    call g(x)
+    call g(y)
+    call g(z)
+  end
+end
+!Expect: m8c.mod
+!module m8c
+! use m8b, only: g
+! use m8a, only: g
+! interface g
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  complex(4) :: x
+! end
+! subroutine test()
+! end
+!end
+
+! Merging a use-associated generic with a local generic
+
+module m9a
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    integer :: x
+  end
+end
+!Expect: m9a.mod
+!module m9a
+! interface g
+!  procedure :: s
+! end interface
+! private :: s
+!contains
+! subroutine s(x)
+!  integer(4) :: x
+! end
+!end
+
+module m9b
+  use m9a
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    real :: x
+  end
+  subroutine test()
+    call g(1)
+    call g(1.0)
+  end
+end
+!Expect: m9b.mod
+!module m9b
+! use m9a,only:g
+! interface g
+!   procedure::s
+! end interface
+! private::s
+!contains
+! subroutine s(x)
+!   real(4)::x
+! end
+! subroutine test()
+! end
+!end
+

diff  --git a/flang/test/Semantics/resolve17.f90 b/flang/test/Semantics/resolve17.f90
index 59fd43605d23..5aedaaa62003 100644
--- a/flang/test/Semantics/resolve17.f90
+++ b/flang/test/Semantics/resolve17.f90
@@ -165,23 +165,18 @@ subroutine s8
 
 module m9a
   interface g
-    module procedure s1
     module procedure g
   end interface
 contains
   subroutine g()
   end
-  subroutine s1(x)
-    integer :: x
-  end
 end module
 module m9b
-  use m9a
   interface g
-    module procedure s2
+    module procedure g
   end interface
 contains
-  subroutine s2(x)
+  subroutine g(x)
     real :: x
   end
 end module
@@ -190,18 +185,56 @@ module m9c
     module procedure g
   end interface
 contains
-  subroutine g(x)
-    real :: x
+  subroutine g()
   end
 end module
-! Merge use-associated generics that have the same symbol (s1)
-subroutine s9
+subroutine s9a
   use m9a
   use m9b
 end
-! Merge use-associate generics each with specific of same name
-subroutine s9c
+subroutine s9b
+  !ERROR: USE-associated generic 'g' may not have specific procedures 'g' and 'g' as their interfaces are not distinguishable
   use m9a
-  !ERROR: Generic interface 'g' has ambiguous specific procedures from modules 'm9a' and 'm9c'
   use m9c
 end
+
+module m10a
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    integer :: x
+  end
+end
+module m10b
+  use m10a
+  !ERROR: Generic 'g' may not have specific procedures 's' and 's' as their interfaces are not distinguishable
+  interface g
+    module procedure s
+  end interface
+  private :: s
+contains
+  subroutine s(x)
+    integer :: x
+  end
+end
+
+module m11a
+  interface g
+  end interface
+  type g
+  end type
+end module
+module m11b
+  interface g
+  end interface
+  type g
+  end type
+end module
+module m11c
+  use m11a
+  !ERROR: Generic interface 'g' has ambiguous derived types from modules 'm11a' and 'm11b'
+  use m11b
+end module


        


More information about the flang-commits mailing list