Skip to content

Commit fa6b574

Browse files
authored
[flang] Account for shadowed symbols when skimming executable part (#70246)
Name resolution takes a quick pass over the executable part of a (sub)program in search of symbols that appear to be called as procedures, so that those names don't get mistakenly converted into objects when finishing up specification part processing. This pass doesn't currently cope with symbol shadowing by nested declarations in executable constructs. This patch ensures that nested declarations for symbols that could be used in contexts that might have been parsed as function references properly shadow symbols of the same name in outer scopes.
1 parent ae7b20b commit fa6b574

File tree

2 files changed

+206
-26
lines changed

2 files changed

+206
-26
lines changed

flang/lib/Semantics/resolve-names.cpp

+131-26
Original file line numberDiff line numberDiff line change
@@ -1573,8 +1573,7 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
15731573
llvm_unreachable("This node is handled in ProgramUnit");
15741574
}
15751575

1576-
void NoteExecutablePartCall(
1577-
Symbol::Flag, const parser::Call &, bool hasCUDAChevrons);
1576+
void NoteExecutablePartCall(Symbol::Flag, SourceName, bool hasCUDAChevrons);
15781577

15791578
friend void ResolveSpecificationParts(SemanticsContext &, const Symbol &);
15801579

@@ -7711,27 +7710,24 @@ bool ResolveNamesVisitor::CheckImplicitNoneExternal(
77117710
// of the subprogram's interface, and to mark as procedures any symbols
77127711
// that might otherwise have been miscategorized as objects.
77137712
void ResolveNamesVisitor::NoteExecutablePartCall(
7714-
Symbol::Flag flag, const parser::Call &call, bool hasCUDAChevrons) {
7715-
auto &designator{std::get<parser::ProcedureDesignator>(call.t)};
7716-
if (const auto *name{std::get_if<parser::Name>(&designator.u)}) {
7717-
// Subtlety: The symbol pointers in the parse tree are not set, because
7718-
// they might end up resolving elsewhere (e.g., construct entities in
7719-
// SELECT TYPE).
7720-
if (Symbol * symbol{currScope().FindSymbol(name->source)}) {
7721-
Symbol::Flag other{flag == Symbol::Flag::Subroutine
7722-
? Symbol::Flag::Function
7723-
: Symbol::Flag::Subroutine};
7724-
if (!symbol->test(other)) {
7725-
ConvertToProcEntity(*symbol);
7726-
if (auto *details{symbol->detailsIf<ProcEntityDetails>()}) {
7727-
symbol->set(flag);
7728-
if (IsDummy(*symbol)) {
7729-
SetImplicitAttr(*symbol, Attr::EXTERNAL);
7730-
}
7731-
ApplyImplicitRules(*symbol);
7732-
if (hasCUDAChevrons) {
7733-
details->set_isCUDAKernel();
7734-
}
7713+
Symbol::Flag flag, SourceName name, bool hasCUDAChevrons) {
7714+
// Subtlety: The symbol pointers in the parse tree are not set, because
7715+
// they might end up resolving elsewhere (e.g., construct entities in
7716+
// SELECT TYPE).
7717+
if (Symbol * symbol{currScope().FindSymbol(name)}) {
7718+
Symbol::Flag other{flag == Symbol::Flag::Subroutine
7719+
? Symbol::Flag::Function
7720+
: Symbol::Flag::Subroutine};
7721+
if (!symbol->test(other)) {
7722+
ConvertToProcEntity(*symbol);
7723+
if (auto *details{symbol->detailsIf<ProcEntityDetails>()}) {
7724+
symbol->set(flag);
7725+
if (IsDummy(*symbol)) {
7726+
SetImplicitAttr(*symbol, Attr::EXTERNAL);
7727+
}
7728+
ApplyImplicitRules(*symbol);
7729+
if (hasCUDAChevrons) {
7730+
details->set_isCUDAKernel();
77357731
}
77367732
}
77377733
}
@@ -8451,21 +8447,130 @@ class ExecutionPartSkimmer {
84518447
void Walk(const parser::ExecutionPart *exec) {
84528448
if (exec) {
84538449
parser::Walk(*exec, *this);
8450+
CHECK(nestedScopes_.empty());
84548451
}
84558452
}
84568453

84578454
template <typename A> bool Pre(const A &) { return true; }
84588455
template <typename A> void Post(const A &) {}
84598456
void Post(const parser::FunctionReference &fr) {
8460-
resolver_.NoteExecutablePartCall(Symbol::Flag::Function, fr.v, false);
8457+
NoteCall(Symbol::Flag::Function, fr.v, false);
84618458
}
84628459
void Post(const parser::CallStmt &cs) {
8463-
resolver_.NoteExecutablePartCall(
8464-
Symbol::Flag::Subroutine, cs.call, cs.chevrons.has_value());
8460+
NoteCall(Symbol::Flag::Subroutine, cs.call, cs.chevrons.has_value());
8461+
}
8462+
bool Pre(const parser::AssociateConstruct &) {
8463+
PushScope();
8464+
return true;
8465+
}
8466+
void Post(const parser::AssociateConstruct &) { PopScope(); }
8467+
bool Pre(const parser::Association &x) {
8468+
Hide(std::get<parser::Name>(x.t));
8469+
return true;
8470+
}
8471+
bool Pre(const parser::BlockConstruct &) {
8472+
PushScope();
8473+
return true;
8474+
}
8475+
void Post(const parser::BlockConstruct &) { PopScope(); }
8476+
bool Pre(const parser::EntityDecl &x) {
8477+
Hide(std::get<parser::ObjectName>(x.t));
8478+
return true;
8479+
}
8480+
void Post(const parser::ImportStmt &x) {
8481+
if (x.kind == common::ImportKind::None ||
8482+
x.kind == common::ImportKind::Only) {
8483+
if (!nestedScopes_.front().importOnly.has_value()) {
8484+
nestedScopes_.front().importOnly.emplace();
8485+
}
8486+
for (const auto &name : x.names) {
8487+
nestedScopes_.front().importOnly->emplace(name.source);
8488+
}
8489+
} else {
8490+
// no special handling needed for explicit names or IMPORT, ALL
8491+
}
8492+
}
8493+
void Post(const parser::UseStmt &x) {
8494+
if (const auto *onlyList{std::get_if<std::list<parser::Only>>(&x.u)}) {
8495+
for (const auto &only : *onlyList) {
8496+
if (const auto *name{std::get_if<parser::Name>(&only.u)}) {
8497+
Hide(*name);
8498+
} else if (const auto *rename{std::get_if<parser::Rename>(&only.u)}) {
8499+
if (const auto *names{
8500+
std::get_if<parser::Rename::Names>(&rename->u)}) {
8501+
Hide(std::get<0>(names->t));
8502+
}
8503+
}
8504+
}
8505+
} else {
8506+
// USE may or may not shadow symbols in host scopes
8507+
nestedScopes_.front().hasUseWithoutOnly = true;
8508+
}
8509+
}
8510+
bool Pre(const parser::DerivedTypeStmt &x) {
8511+
Hide(std::get<parser::Name>(x.t));
8512+
PushScope();
8513+
return true;
8514+
}
8515+
void Post(const parser::DerivedTypeDef &) { PopScope(); }
8516+
bool Pre(const parser::SelectTypeConstruct &) {
8517+
PushScope();
8518+
return true;
8519+
}
8520+
void Post(const parser::SelectTypeConstruct &) { PopScope(); }
8521+
bool Pre(const parser::SelectTypeStmt &x) {
8522+
if (const auto &maybeName{std::get<1>(x.t)}) {
8523+
Hide(*maybeName);
8524+
}
8525+
return true;
8526+
}
8527+
bool Pre(const parser::SelectRankConstruct &) {
8528+
PushScope();
8529+
return true;
8530+
}
8531+
void Post(const parser::SelectRankConstruct &) { PopScope(); }
8532+
bool Pre(const parser::SelectRankStmt &x) {
8533+
if (const auto &maybeName{std::get<1>(x.t)}) {
8534+
Hide(*maybeName);
8535+
}
8536+
return true;
84658537
}
84668538

84678539
private:
8540+
void NoteCall(
8541+
Symbol::Flag flag, const parser::Call &call, bool hasCUDAChevrons) {
8542+
auto &designator{std::get<parser::ProcedureDesignator>(call.t)};
8543+
if (const auto *name{std::get_if<parser::Name>(&designator.u)}) {
8544+
for (const auto &scope : nestedScopes_) {
8545+
if (scope.locals.find(name->source) != scope.locals.end()) {
8546+
return; // shadowed by nested declaration
8547+
}
8548+
if (scope.hasUseWithoutOnly) {
8549+
break;
8550+
}
8551+
if (scope.importOnly &&
8552+
scope.importOnly->find(name->source) == scope.importOnly->end()) {
8553+
return; // not imported
8554+
}
8555+
}
8556+
resolver_.NoteExecutablePartCall(flag, name->source, hasCUDAChevrons);
8557+
}
8558+
}
8559+
8560+
void PushScope() { nestedScopes_.emplace_front(); }
8561+
void PopScope() { nestedScopes_.pop_front(); }
8562+
void Hide(const parser::Name &name) {
8563+
nestedScopes_.front().locals.emplace(name.source);
8564+
}
8565+
84688566
ResolveNamesVisitor &resolver_;
8567+
8568+
struct NestedScopeInfo {
8569+
bool hasUseWithoutOnly{false};
8570+
std::set<SourceName> locals;
8571+
std::optional<std::set<SourceName>> importOnly;
8572+
};
8573+
std::list<NestedScopeInfo> nestedScopes_;
84698574
};
84708575

84718576
// Build the scope tree and resolve names in the specification parts of this

flang/test/Semantics/symbol32.f90

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
! RUN: %python %S/test_symbols.py %s %flang_fc1
2+
! Test the executable part skimming for apparent calls, to ensure that
3+
! symbols in nested scopes (BLOCK, &c.) properly shadow host symbols.
4+
!DEF: /m Module
5+
module m
6+
end module
7+
!DEF: /subr (Subroutine) Subprogram
8+
!DEF: /subr/da INTENT(IN) ObjectEntity CLASS(*)
9+
!DEF: /subr/ar INTENT(IN) ObjectEntity REAL(4)
10+
subroutine subr (da, ar)
11+
!REF: /subr/da
12+
class(*), intent(in) :: da(:)
13+
!REF: /subr/ar
14+
real, intent(in) :: ar(..)
15+
!DEF: /subr/s2 ObjectEntity REAL(4)
16+
!DEF: /subr/s4 ObjectEntity REAL(4)
17+
!DEF: /subr/s6 ObjectEntity REAL(4)
18+
!DEF: /subr/s7 (Function) ProcEntity REAL(4)
19+
!DEF: /subr/s8 ObjectEntity REAL(4)
20+
real s2, s4, s6, s7, s8
21+
!DEF: /s1 EXTERNAL (Function, Implicit) ProcEntity REAL(4)
22+
print *, s1(1)
23+
block
24+
!DEF: /subr/BlockConstruct1/s2 ObjectEntity INTEGER(4)
25+
!DEF: /subr/BlockConstruct1/s5 (Function) ProcEntity INTEGER(4)
26+
integer s2(10), s5
27+
!DEF: /subr/BlockConstruct1/s4 DerivedType
28+
type :: s4
29+
!DEF: /subr/BlockConstruct1/s4/n ObjectEntity INTEGER(4)
30+
integer :: n
31+
end type
32+
!REF: /subr/BlockConstruct1/s2
33+
print *, s2(1)
34+
!DEF: /s3 EXTERNAL (Function, Implicit) ProcEntity REAL(4)
35+
print *, s3(1)
36+
!REF: /subr/BlockConstruct1/s4
37+
print *, s4(1)
38+
!REF: /subr/BlockConstruct1/s5
39+
print *, s5(1)
40+
end block
41+
block
42+
import, none
43+
!DEF: /s2 EXTERNAL (Function, Implicit) ProcEntity REAL(4)
44+
print *, s2(1)
45+
end block
46+
block
47+
!REF: /subr/s6
48+
import, only: s6
49+
!DEF: /s8 EXTERNAL (Function, Implicit) ProcEntity REAL(4)
50+
print *, s8(1)
51+
end block
52+
block
53+
!REF: /m
54+
use :: m
55+
!REF: /subr/s7
56+
print *, s7(1)
57+
end block
58+
!DEF: /subr/OtherConstruct1/s2 AssocEntity REAL(4)
59+
associate (s2 => [1.])
60+
!REF: /subr/OtherConstruct1/s2
61+
print *, s2(1)
62+
end associate
63+
!REF: /subr/da
64+
select type (s2 => da)
65+
type is (real)
66+
!DEF: /subr/OtherConstruct2/s2 AssocEntity REAL(4)
67+
print *, s2(1)
68+
end select
69+
!REF: /subr/ar
70+
select rank (s2 => ar)
71+
rank (1)
72+
!DEF: /subr/OtherConstruct3/s2 AssocEntity REAL(4)
73+
print *, s2(1)
74+
end select
75+
end subroutine

0 commit comments

Comments
 (0)