[flang] Statement function dummy names do not clash with host or unreferenced global names (#212396)
The check added for F2023 19.4 p2 (statement function dummy argument
name may be the same as an accessible name only if that name is a scalar
variable) looked the name up through the whole host scope chain and,
failing that, in the global scope. Both lookups overshoot what is
"accessible" in the scoping unit:
* Per F2023 19.5.1.4 p2 item (11), the appearance of a name as a
dummy-arg-name in a stmt-function-stmt makes any host entity of that
name inaccessible by host association throughout the scoping unit, so a
host entity can never conflict with the dummy.
* A global entity to which the scoping unit makes no reference at all is
not accessible in it; otherwise conformance would depend on whether
unrelated program units happen to be compiled in the same file.
This caused bogus errors on conforming code such as
```
program p
logical, external :: x
contains
subroutine s
real :: f
f(x) = x * x ! 'x' is an implicitly typed real dummy here
end
end
```
where the dummy 'x' must be implicitly typed real (both NAG and gfortran
accept this and type it that way; flang already typed it correctly, but
rejected the program).
Restrict the lookup to names made visible by the scoping unit itself
(declared, referenced, or USE-associated there), matching the lookup
HandleStmtFunction already uses to type the dummies.
Assisted-by: AI
This commit is contained in:
parent
e24eb10725
commit
32eae3a394
2 changed files with 125 additions and 35 deletions
|
|
@ -10686,13 +10686,38 @@ void ResolveNamesVisitor::AnalyzeStmtFunctionStmt(
|
|||
return; // error recovery
|
||||
}
|
||||
|
||||
// F2023 19.4 p2: a statement-function dummy argument name may be the same
|
||||
// as an accessible name only if that name is a scalar variable. The lookup
|
||||
// walks the host chain (but not into the global scope), so host-associated
|
||||
// identifiers are also considered.
|
||||
// F2023 19.4 p2: a statement function dummy argument name may be the same
|
||||
// as an accessible name only if that name is a scalar variable. Only
|
||||
// names present in the current scope after specification-part resolution
|
||||
// can conflict: per 19.5.1.4 p2 item (11), the name's appearance as a
|
||||
// statement function dummy argument renders any host entity of that name
|
||||
// inaccessible by host association, and a global entity to which this
|
||||
// scoping unit makes no other reference is not accessible in it either.
|
||||
// (A name that becomes an external procedure name only by way of a
|
||||
// reference in the execution part is not yet visible here and is not
|
||||
// diagnosed.)
|
||||
const std::set<SourceName> importNames{currScope().importNames()};
|
||||
const common::ImportKind importKind{currScope().GetImportKind()};
|
||||
for (const auto &dummyName : std::get<std::list<parser::Name>>(stmtFunc.t)) {
|
||||
if (const Symbol *hostSymbol{currScope().FindSymbol(dummyName.source)}) {
|
||||
const Symbol &ultimate{hostSymbol->GetUltimate()};
|
||||
// F2023 C8106: an explicitly imported name, or any host name made
|
||||
// accessible by IMPORT, ALL, may not appear in a context that would
|
||||
// render the host entity inaccessible, and a statement function dummy
|
||||
// argument name is such a context (19.5.1.4 p2 item (11)). There is
|
||||
// no exception for scalar variables. A plain IMPORT statement does
|
||||
// not protect names from being hidden (8.8 p4).
|
||||
if (importNames.count(dummyName.source) > 0 ||
|
||||
importKind == common::ImportKind::All) {
|
||||
if (const Symbol *host{
|
||||
currScope().parent().FindSymbol(dummyName.source)}) {
|
||||
Say(dummyName.source,
|
||||
"'%s' from host may not be hidden by a statement function dummy argument"_err_en_US,
|
||||
dummyName.source)
|
||||
.Attach(host->name(), "Declaration of '%s'"_en_US, host->name());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (const Symbol *local{FindInScope(currScope(), dummyName.source)}) {
|
||||
const Symbol &ultimate{local->GetUltimate()};
|
||||
const bool isScalarVariable{(ultimate.has<ObjectEntityDetails>() ||
|
||||
ultimate.has<EntityDetails>()) &&
|
||||
!IsNamedConstant(ultimate) && ultimate.Rank() == 0};
|
||||
|
|
@ -10700,14 +10725,6 @@ void ResolveNamesVisitor::AnalyzeStmtFunctionStmt(
|
|||
Say(dummyName.source,
|
||||
"The name '%s' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable"_err_en_US);
|
||||
}
|
||||
} else {
|
||||
// Also check the global scope, which FindSymbol skips
|
||||
const Scope &globals{context().globalScope()};
|
||||
if (auto it{globals.find(dummyName.source)}; it != globals.end()) {
|
||||
// global function/subroutine — definitely not a scalar variable
|
||||
Say(dummyName.source,
|
||||
"The name '%s' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable"_err_en_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10740,7 +10757,7 @@ void ResolveNamesVisitor::CheckImports() {
|
|||
case common::ImportKind::None:
|
||||
break;
|
||||
case common::ImportKind::All:
|
||||
// C8102: all entities in host must not be hidden
|
||||
// F2023 C8106: all entities in host must not be hidden
|
||||
for (const auto &pair : scope.parent()) {
|
||||
auto &name{pair.first};
|
||||
std::optional<SourceName> scopeName{scope.GetName()};
|
||||
|
|
@ -10751,7 +10768,7 @@ void ResolveNamesVisitor::CheckImports() {
|
|||
break;
|
||||
case common::ImportKind::Default:
|
||||
case common::ImportKind::Only:
|
||||
// C8102: entities named in IMPORT must not be hidden
|
||||
// F2023 C8106: entities named in IMPORT must not be hidden
|
||||
for (auto &name : scope.importNames()) {
|
||||
CheckImport(name, name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
! RUN: %python %S/test_errors.py %s %flang_fc1
|
||||
! F2023 19.4 p2: a statement function dummy argument name may be the same as an
|
||||
! accessible global identifier or local identifier of class (1) only if that
|
||||
! name is a scalar variable.
|
||||
! name is a scalar variable. Only names made visible by the scoping unit
|
||||
! itself can conflict: per 19.5.1.4 p2 item (11), the name's appearance as a
|
||||
! statement function dummy argument renders any host entity of that name
|
||||
! inaccessible by host association, and a global entity to which the scoping
|
||||
! unit makes no other reference is not accessible in it either.
|
||||
|
||||
! Clashes within the statement function's own scoping unit.
|
||||
subroutine local_clashes
|
||||
|
|
@ -22,29 +26,28 @@ subroutine local_clashes
|
|||
f5(scalarvar) = scalarvar + 1 ! ok: scalar variable shadowing is permitted
|
||||
end subroutine
|
||||
|
||||
! Clashes with host-associated identifiers (module scope).
|
||||
! No clashes with host entities (module scope): the dummy argument's
|
||||
! appearance blocks host association (19.5.1.4 p2 item (11)), so the host
|
||||
! entities are inaccessible here and the dummies are implicitly typed.
|
||||
module m
|
||||
integer :: hostarr(10)
|
||||
integer :: hostscalar
|
||||
integer, parameter :: hostconst = 3
|
||||
contains
|
||||
subroutine host_clashes
|
||||
!ERROR: The name 'hostarr' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
g1(hostarr) = hostarr + 1
|
||||
!ERROR: The name 'hostconst' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
g2(hostconst) = hostconst + 1
|
||||
subroutine host_no_clashes
|
||||
g1(hostarr) = hostarr + 1 ! ok: host's 'hostarr' is inaccessible here
|
||||
g2(hostconst) = hostconst + 1 ! ok: host's 'hostconst' is inaccessible here
|
||||
g3(hostscalar) = hostscalar + 1 ! ok: host scalar variable
|
||||
end subroutine
|
||||
end module
|
||||
|
||||
! Clashes with grandparent-associated identifiers (internal procedure).
|
||||
! Same for grandparent host entities (internal procedure).
|
||||
program p
|
||||
integer :: grandarr(5)
|
||||
integer :: grandscalar
|
||||
contains
|
||||
subroutine grand_clashes
|
||||
!ERROR: The name 'grandarr' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
h1(grandarr) = grandarr + 1
|
||||
subroutine grand_no_clashes
|
||||
h1(grandarr) = grandarr + 1 ! ok: host's 'grandarr' is inaccessible here
|
||||
h2(grandscalar) = grandscalar + 1 ! ok: grandparent scalar variable
|
||||
end subroutine
|
||||
end program
|
||||
|
|
@ -64,23 +67,22 @@ subroutine use_clashes
|
|||
k3(usescalar) = usescalar + 1 ! ok: USE-associated scalar variable
|
||||
end subroutine
|
||||
|
||||
! Clashes with global-scope program units.
|
||||
! No clash with a global-scope program unit to which the scoping unit makes
|
||||
! no other reference: that global identifier is not accessible in it.
|
||||
real function global_func(x)
|
||||
real :: x
|
||||
global_func = x
|
||||
end function
|
||||
subroutine global_clashes
|
||||
!ERROR: The name 'global_func' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
p1(global_func) = global_func + 1
|
||||
subroutine global_no_clashes
|
||||
p1(global_func) = global_func + 1 ! ok: 'global_func' is not accessible here
|
||||
end subroutine
|
||||
|
||||
! Clashes with bind(c) global subprogram (exercises global scope path).
|
||||
! Likewise for a bind(c) global subprogram.
|
||||
real function bindc_global_func() bind(c)
|
||||
bindc_global_func = 1.0
|
||||
end function
|
||||
subroutine bindc_global_clashes
|
||||
!ERROR: The name 'bindc_global_func' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
r1(bindc_global_func) = bindc_global_func + 1
|
||||
subroutine bindc_global_no_clashes
|
||||
r1(bindc_global_func) = bindc_global_func + 1 ! ok: not accessible here
|
||||
end subroutine
|
||||
|
||||
! Clashes with bind(c) external declared via local explicit interface block.
|
||||
|
|
@ -117,3 +119,74 @@ subroutine bindc_use_proc_clashes
|
|||
!ERROR: The name 'c_mod_func' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
r5(c_mod_func) = c_mod_func + 1
|
||||
end subroutine
|
||||
|
||||
! A host EXTERNAL declaration does not make the name accessible in an inner
|
||||
! subprogram that uses it as a statement function dummy argument; host
|
||||
! association of the name is blocked throughout that subprogram (19.5.1.4 p2
|
||||
! item (11)), so the dummy is implicitly typed there (default real). If host
|
||||
! association leaked through, 'pred * pred' would be a type error.
|
||||
module m_host_external
|
||||
logical, external :: pred
|
||||
contains
|
||||
subroutine eval(arg)
|
||||
real :: sq
|
||||
sq(pred) = pred * pred ! ok: 'pred' is an implicitly typed real dummy here
|
||||
print *, sq(arg)
|
||||
end subroutine
|
||||
subroutine other
|
||||
logical :: q
|
||||
q = pred() ! ok: host association of 'pred' is severed only in 'eval'
|
||||
print *, q
|
||||
end subroutine
|
||||
end module
|
||||
|
||||
! F2023 C8106: an explicitly imported host name, or any host name made
|
||||
! accessible by IMPORT, ALL, may not be hidden, and a statement function
|
||||
! dummy argument of that name would hide it. There is no scalar-variable
|
||||
! exception here. A plain IMPORT does not protect names from being hidden
|
||||
! (8.8 p4).
|
||||
subroutine import_host
|
||||
real :: val
|
||||
contains
|
||||
subroutine only_import
|
||||
import, only: val
|
||||
!ERROR: 'val' from host may not be hidden by a statement function dummy argument
|
||||
w1(val) = val + 1
|
||||
end subroutine
|
||||
subroutine named_import
|
||||
import :: val
|
||||
!ERROR: 'val' from host may not be hidden by a statement function dummy argument
|
||||
w2(val) = val + 1
|
||||
end subroutine
|
||||
subroutine all_import
|
||||
import, all
|
||||
!ERROR: 'val' from host may not be hidden by a statement function dummy argument
|
||||
w3(val) = val + 1
|
||||
end subroutine
|
||||
subroutine plain_import
|
||||
import
|
||||
w4(val) = val + 1 ! ok: plain IMPORT tolerates hiding (8.8 p4)
|
||||
end subroutine
|
||||
end subroutine
|
||||
|
||||
! Boundary cases that remain errors: the enclosing subprogram's own name and
|
||||
! an ENTRY name of the same subprogram are visible in the subprogram itself.
|
||||
subroutine self_clash
|
||||
real :: f
|
||||
!ERROR: The name 'self_clash' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
f(self_clash) = self_clash + 1
|
||||
end subroutine
|
||||
subroutine entry_clash
|
||||
real :: g
|
||||
!ERROR: The name 'ent' of a statement function dummy argument may not be the same as an accessible name unless that name is a scalar variable
|
||||
g(ent) = ent + 1
|
||||
return
|
||||
entry ent
|
||||
end subroutine
|
||||
|
||||
! A common block name is explicitly excepted by 19.4 p2.
|
||||
subroutine common_carveout
|
||||
real :: y, f
|
||||
common /cb/ y
|
||||
f(cb) = cb + 1 ! ok: common block name carve-out
|
||||
end subroutine
|
||||
|
|
|
|||
Loading…
Reference in a new issue