Skip to content

Commit 8f5df88

Browse files
authored
[Flang][Semantics] Allow declare target to be used on functions external to the declare targets scope (#122546)
Whilst a little contrived, OpenMP allows you to utilise declare target in the scope of one function to mark another function declare target, currently this leads to a semantic error. This appears to be because when we process the declare target directive in the scope of another function (referring to another function), we do not search externally from that functions scope to find possible prior definitions, we only search in the current scope, this leads to us implicitly defining a new variable and using that when implicit none is not specified and then error'ng out or error'ng out earlier when implict none is defined. This patch tries to address this behaviour by looking externally for a function first and using that, before defaulting back to the prior behaviour.
1 parent f5f32ce commit 8f5df88

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

flang/lib/Semantics/resolve-names.cpp

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,8 @@ class ScopeHandler : public ImplicitRulesVisitor {
736736
std::vector<const std::list<parser::EquivalenceObject> *> equivalenceSets;
737737
// Names of all common block objects in the scope
738738
std::set<SourceName> commonBlockObjects;
739+
// Names of all names that show in a declare target declaration
740+
std::set<SourceName> declareTargetNames;
739741
// Info about SAVE statements and attributes in current scope
740742
struct {
741743
std::optional<SourceName> saveAll; // "SAVE" without entity list
@@ -1223,6 +1225,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
12231225
const parser::Name *FindComponent(const parser::Name *, const parser::Name &);
12241226
void Initialization(const parser::Name &, const parser::Initialization &,
12251227
bool inComponentDecl);
1228+
bool FindAndMarkDeclareTargetSymbol(const parser::Name &);
12261229
bool PassesLocalityChecks(
12271230
const parser::Name &name, Symbol &symbol, Symbol::Flag flag);
12281231
bool CheckForHostAssociatedImplicit(const parser::Name &);
@@ -1524,7 +1527,47 @@ class OmpVisitor : public virtual DeclarationVisitor {
15241527
return true;
15251528
}
15261529
void Post(const parser::OpenMPThreadprivate &) { SkipImplicitTyping(false); }
1527-
bool Pre(const parser::OpenMPDeclareTargetConstruct &) {
1530+
bool Pre(const parser::OpenMPDeclareTargetConstruct &x) {
1531+
const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
1532+
auto populateDeclareTargetNames{
1533+
[this](const parser::OmpObjectList &objectList) {
1534+
for (const auto &ompObject : objectList.v) {
1535+
common::visit(
1536+
common::visitors{
1537+
[&](const parser::Designator &designator) {
1538+
if (const auto *name{
1539+
semantics::getDesignatorNameIfDataRef(
1540+
designator)}) {
1541+
specPartState_.declareTargetNames.insert(name->source);
1542+
}
1543+
},
1544+
[&](const parser::Name &name) {
1545+
specPartState_.declareTargetNames.insert(name.source);
1546+
},
1547+
},
1548+
ompObject.u);
1549+
}
1550+
}};
1551+
1552+
if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) {
1553+
populateDeclareTargetNames(*objectList);
1554+
} else if (const auto *clauseList{
1555+
parser::Unwrap<parser::OmpClauseList>(spec.u)}) {
1556+
for (const auto &clause : clauseList->v) {
1557+
if (const auto *toClause{
1558+
std::get_if<parser::OmpClause::To>(&clause.u)}) {
1559+
populateDeclareTargetNames(
1560+
std::get<parser::OmpObjectList>(toClause->v.t));
1561+
} else if (const auto *linkClause{
1562+
std::get_if<parser::OmpClause::Link>(&clause.u)}) {
1563+
populateDeclareTargetNames(linkClause->v);
1564+
} else if (const auto *enterClause{
1565+
std::get_if<parser::OmpClause::Enter>(&clause.u)}) {
1566+
populateDeclareTargetNames(enterClause->v);
1567+
}
1568+
}
1569+
}
1570+
15281571
SkipImplicitTyping(true);
15291572
return true;
15301573
}
@@ -8126,7 +8169,12 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
81268169
// If implicit types are allowed, ensure name is in the symbol table.
81278170
// Otherwise, report an error if it hasn't been declared.
81288171
const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
8129-
FindSymbol(name);
8172+
if (!FindSymbol(name)) {
8173+
if (FindAndMarkDeclareTargetSymbol(name)) {
8174+
return &name;
8175+
}
8176+
}
8177+
81308178
if (CheckForHostAssociatedImplicit(name)) {
81318179
NotePossibleBadForwardRef(name);
81328180
return &name;
@@ -8313,6 +8361,48 @@ const parser::Name *DeclarationVisitor::FindComponent(
83138361
return nullptr;
83148362
}
83158363

8364+
bool DeclarationVisitor::FindAndMarkDeclareTargetSymbol(
8365+
const parser::Name &name) {
8366+
if (!specPartState_.declareTargetNames.empty()) {
8367+
if (specPartState_.declareTargetNames.count(name.source)) {
8368+
if (!currScope().IsTopLevel()) {
8369+
// Search preceding scopes until we find a matching symbol or run out
8370+
// of scopes to search, we skip the current scope as it's already been
8371+
// designated as implicit here.
8372+
Symbol *symbol = nullptr;
8373+
for (auto *scope = &currScope().parent();; scope = &scope->parent()) {
8374+
if (Symbol * symbol{scope->FindSymbol(name.source)}) {
8375+
if (symbol->test(Symbol::Flag::Subroutine) ||
8376+
symbol->test(Symbol::Flag::Function)) {
8377+
const auto [sym, success]{currScope().try_emplace(
8378+
symbol->name(), Attrs{}, HostAssocDetails{*symbol})};
8379+
assert(success &&
8380+
"FindAndMarkDeclareTargetSymbol could not emplace new "
8381+
"subroutine/function symbol");
8382+
name.symbol = &*sym->second;
8383+
symbol->test(Symbol::Flag::Subroutine)
8384+
? name.symbol->set(Symbol::Flag::Subroutine)
8385+
: name.symbol->set(Symbol::Flag::Function);
8386+
return true;
8387+
}
8388+
// if we find a symbol that is not a function or subroutine, we
8389+
// currently escape without doing anything.
8390+
break;
8391+
}
8392+
8393+
// This is our loop exit condition, as parent() has an inbuilt assert
8394+
// if you call it on a top level scope, rather than returning a null
8395+
// value.
8396+
if (scope->IsTopLevel()) {
8397+
return false;
8398+
}
8399+
}
8400+
}
8401+
}
8402+
}
8403+
return false;
8404+
}
8405+
83168406
void DeclarationVisitor::Initialization(const parser::Name &name,
83178407
const parser::Initialization &init, bool inComponentDecl) {
83188408
// Traversal of the initializer was deferred to here so that the
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
! RUN: %flang_fc1 -fopenmp -fdebug-dump-symbols %s | FileCheck %s
2+
3+
subroutine bar(i, a)
4+
!$omp declare target
5+
real :: a
6+
integer :: i
7+
a = a - i
8+
end subroutine
9+
10+
function baz(a)
11+
!$omp declare target
12+
real, intent(in) :: a
13+
baz = a
14+
end function baz
15+
16+
program main
17+
real a
18+
!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc
19+
!CHECK: baz (Function, OmpDeclareTarget): HostAssoc
20+
!$omp declare target(bar)
21+
!$omp declare target(baz)
22+
23+
a = baz(a)
24+
call bar(2,a)
25+
call foo(a)
26+
return
27+
end
28+
29+
subroutine foo(a)
30+
real a
31+
integer i
32+
!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc
33+
!CHECK: baz (Function, OmpDeclareTarget): HostAssoc
34+
!$omp declare target(bar)
35+
!$omp declare target(baz)
36+
!$omp target
37+
a = baz(a)
38+
call bar(i,a)
39+
!$omp end target
40+
return
41+
end

0 commit comments

Comments
 (0)