[flang] Improve handling of generics with same name as type or procedure

Create symbols for generics in a pre-pass over the specification part so
it is easier to handle cases when they have the same name as a derived
type or subprogram. This is done by calling `PreSpecificationConstruct`
on each `SpecificationConstruct` of a specification part before we
continue walking it. The generics symbols are created there and the same
mechanism will be used to handle forward references to derived types.

Report an error when the same name is used for a generic interface,
derived type, and subprogram.

Improve the error message issued when a procedure and generic interface
have the same name but the procedure is not a specific of the generic.

Change `SayAlreadyDeclared` to report the error on the second occurence
of the name when possible. This can arise for declarations the are
processed out of order, e.g. contained subprograms and generic interfaces.

Avoid multiple "already declared" errors for the case when a contained
subprogram has the same name as a declared entity. We first create the
symbol with SubprogramNameDetails, then replace it with the entity (and
report the error), then replace it with the real subprogram (and get the
error again). By setting and checking the error flag we avoid the second
error.

Original-commit: flang-compiler/f18@48fc076783
Reviewed-on: https://github.com/flang-compiler/f18/pull/614
Tree-same-pre-rewrite: false
This commit is contained in:
Tim Keith 2019-07-30 15:29:50 -07:00
parent 1f490974d8
commit ebe4ff24b4
8 changed files with 164 additions and 93 deletions

View File

