[flang-commits] [flang] [flang] Statement function dummy names do not clash with host or unreferenced global names (PR #212396)
via flang-commits
flang-commits at lists.llvm.org
Tue Jul 28 06:32:41 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Eugene Epshteyn (eugeneepshteyn)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/212396.diff
2 Files Affected:
- (modified) flang/lib/Semantics/resolve-names.cpp (+33-16)
- (modified) flang/test/Semantics/stmt-func04.f90 (+92-19)
``````````diff
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 83d92f253e626..f4e650ab05f33 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -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);
}
diff --git a/flang/test/Semantics/stmt-func04.f90 b/flang/test/Semantics/stmt-func04.f90
index 2b3c1ff008527..5ed0afbf45e3e 100644
--- a/flang/test/Semantics/stmt-func04.f90
+++ b/flang/test/Semantics/stmt-func04.f90
@@ -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
``````````
</details>
https://github.com/llvm/llvm-project/pull/212396
More information about the flang-commits
mailing list