[flang] Resolve names in ProcedureStmt and GenericStmt

We need to save the names of specific procedures that appear in a
ProcedureStmt or GenericStmt to resolve at the end of the specification
section. They may be forward references to interface bodies.

We were saving them in the GenericDetails but that was not the right
place: we need to save the parser::Name so that we can eventually
resolve it to a Symbol, but the symbol table should not contains
references to Name because they go away with the parse tree.

The fix is to save the mapping in a new multimap, specificProcs_ in
InterfaceVisitor. We can reference parser::Names there and resolve them
during ResolveSpecificsInGenerics(). We no longer need to clutter
GenericDetails with data structures for unresolved names.

Also handle the case where a specific procedure is use-associated from
another module.

Original-commit: flang-compiler/f18@c7f7b1e72d
Reviewed-on: https://github.com/flang-compiler/f18/pull/249
Tree-same-pre-rewrite: false
This commit is contained in:
Tim Keith 2018-12-18 07:59:40 -08:00
parent a040d7462e
commit bc469ef570
4 changed files with 87 additions and 26 deletions

View File

@ -30,6 +30,7 @@
#include "../parser/parse-tree-visitor.h"
#include "../parser/parse-tree.h"
#include <list>
#include <map>
#include <memory>
#include <ostream>
#include <set>
@ -504,7 +505,12 @@ private:
bool inInterfaceBlock_{false}; // set when in interface block
bool isAbstract_{false}; // set when in abstract interface block
const parser::Name *genericName_{nullptr}; // set in generic interface block
using ProcedureKind = parser::ProcedureStmt::Kind;
// mapping of generic to its specific proc names and kinds
std::multimap<Symbol *, std::pair<const parser::Name *, ProcedureKind>>
specificProcs_;
void AddSpecificProcs(const std::list<parser::Name> &, ProcedureKind);
void ResolveSpecificsInGeneric(Symbol &generic);
};
@ -1783,11 +1789,9 @@ bool InterfaceVisitor::Pre(const parser::ProcedureStmt &x) {
Say("A PROCEDURE statement is only allowed in a generic interface block"_err_en_US);
return false;
}
bool expectModuleProc = std::get<parser::ProcedureStmt::Kind>(x.t) ==
parser::ProcedureStmt::Kind::ModuleProcedure;
for (const auto &name : std::get<std::list<parser::Name>>(x.t)) {
GetGenericDetails().add_specificProcName(name.source, expectModuleProc);
}
auto kind{std::get<parser::ProcedureStmt::Kind>(x.t)};
const auto &names{std::get<std::list<parser::Name>>(x.t)};
AddSpecificProcs(names, kind);
return false;
}
@ -1795,9 +1799,8 @@ void InterfaceVisitor::Post(const parser::GenericStmt &x) {
if (auto &accessSpec{std::get<std::optional<parser::AccessSpec>>(x.t)}) {
genericName_->symbol->attrs().set(AccessSpecToAttr(*accessSpec));
}
for (const auto &name : std::get<std::list<parser::Name>>(x.t)) {
GetGenericDetails().add_specificProcName(name.source, false);
}
const auto &names{std::get<std::list<parser::Name>>(x.t)};
AddSpecificProcs(names, ProcedureKind::Procedure);
}
GenericDetails &InterfaceVisitor::GetGenericDetails() {
@ -1806,6 +1809,13 @@ GenericDetails &InterfaceVisitor::GetGenericDetails() {
return genericName_->symbol->get<GenericDetails>();
}
void InterfaceVisitor::AddSpecificProcs(
const std::list<parser::Name> &names, ProcedureKind kind) {
for (const auto &name : names) {
specificProcs_.emplace(genericName_->symbol, std::make_pair(&name, kind));
}
}
// By now we should have seen all specific procedures referenced by name in
// this generic interface. Resolve those names to symbols.
void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
@ -1814,12 +1824,16 @@ void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
for (const auto *symbol : details.specificProcs()) {
namesSeen.insert(symbol->name());
}
for (const auto &[name, expectModuleProc] : details.specificProcNames()) {
const auto *symbol{currScope().FindSymbol(name)};
auto range{specificProcs_.equal_range(&generic)};
for (auto it{range.first}; it != range.second; ++it) {
auto *name{it->second.first};
auto kind{it->second.second};
const auto *symbol{FindSymbol(*name)};
if (!symbol) {
Say(name, "Procedure '%s' not found"_err_en_US);
Say(*name, "Procedure '%s' not found"_err_en_US);
continue;
}
symbol = &symbol->GetUltimate();
if (symbol == &generic) {
if (auto *specific{generic.get<GenericDetails>().specific()}) {
symbol = specific;
@ -1827,23 +1841,24 @@ void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
}
if (!symbol->has<SubprogramDetails>() &&
!symbol->has<SubprogramNameDetails>()) {
Say(name, "'%s' is not a subprogram"_err_en_US);
Say(*name, "'%s' is not a subprogram"_err_en_US);
continue;
}
if (expectModuleProc) {
if (kind == ProcedureKind::ModuleProcedure) {
const auto *d{symbol->detailsIf<SubprogramNameDetails>()};
if (!d || d->kind() != SubprogramKind::Module) {
Say(name, "'%s' is not a module procedure"_err_en_US);
Say(*name, "'%s' is not a module procedure"_err_en_US);
}
}
if (!namesSeen.insert(name).second) {
Say(name, "Procedure '%s' is already specified in generic '%s'"_err_en_US,
name, generic.name());
if (!namesSeen.insert(name->source).second) {
Say(*name,
"Procedure '%s' is already specified in generic '%s'"_err_en_US,
name->source, generic.name());
continue;
}
details.add_specificProc(symbol);
}
details.ClearSpecificProcNames();
specificProcs_.erase(range.first, range.second);
}
// Check that the specific procedures are all functions or all subroutines.

View File

@ -257,20 +257,14 @@ private:
class GenericDetails {
public:
using listType = std::list<const Symbol *>;
using procNamesType = std::list<std::pair<SourceName, bool>>;
GenericDetails() {}
GenericDetails(const listType &specificProcs);
GenericDetails(Symbol *specific) : specific_{specific} {}
const listType specificProcs() const { return specificProcs_; }
const procNamesType specificProcNames() const { return specificProcNames_; }
void add_specificProc(const Symbol *proc) { specificProcs_.push_back(proc); }
void add_specificProcName(const SourceName &name, bool isModuleProc) {
specificProcNames_.emplace_back(name, isModuleProc);
}
void ClearSpecificProcNames() { specificProcNames_.clear(); }
Symbol *specific() { return specific_; }
void set_specific(Symbol &specific);
@ -287,8 +281,6 @@ public:
private:
// all of the specific procedures for this generic
listType specificProcs_;
// specific procs referenced by name and whether it's a module proc
procNamesType specificProcNames_;
// 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

View File

@ -75,6 +75,7 @@ set(SYMBOL_TESTS
symbol07.f90
symbol08.f90
symbol09.f90
symbol10.f90
)
# These test files have expected .mod file contents in the source

View File

@ -0,0 +1,53 @@
! Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!DEF: /m1 Module
module m1
contains
!DEF: /m1/foo_complex PUBLIC Subprogram
!DEF: /m1/foo_complex/z ObjectEntity COMPLEX(4)
subroutine foo_complex (z)
!REF: /m1/foo_complex/z
complex z
end subroutine
end module
!DEF: /m2 Module
module m2
!REF: /m1
use :: m1
!DEF: /m2/foo PUBLIC Generic
interface foo
!DEF: /m2/foo_int PUBLIC Subprogram
module procedure :: foo_int
!DEF: /m2/foo_real EXTERNAL, PUBLIC Subprogram
procedure :: foo_real
!DEF: /m2/foo_complex PUBLIC Use
procedure :: foo_complex
end interface
interface
!REF: /m2/foo_real
!DEF: /m2/foo_real/r ObjectEntity REAL(4)
subroutine foo_real (r)
!REF: /m2/foo_real/r
real r
end subroutine
end interface
contains
!REF: /m2/foo_int
!DEF: /m2/foo_int/i ObjectEntity INTEGER(4)
subroutine foo_int (i)
!REF: /m2/foo_int/i
integer i
end subroutine
end module