[flang] Skim executable parts to detect dummy procedure calls
Original-commit: flang-compiler/f18@b085255e5c Reviewed-on: https://github.com/flang-compiler/f18/pull/477 Tree-same-pre-rewrite: false
This commit is contained in:
parent
0d242e2006
commit
cab9b3072b
|
@ -1672,7 +1672,6 @@ static bool NeedsType(const Symbol &symbol) {
|
|||
return true;
|
||||
}
|
||||
void ScopeHandler::ApplyImplicitRules(Symbol &symbol) {
|
||||
ConvertToObjectEntity(symbol);
|
||||
if (NeedsType(symbol)) {
|
||||
if (isImplicitNoneType()) {
|
||||
Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
|
||||
|
@ -3628,6 +3627,7 @@ Symbol *DeclarationVisitor::DeclareLocalEntity(const parser::Name &name) {
|
|||
// Declare the name as an object in the enclosing scope so that
|
||||
// the name can't be repurposed there later as something else.
|
||||
prev = &MakeSymbol(InclusiveScope(), name.source, Attrs{});
|
||||
ConvertToObjectEntity(*prev);
|
||||
ApplyImplicitRules(*prev);
|
||||
implicit = true;
|
||||
}
|
||||
|
@ -4297,6 +4297,7 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
|
|||
return nullptr; // reported an error
|
||||
}
|
||||
if (symbol->IsDummy()) {
|
||||
ConvertToObjectEntity(*symbol);
|
||||
ApplyImplicitRules(*symbol);
|
||||
}
|
||||
return &name;
|
||||
|
@ -4313,6 +4314,7 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
|
|||
"'%s' from host scoping unit is not accessible due to IMPORT"_err_en_US);
|
||||
return nullptr;
|
||||
}
|
||||
ConvertToObjectEntity(*symbol);
|
||||
ApplyImplicitRules(*symbol);
|
||||
return &name;
|
||||
}
|
||||
|
@ -4669,6 +4671,51 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Calls to dummy procedures need to record that their symbols are known
|
||||
// to be procedures, so that they don't get converted to objects by default.
|
||||
class ExecutionPartSkimmer {
|
||||
public:
|
||||
ExecutionPartSkimmer(SemanticsContext &c, Scope &s)
|
||||
: context_{c}, scope_{s} {}
|
||||
|
||||
void Walk(const parser::ExecutionPart *exec) {
|
||||
if (exec != nullptr) {
|
||||
parser::Walk(*exec, *this);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A> bool Pre(const A &) { return true; }
|
||||
template<typename A> void Post(const A &) {}
|
||||
void Post(const parser::FunctionReference &fr) {
|
||||
NoteCall(Symbol::Flag::Function, fr.v);
|
||||
}
|
||||
void Post(const parser::CallStmt &cs) {
|
||||
NoteCall(Symbol::Flag::Subroutine, cs.v);
|
||||
}
|
||||
|
||||
private:
|
||||
void NoteCall(Symbol::Flag, const parser::Call &);
|
||||
|
||||
SemanticsContext &context_;
|
||||
Scope &scope_;
|
||||
};
|
||||
|
||||
void ExecutionPartSkimmer::NoteCall(
|
||||
Symbol::Flag flag, const parser::Call &call) {
|
||||
auto &designator{std::get<parser::ProcedureDesignator>(call.t)};
|
||||
if (const auto *name{std::get_if<parser::Name>(&designator.u)}) {
|
||||
if (Symbol * symbol{scope_.FindSymbol(name->source)}) {
|
||||
if (auto *details{symbol->detailsIf<EntityDetails>()}) {
|
||||
if (details->isDummy()) {
|
||||
symbol->set_details(ProcEntityDetails{std::move(*details)});
|
||||
symbol->set(flag);
|
||||
symbol->attrs().set(Attr::EXTERNAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the scope tree and resolve names in the specification parts of this
|
||||
// node and its children
|
||||
void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
|
||||
|
@ -4683,14 +4730,6 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
|
|||
if (node.IsModule()) {
|
||||
ApplyDefaultAccess();
|
||||
}
|
||||
for (auto &child : node.children()) {
|
||||
ResolveSpecificationParts(child);
|
||||
}
|
||||
// Subtlety: PopScope() is not called here because we want to defer
|
||||
// conversions of uncategorized entities into objects until after
|
||||
// we have traversed the executable part of the subprogram.
|
||||
// Function results, however, are converted now so that they can
|
||||
// be used in executable parts.
|
||||
if (Symbol * symbol{currScope().symbol()}) {
|
||||
if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
|
||||
if (details->isFunction()) {
|
||||
|
@ -4699,6 +4738,13 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
|
|||
}
|
||||
}
|
||||
}
|
||||
ExecutionPartSkimmer{context(), scope}.Walk(node.exec());
|
||||
for (auto &child : node.children()) {
|
||||
ResolveSpecificationParts(child);
|
||||
}
|
||||
// Subtlety: PopScope() is not called here because we want to defer
|
||||
// conversions of uncategorized entities into objects until after
|
||||
// we have traversed the executable part of the subprogram.
|
||||
SetScope(currScope().parent());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue