diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index f3c2a5bf094d0..9857ae61939d7 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -736,6 +736,8 @@ class ScopeHandler : public ImplicitRulesVisitor { std::vector *> equivalenceSets; // Names of all common block objects in the scope std::set commonBlockObjects; + // Names of all names that show in a declare target declaration + std::set declareTargetNames; // Info about SAVE statements and attributes in current scope struct { std::optional saveAll; // "SAVE" without entity list @@ -1223,6 +1225,7 @@ class DeclarationVisitor : public ArraySpecVisitor, const parser::Name *FindComponent(const parser::Name *, const parser::Name &); void Initialization(const parser::Name &, const parser::Initialization &, bool inComponentDecl); + bool FindAndMarkDeclareTargetSymbol(const parser::Name &); bool PassesLocalityChecks( const parser::Name &name, Symbol &symbol, Symbol::Flag flag); bool CheckForHostAssociatedImplicit(const parser::Name &); @@ -1524,7 +1527,47 @@ class OmpVisitor : public virtual DeclarationVisitor { return true; } void Post(const parser::OpenMPThreadprivate &) { SkipImplicitTyping(false); } - bool Pre(const parser::OpenMPDeclareTargetConstruct &) { + bool Pre(const parser::OpenMPDeclareTargetConstruct &x) { + const auto &spec{std::get(x.t)}; + auto populateDeclareTargetNames{ + [this](const parser::OmpObjectList &objectList) { + for (const auto &ompObject : objectList.v) { + common::visit( + common::visitors{ + [&](const parser::Designator &designator) { + if (const auto *name{ + semantics::getDesignatorNameIfDataRef( + designator)}) { + specPartState_.declareTargetNames.insert(name->source); + } + }, + [&](const parser::Name &name) { + specPartState_.declareTargetNames.insert(name.source); + }, + }, + ompObject.u); + } + }}; + + if (const auto *objectList{parser::Unwrap(spec.u)}) { + populateDeclareTargetNames(*objectList); + } else if (const auto *clauseList{ + parser::Unwrap(spec.u)}) { + for (const auto &clause : clauseList->v) { + if (const auto *toClause{ + std::get_if(&clause.u)}) { + populateDeclareTargetNames( + std::get(toClause->v.t)); + } else if (const auto *linkClause{ + std::get_if(&clause.u)}) { + populateDeclareTargetNames(linkClause->v); + } else if (const auto *enterClause{ + std::get_if(&clause.u)}) { + populateDeclareTargetNames(enterClause->v); + } + } + } + SkipImplicitTyping(true); return true; } @@ -8126,7 +8169,12 @@ const parser::Name *DeclarationVisitor::ResolveDataRef( // If implicit types are allowed, ensure name is in the symbol table. // Otherwise, report an error if it hasn't been declared. const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) { - FindSymbol(name); + if (!FindSymbol(name)) { + if (FindAndMarkDeclareTargetSymbol(name)) { + return &name; + } + } + if (CheckForHostAssociatedImplicit(name)) { NotePossibleBadForwardRef(name); return &name; @@ -8313,6 +8361,48 @@ const parser::Name *DeclarationVisitor::FindComponent( return nullptr; } +bool DeclarationVisitor::FindAndMarkDeclareTargetSymbol( + const parser::Name &name) { + if (!specPartState_.declareTargetNames.empty()) { + if (specPartState_.declareTargetNames.count(name.source)) { + if (!currScope().IsTopLevel()) { + // Search preceding scopes until we find a matching symbol or run out + // of scopes to search, we skip the current scope as it's already been + // designated as implicit here. + Symbol *symbol = nullptr; + for (auto *scope = &currScope().parent();; scope = &scope->parent()) { + if (Symbol * symbol{scope->FindSymbol(name.source)}) { + if (symbol->test(Symbol::Flag::Subroutine) || + symbol->test(Symbol::Flag::Function)) { + const auto [sym, success]{currScope().try_emplace( + symbol->name(), Attrs{}, HostAssocDetails{*symbol})}; + assert(success && + "FindAndMarkDeclareTargetSymbol could not emplace new " + "subroutine/function symbol"); + name.symbol = &*sym->second; + symbol->test(Symbol::Flag::Subroutine) + ? name.symbol->set(Symbol::Flag::Subroutine) + : name.symbol->set(Symbol::Flag::Function); + return true; + } + // if we find a symbol that is not a function or subroutine, we + // currently escape without doing anything. + break; + } + + // This is our loop exit condition, as parent() has an inbuilt assert + // if you call it on a top level scope, rather than returning a null + // value. + if (scope->IsTopLevel()) { + return false; + } + } + } + } + } + return false; +} + void DeclarationVisitor::Initialization(const parser::Name &name, const parser::Initialization &init, bool inComponentDecl) { // Traversal of the initializer was deferred to here so that the diff --git a/flang/test/Semantics/OpenMP/declare-target08.f90 b/flang/test/Semantics/OpenMP/declare-target08.f90 new file mode 100644 index 0000000000000..1438d79d37348 --- /dev/null +++ b/flang/test/Semantics/OpenMP/declare-target08.f90 @@ -0,0 +1,41 @@ +! RUN: %flang_fc1 -fopenmp -fdebug-dump-symbols %s | FileCheck %s + +subroutine bar(i, a) + !$omp declare target + real :: a + integer :: i + a = a - i +end subroutine + +function baz(a) + !$omp declare target + real, intent(in) :: a + baz = a +end function baz + +program main +real a +!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc +!CHECK: baz (Function, OmpDeclareTarget): HostAssoc +!$omp declare target(bar) +!$omp declare target(baz) + +a = baz(a) +call bar(2,a) +call foo(a) +return +end + +subroutine foo(a) +real a +integer i +!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc +!CHECK: baz (Function, OmpDeclareTarget): HostAssoc +!$omp declare target(bar) +!$omp declare target(baz) +!$omp target + a = baz(a) + call bar(i,a) +!$omp end target +return +end