@ -430,8 +430,9 @@ public:
// Special messages: already declared; referencing symbol's declaration;
// about a type; two names & locations
void SayAlreadyDeclared(const SourceName &, Symbol &);
void SayAlreadyDeclared(const parser::Name &, Symbol &);
void SayAlreadyDeclared(const SourceName &, Symbol &);
void SayAlreadyDeclared(const SourceName &, const SourceName &);
void SayWithReason(
const parser::Name &, Symbol &, MessageFixedText &&, MessageFixedText &&);
void SayWithDecl(const parser::Name &, Symbol &, MessageFixedText &&);
@ -489,16 +490,18 @@ public:
}
if constexpr (std::is_same_v<DerivedTypeDetails, D>) {
if (auto *d{symbol->detailsIf<GenericDetails>()}) {
// derived type with same name as a generic
auto *derivedType{d->derivedType()};
if (!derivedType) {
derivedType =
&currScope().MakeSymbol(name, attrs, std::move(details));
d->set_derivedType(*derivedType);
} else {
SayAlreadyDeclared(name, *derivedType);
if (!d->specific()) {
// derived type with same name as a generic
auto *derivedType{d->derivedType()};
if (!derivedType) {
derivedType =
&currScope().MakeSymbol(name, attrs, std::move(details));
d->set_derivedType(*derivedType);
} else {
SayAlreadyDeclared(name, *derivedType);
}
return *derivedType;
}
return *derivedType;
}
}
if (symbol->CanReplaceDetails(details)) {
@ -513,7 +516,9 @@ public:
SayAlreadyDeclared(name, *symbol);
// replace the old symbol with a new one with correct details
EraseSymbol(*symbol);
return MakeSymbol(name, attrs, std::move(details));
auto &result{MakeSymbol(name, attrs, std::move(details))};
context().SetError(result);
return result;
}
}
@ -1029,7 +1034,7 @@ public:
template<typename T> bool Pre(const T &) { return true; }
template<typename T> void Post(const T &) {}
void Post(const parser::SpecificationPart &);
bool Pre(const parser::SpecificationPart &);
void Post(const parser::Program &);
bool Pre(const parser::ImplicitStmt &);
void Post(const parser::PointerObject &);
@ -1065,6 +1070,9 @@ private:
std::optional<Symbol::Flag> expectedProcFlag_;
const SourceName *prevImportStmt_{nullptr};
void PreSpecificationConstruct(const parser::SpecificationConstruct &);
void CreateGeneric(const parser::GenericSpec &);
void FinishSpecificationPart();
void CheckImports();
void CheckImport(const SourceName &, const SourceName &);
void HandleCall(Symbol::Flag, const parser::Call &);
@ -1517,17 +1525,27 @@ void ScopeHandler::SayAlreadyDeclared(const parser::Name &name, Symbol &prev) {
SayAlreadyDeclared(name.source, prev);
}
void ScopeHandler::SayAlreadyDeclared(const SourceName &name, Symbol &prev) {
auto &msg{
Say(name, "'%s' is already declared in this scoping unit"_err_en_US)};
if (const auto *details{prev.detailsIf<UseDetails>()}) {
msg.Attach(details->location(),
"It is use-associated with '%s' in module '%s'"_err_en_US,
details->symbol().name(), details->module().name());
if (context().HasError(prev)) {
// don't report another error about prev
} else if (const auto *details{prev.detailsIf<UseDetails>()}) {
Say(name, "'%s' is already declared in this scoping unit"_err_en_US)
.Attach(details->location(),
"It is use-associated with '%s' in module '%s'"_err_en_US,
details->symbol().name(), details->module().name());
} else {
msg.Attach(prev.name(), "Previous declaration of '%s'"_en_US, prev.name());
SayAlreadyDeclared(name, prev.name());
}
context().SetError(prev);
}
void ScopeHandler::SayAlreadyDeclared(
const SourceName &name1, const SourceName &name2) {
if (name1.begin() < name2.begin()) {
SayAlreadyDeclared(name2, name1);
} else {
Say(name1, "'%s' is already declared in this scoping unit"_err_en_US)
.Attach(name2, "Previous declaration of '%s'"_en_US, name2);
}
}
void ScopeHandler::SayWithReason(const parser::Name &name, Symbol &symbol,
MessageFixedText &&msg1, MessageFixedText &&msg2) {
@ -2067,65 +2085,9 @@ void InterfaceVisitor::Post(const parser::EndInterfaceStmt &) {
// Create a symbol in genericSymbol_ for this GenericSpec.
bool InterfaceVisitor::Pre(const parser::GenericSpec &x) {
auto info{GenericSpecInfo{x}};
const SourceName &symbolName{info.symbolName()};
if (IsLogicalConstant(context(), symbolName)) {
Say(symbolName,
"Logical constant '%s' may not be used as a defined operator"_err_en_US);
return false;
if (auto *symbol{currScope().FindSymbol(GenericSpecInfo{x}.symbolName())}) {
SetGenericSymbol(*symbol);
}
Symbol *symbol{currScope().FindSymbol(symbolName)};
if (symbol) {
if (symbol->has<DerivedTypeDetails>()) {
// A generic and derived type with same name: create a generic symbol
// and save derived type in it.
CHECK(symbol->scope()->symbol() == symbol);
GenericDetails details;
details.set_derivedType(*symbol);
EraseSymbol(*symbol);
symbol = &MakeSymbol(symbolName);
symbol->set_details(details);
// preserve access attributes
symbol->attrs() |=
details.derivedType()->attrs() & Attrs{Attr::PUBLIC, Attr::PRIVATE};
} else if (symbol->has<UnknownDetails>()) {
// okay
} else if (!symbol->IsSubprogram()) {
SayAlreadyDeclared(symbolName, *symbol);
EraseSymbol(*symbol);
symbol = nullptr;
} else if (symbol->has<UseDetails>()) {
// copy the USEd symbol into this scope so we can modify it
const Symbol &ultimate{symbol->GetUltimate()};
EraseSymbol(*symbol);
symbol = &CopySymbol(symbolName, ultimate);
if (const auto *details{ultimate.detailsIf<GenericDetails>()}) {
symbol->set_details(GenericDetails{details->specificProcs()});
} else if (const auto *details{ultimate.detailsIf<SubprogramDetails>()}) {
symbol->set_details(SubprogramDetails{*details});
} else {
DIE("unexpected kind of symbol");
}
}
}
if (!symbol || symbol->has<UnknownDetails>()) {
symbol = &MakeSymbol(symbolName);
symbol->set_details(GenericDetails{});
}
if (symbol->has<GenericDetails>()) {
// okay
} else if (symbol->has<SubprogramDetails>() ||
symbol->has<SubprogramNameDetails>()) {
GenericDetails genericDetails;
genericDetails.set_specific(*symbol);
EraseSymbol(*symbol);
symbol = &MakeSymbol(symbolName);
symbol->set_details(genericDetails);
} else {
common::die("unexpected kind of symbol");
}
info.Resolve(symbol);
SetGenericSymbol(*symbol);
return false;
}
@ -2237,7 +2199,14 @@ void InterfaceVisitor::CheckGenericProcedures(Symbol &generic) {
ResolveSpecificsInGeneric(generic);
auto &details{generic.get<GenericDetails>()};
if (auto *proc{details.CheckSpecific()}) {
SayAlreadyDeclared(generic.name(), *proc);
auto msg{
"'%s' may not be the name of both a generic interface and a"
" procedure unless it is a specific procedure of the generic"_err_en_US};
if (proc->name().begin() > generic.name().begin()) {
Say(proc->name(), std::move(msg));
} else {
Say(generic.name(), std::move(msg));
}
}
auto &specifics{details.specificProcs()};
if (specifics.empty()) {
@ -5027,7 +4996,77 @@ static bool NeedsExplicitType(const Symbol &symbol) {
}
}
void ResolveNamesVisitor::Post(const parser::SpecificationPart &) {
bool ResolveNamesVisitor::Pre(const parser::SpecificationPart &x) {
Walk(std::get<0>(x.t));
Walk(std::get<1>(x.t));
Walk(std::get<2>(x.t));
Walk(std::get<3>(x.t));
const std::list<parser::DeclarationConstruct> &decls{std::get<4>(x.t)};
for (const auto &decl : decls) {
if (const auto *spec{
std::get_if<parser::SpecificationConstruct>(&decl.u)}) {
PreSpecificationConstruct(*spec);
}
}
Walk(decls);
FinishSpecificationPart();
return false;
}
// Initial processing on specification constructs, before visiting them.
void ResolveNamesVisitor::PreSpecificationConstruct(
const parser::SpecificationConstruct &spec) {
std::visit(
common::visitors{
[&](const Indirection<parser::DerivedTypeDef> &y) {},
[&](const parser::Statement<Indirection<parser::GenericStmt>> &y) {
CreateGeneric(std::get<parser::GenericSpec>(y.statement.value().t));
},
[&](const Indirection<parser::InterfaceBlock> &y) {
const auto &stmt{std::get<parser::Statement<parser::InterfaceStmt>>(
y.value().t)};
const auto *spec{std::get_if<std::optional<parser::GenericSpec>>(
&stmt.statement.u)};
if (spec && spec->has_value()) {
CreateGeneric(**spec);
}
},
[&](const auto &) {},
},
spec.u);
}
void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
auto info{GenericSpecInfo{x}};
const SourceName &symbolName{info.symbolName()};
if (IsLogicalConstant(context(), symbolName)) {
Say(symbolName,
"Logical constant '%s' may not be used as a defined operator"_err_en_US);
return;
}
GenericDetails genericDetails;
if (Symbol * existing{currScope().FindSymbol(symbolName)}) {
if (existing->has<GenericDetails>()) {
info.Resolve(existing);
return; // already have generic, add to it
}
Symbol &ultimate{existing->GetUltimate()};
if (ultimate.has<GenericDetails>()) {
genericDetails.AddSpecificProcsFrom(ultimate);
} else if (ultimate.has<SubprogramDetails>() ||
ultimate.has<SubprogramNameDetails>()) {
genericDetails.set_specific(ultimate);
} else if (ultimate.has<DerivedTypeDetails>()) {
genericDetails.set_derivedType(ultimate);
} else {
SayAlreadyDeclared(symbolName, *existing);
}
EraseSymbol(*existing);
}
info.Resolve(&MakeSymbol(symbolName, Attrs{}, std::move(genericDetails)));
}
void ResolveNamesVisitor::FinishSpecificationPart() {
badStmtFuncFound_ = false;
CheckImports();
bool inModule{currScope().kind() == Scope::Kind::Module};

View File

@ -57,6 +57,7 @@ public:
// Don't bother resolving names in end statements.
bool Pre(parser::EndBlockDataStmt &) { return false; }
bool Pre(parser::EndFunctionStmt &) { return false; }
bool Pre(parser::EndInterfaceStmt &) { return false; }
bool Pre(parser::EndModuleStmt &) { return false; }
bool Pre(parser::EndMpSubprogramStmt &) { return false; }
bool Pre(parser::EndProgramStmt &) { return false; }

View File

@ -157,9 +157,11 @@ GenericDetails::GenericDetails(const SymbolVector &specificProcs)
void GenericDetails::set_specific(Symbol &specific) {
CHECK(!specific_);
CHECK(!derivedType_);
specific_ = &specific;
}
void GenericDetails::set_derivedType(Symbol &derivedType) {
CHECK(!specific_);
CHECK(!derivedType_);
derivedType_ = &derivedType;
}
@ -417,6 +419,9 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
[](const HostAssocDetails &) {},
[&](const GenericDetails &x) {
os << ' ' << EnumToString(x.kind());
DumpBool(os, "(specific)", x.specific() != nullptr);
DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
os << " procs:";
DumpSymbolVector(os, x.specificProcs());
},
[&](const ProcBindingDetails &x) {

View File

@ -402,11 +402,11 @@ public:
void add_specificProc(const Symbol &proc) { specificProcs_.push_back(&proc); }
void AddSpecificProcsFrom(const Symbol &generic);
// specific and derivedType indicate a specific procedure or derived type
// with the same name as this generic. Only one of them may be set.
Symbol *specific() { return specific_; }
const Symbol *specific() const { return specific_; }
void set_specific(Symbol &specific);
// Derived type with same name as generic, if any.
Symbol *derivedType() { return derivedType_; }
const Symbol *derivedType() const { return derivedType_; }
void set_derivedType(Symbol &derivedType);

View File

@ -80,9 +80,6 @@ module m2
interface foo
procedure foo
end interface
type :: foo
real :: x
end type
contains
complex function foo()
foo = 1.0
@ -91,9 +88,6 @@ end
!Expect: m2.mod
!module m2
! generic::foo=>foo
! type::foo
! real(4)::x
! end type
!contains
! function foo()
! complex(4)::foo

View File

@ -21,17 +21,16 @@ module m
end module
module m2
!Note: PGI and GNU allow this; Intel, NAG, and Sun do not
!ERROR: 's' is already declared in this scoping unit
interface s
end interface
contains
!ERROR: 's' may not be the name of both a generic interface and a procedure unless it is a specific procedure of the generic
subroutine s
end subroutine
end module
module m3
! This is okay: so is generic and specific
! This is okay: s is generic and specific
interface s
procedure s2
end interface

View File

@ -1,4 +1,4 @@
! Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
! Copyright (c) 2018-2019, 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.
@ -24,7 +24,7 @@ end module
module m2
use m1
implicit none
!ERROR: 'foo' is already declared in this scoping unit
!ERROR: 'foo' may not be the name of both a generic interface and a procedure unless it is a specific procedure of the generic
interface foo
module procedure s
end interface
@ -43,3 +43,36 @@ subroutine bar
!ERROR: Cannot use-associate 'bar'; it is already declared in this scope
use m1, bar => foo
end
!OK to use-associate a type with the same name as a generic
module m3a
type :: foo
end type
end
module m3b
use m3a
interface foo
end interface
end
! Can't have derived type and function with same name
module m4a
type :: foo
end type
contains
!ERROR: 'foo' is already declared in this scoping unit
function foo(x)
end
end
! Even if there is also a generic interface of that name
module m4b
type :: foo
end type
!ERROR: 'foo' is already declared in this scoping unit
interface foo
procedure :: foo
end interface foo
contains
function foo(x)
end
end

View File

@ -49,7 +49,6 @@ module m2
module integer function fun1()
end function
end interface
!ERROR: 't' is already declared in this scoping unit
type t
end type
!ERROR: Declaration of 'i' conflicts with its use as module procedure
@ -61,6 +60,7 @@ contains
!ERROR: 'missing2' was not declared a separate module procedure
module subroutine missing2
end
!ERROR: 't' is already declared in this scoping unit
!ERROR: 't' was not declared a separate module procedure
module procedure t
end