aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics')
-rw-r--r--flang/lib/Semantics/check-cuda.cpp3
-rw-r--r--flang/lib/Semantics/check-omp-structure.cpp302
-rw-r--r--flang/lib/Semantics/check-omp-structure.h41
-rw-r--r--flang/lib/Semantics/openmp-utils.cpp2
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp41
-rw-r--r--flang/lib/Semantics/resolve-names.cpp14
-rw-r--r--flang/lib/Semantics/scope.cpp52
7 files changed, 354 insertions, 101 deletions
diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp
index 3d2db6a..caa9bdd 100644
--- a/flang/lib/Semantics/check-cuda.cpp
+++ b/flang/lib/Semantics/check-cuda.cpp
@@ -131,6 +131,9 @@ struct FindHostArray
return (*this)(x.base());
}
Result operator()(const Symbol &symbol) const {
+ if (symbol.IsFuncResult()) {
+ return nullptr;
+ }
if (const auto *details{
symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) {
if (details->IsArray() &&
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index be10669..4141630 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -61,6 +61,124 @@ namespace Fortran::semantics {
using namespace Fortran::semantics::omp;
using namespace Fortran::parser::omp;
+OmpStructureChecker::OmpStructureChecker(SemanticsContext &context)
+ : DirectiveStructureChecker(context,
+#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
+#include "llvm/Frontend/OpenMP/OMP.inc"
+ ) {
+ scopeStack_.push_back(&context.globalScope());
+}
+
+bool OmpStructureChecker::Enter(const parser::MainProgram &x) {
+ using StatementProgramStmt = parser::Statement<parser::ProgramStmt>;
+ if (auto &stmt{std::get<std::optional<StatementProgramStmt>>(x.t)}) {
+ scopeStack_.push_back(stmt->statement.v.symbol->scope());
+ } else {
+ for (const Scope &scope : context_.globalScope().children()) {
+ // There can only be one main program.
+ if (scope.kind() == Scope::Kind::MainProgram) {
+ scopeStack_.push_back(&scope);
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::MainProgram &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::BlockData &x) {
+ // The BLOCK DATA name is optional, so we need to look for the
+ // corresponding scope in the global scope.
+ auto &stmt{std::get<parser::Statement<parser::BlockDataStmt>>(x.t)};
+ if (auto &name{stmt.statement.v}) {
+ scopeStack_.push_back(name->symbol->scope());
+ } else {
+ for (const Scope &scope : context_.globalScope().children()) {
+ if (scope.kind() == Scope::Kind::BlockData) {
+ if (scope.symbol()->name().empty()) {
+ scopeStack_.push_back(&scope);
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::BlockData &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::Module &x) {
+ auto &stmt{std::get<parser::Statement<parser::ModuleStmt>>(x.t)};
+ const Symbol *sym{stmt.statement.v.symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::Module &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::Submodule &x) {
+ auto &stmt{std::get<parser::Statement<parser::SubmoduleStmt>>(x.t)};
+ const Symbol *sym{std::get<parser::Name>(stmt.statement.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::Submodule &x) {
+ scopeStack_.pop_back();
+}
+
+// Function/subroutine subprogram nodes don't appear in INTERFACEs, but
+// the subprogram/end statements do.
+bool OmpStructureChecker::Enter(const parser::SubroutineStmt &x) {
+ const Symbol *sym{std::get<parser::Name>(x.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::EndSubroutineStmt &x) {
+ scopeStack_.pop_back();
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::FunctionStmt &x) {
+ const Symbol *sym{std::get<parser::Name>(x.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::EndFunctionStmt &x) {
+ scopeStack_.pop_back();
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::BlockConstruct &x) {
+ auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
+ auto &execPart{std::get<parser::Block>(x.t)};
+ if (auto &&source{parser::GetSource(specPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ } else if (auto &&source{parser::GetSource(execPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {
+ auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
+ auto &execPart{std::get<parser::Block>(x.t)};
+ if (auto &&source{parser::GetSource(specPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ } else if (auto &&source{parser::GetSource(execPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ }
+}
+
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
#define CHECK_SIMPLE_CLAUSE(X, Y) \
void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
@@ -362,6 +480,36 @@ bool OmpStructureChecker::IsNestedInDirective(llvm::omp::Directive directive) {
return false;
}
+bool OmpStructureChecker::InTargetRegion() {
+ if (IsNestedInDirective(llvm::omp::Directive::OMPD_target)) {
+ // Return true even for device_type(host).
+ return true;
+ }
+ for (const Scope *scope : llvm::reverse(scopeStack_)) {
+ if (const auto *symbol{scope->symbol()}) {
+ if (symbol->test(Symbol::Flag::OmpDeclareTarget)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool OmpStructureChecker::HasRequires(llvm::omp::Clause req) {
+ const Scope &unit{GetProgramUnit(*scopeStack_.back())};
+ return common::visit(
+ [&](const auto &details) {
+ if constexpr (std::is_convertible_v<decltype(details),
+ const WithOmpDeclarative &>) {
+ if (auto *reqs{details.ompRequires()}) {
+ return reqs->test(req);
+ }
+ }
+ return false;
+ },
+ DEREF(unit.symbol()).details());
+}
+
void OmpStructureChecker::CheckVariableListItem(
const SymbolSourceMap &symbols) {
for (auto &[symbol, source] : symbols) {
@@ -1562,40 +1710,95 @@ void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
- isPredefinedAllocator = true;
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
- SymbolSourceMap currSymbols;
- GetSymbolsInObjectList(objectList, currSymbols);
- for (auto &[symbol, source] : currSymbols) {
- if (IsPointer(*symbol)) {
- context_.Say(source,
- "List item '%s' in ALLOCATE directive must not have POINTER "
- "attribute"_err_en_US,
- source.ToString());
+void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,
+ const parser::OmpObjectList &objects,
+ const parser::OmpClauseList &clauses) {
+ const Scope &thisScope{context_.FindScope(source)};
+ SymbolSourceMap symbols;
+ GetSymbolsInObjectList(objects, symbols);
+
+ auto maybeHasPredefinedAllocator{[&](const parser::OmpClause *calloc) {
+ // Return "true" if the ALLOCATOR clause was provided with an argument
+ // that is either a prefdefined allocator, or a run-time value.
+ // Otherwise return "false".
+ if (!calloc) {
+ return false;
}
- if (IsDummy(*symbol)) {
+ auto *allocator{std::get_if<parser::OmpClause::Allocator>(&calloc->u)};
+ if (auto val{ToInt64(GetEvaluateExpr(DEREF(allocator).v))}) {
+ // Predefined allocators (defined in OpenMP 6.0 20.8.1):
+ // omp_null_allocator = 0,
+ // omp_default_mem_alloc = 1,
+ // omp_large_cap_mem_alloc = 2,
+ // omp_const_mem_alloc = 3,
+ // omp_high_bw_mem_alloc = 4,
+ // omp_low_lat_mem_alloc = 5,
+ // omp_cgroup_mem_alloc = 6,
+ // omp_pteam_mem_alloc = 7,
+ // omp_thread_mem_alloc = 8
+ return *val >= 0 && *val <= 8;
+ }
+ return true;
+ }};
+
+ const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)};
+ if (InTargetRegion()) {
+ bool hasDynAllocators{
+ HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
+ if (!allocator && !hasDynAllocators) {
context_.Say(source,
- "List item '%s' in ALLOCATE directive must not be a dummy "
- "argument"_err_en_US,
- source.ToString());
+ "An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
+ }
+ }
+
+ auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
+
+ for (auto &[symbol, source] : symbols) {
+ if (!inExecutableAllocate_) {
+ if (symbol->owner() != thisScope) {
+ context_.Say(source,
+ "A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);
+ }
+ if (IsPointer(*symbol) || IsAllocatable(*symbol)) {
+ context_.Say(source,
+ "A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);
+ }
}
if (symbol->GetUltimate().has<AssocEntityDetails>()) {
context_.Say(source,
- "List item '%s' in ALLOCATE directive must not be an associate "
- "name"_err_en_US,
- source.ToString());
+ "A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);
+ }
+ if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) {
+ if (!allocator) {
+ context_.Say(source,
+ "If a list item is a named common block or has SAVE attribute, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US);
+ } else if (!maybePredefined) {
+ context_.Say(source,
+ "If a list item is a named common block or has SAVE attribute, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US);
+ }
+ }
+ if (FindCommonBlockContaining(*symbol)) {
+ context_.Say(source,
+ "A variable that is part of a common block may not be specified as a list item in an ALLOCATE directive, except implicitly via the named common block"_err_en_US);
}
}
- CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
+ CheckVarIsNotPartOfAnotherVar(source, objects);
}
-void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
+void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
- CheckPredefinedAllocatorRestriction(dir.source, objectList);
+ PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
+ if (!inExecutableAllocate_) {
+ const auto &dir{std::get<parser::Verbatim>(x.t)};
+ const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+ const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
+
+ isPredefinedAllocator = true;
+ CheckAllocateDirective(dir.source, objectList, clauseList);
+ }
dirContext_.pop_back();
}
@@ -1951,6 +2154,7 @@ void OmpStructureChecker::CheckNameInAllocateStmt(
}
void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
+ inExecutableAllocate_ = true;
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
@@ -1960,24 +2164,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
"The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
}
- bool hasAllocator = false;
- // TODO: Investigate whether searching the clause list can be done with
- // parser::Unwrap instead of the following loop
- const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
- for (const auto &clause : clauseList.v) {
- if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
- hasAllocator = true;
- }
- }
-
- if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
- // TODO: expand this check to exclude the case when a requires
- // directive with the dynamic_allocators clause is present
- // in the same compilation unit (OMP5.0 2.11.3).
- context_.Say(x.source,
- "ALLOCATE directives that appear in a TARGET region must specify an allocator clause"_err_en_US);
- }
-
const auto &allocateStmt =
std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
@@ -1994,18 +2180,34 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
}
isPredefinedAllocator = true;
- const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
- if (objectList) {
- CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
- }
}
void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
- if (objectList)
- CheckPredefinedAllocatorRestriction(dir.source, *objectList);
+ parser::OmpObjectList empty{std::list<parser::OmpObject>{}};
+ auto &objects{[&]() -> const parser::OmpObjectList & {
+ if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
+ return *objects;
+ } else {
+ return empty;
+ }
+ }()};
+ auto &clauses{std::get<parser::OmpClauseList>(x.t)};
+ CheckAllocateDirective(
+ std::get<parser::Verbatim>(x.t).source, objects, clauses);
+
+ if (const auto &subDirs{
+ std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
+ x.t)}) {
+ for (const auto &dalloc : *subDirs) {
+ const auto &dir{std::get<parser::Verbatim>(x.t)};
+ const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)};
+ const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)};
+ CheckAllocateDirective(dir.source, objects, clauses);
+ }
+ }
+
dirContext_.pop_back();
+ inExecutableAllocate_ = false;
}
void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index b3fd6c8..7426559 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -56,21 +56,32 @@ using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;
using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,
std::pair<llvm::omp::Directive, const OmpClauseSet>>;
-class OmpStructureChecker
- : public DirectiveStructureChecker<llvm::omp::Directive, llvm::omp::Clause,
- parser::OmpClause, llvm::omp::Clause_enumSize> {
+using OmpStructureCheckerBase = DirectiveStructureChecker<llvm::omp::Directive,
+ llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
+
+class OmpStructureChecker : public OmpStructureCheckerBase {
public:
- using Base = DirectiveStructureChecker<llvm::omp::Directive,
- llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
+ using Base = OmpStructureCheckerBase;
+
+ OmpStructureChecker(SemanticsContext &context);
- OmpStructureChecker(SemanticsContext &context)
- : DirectiveStructureChecker(context,
-#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
-#include "llvm/Frontend/OpenMP/OMP.inc"
- ) {
- }
using llvmOmpClause = const llvm::omp::Clause;
+ bool Enter(const parser::MainProgram &);
+ void Leave(const parser::MainProgram &);
+ bool Enter(const parser::BlockData &);
+ void Leave(const parser::BlockData &);
+ bool Enter(const parser::Module &);
+ void Leave(const parser::Module &);
+ bool Enter(const parser::Submodule &);
+ void Leave(const parser::Submodule &);
+ bool Enter(const parser::SubroutineStmt &);
+ bool Enter(const parser::EndSubroutineStmt &);
+ bool Enter(const parser::FunctionStmt &);
+ bool Enter(const parser::EndFunctionStmt &);
+ bool Enter(const parser::BlockConstruct &);
+ void Leave(const parser::BlockConstruct &);
+
void Enter(const parser::OpenMPConstruct &);
void Leave(const parser::OpenMPConstruct &);
void Enter(const parser::OpenMPInteropConstruct &);
@@ -177,10 +188,12 @@ private:
const parser::CharBlock &, const OmpDirectiveSet &);
bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
bool IsNestedInDirective(llvm::omp::Directive directive);
+ bool InTargetRegion();
void HasInvalidTeamsNesting(
const llvm::omp::Directive &dir, const parser::CharBlock &source);
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
+ bool HasRequires(llvm::omp::Clause req);
// specific clause related
void CheckAllowedMapTypes(
parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);
@@ -250,6 +263,9 @@ private:
bool CheckTargetBlockOnlyTeams(const parser::Block &);
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
+ void CheckAllocateDirective(parser::CharBlock source,
+ const parser::OmpObjectList &objects,
+ const parser::OmpClauseList &clauses);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
@@ -367,12 +383,15 @@ private:
};
int directiveNest_[LastType + 1] = {0};
+ bool inExecutableAllocate_{false};
parser::CharBlock visitedAtomicSource_;
SymbolSourceMap deferredNonVariables_;
using LoopConstruct = std::variant<const parser::DoConstruct *,
const parser::OpenMPLoopConstruct *>;
std::vector<LoopConstruct> loopStack_;
+ // Scopes for scoping units.
+ std::vector<const Scope *> scopeStack_;
};
/// Find a duplicate entry in the range, and return an iterator to it.
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 292e73b..cc55bb4 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -218,7 +218,7 @@ bool IsMapExitingType(parser::OmpMapType::Value type) {
}
}
-std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr) {
+MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {
const parser::TypedExpr &typedExpr{parserExpr.typedExpr};
// ForwardOwningPointer typedExpr
// `- GenericExprWrapper ^.get()
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index b0c36ec..196755e 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1723,17 +1723,12 @@ void AccAttributeVisitor::Post(const parser::Name &name) {
Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
const parser::Name *name) {
- if (auto *prev{name
- ? GetContext().scope.parent().FindCommonBlock(name->source)
- : nullptr}) {
- name->symbol = prev;
- return prev;
- }
- // Check if the Common Block is declared in the current scope
- if (auto *commonBlockSymbol{
- name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
- name->symbol = commonBlockSymbol;
- return commonBlockSymbol;
+ if (name) {
+ if (Symbol *
+ cb{GetContext().scope.FindCommonBlockInVisibleScopes(name->source)}) {
+ name->symbol = cb;
+ return cb;
+ }
}
return nullptr;
}
@@ -1783,8 +1778,8 @@ void AccAttributeVisitor::ResolveAccObject(
}
} else {
context_.Say(name.source,
- "COMMON block must be declared in the same scoping unit "
- "in which the OpenACC directive or clause appears"_err_en_US);
+ "Could not find COMMON block '%s' used in OpenACC directive"_err_en_US,
+ name.ToString());
}
},
},
@@ -3099,26 +3094,6 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
AddAllocateName(name);
}
}
- if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
- IsAllocatable(*symbol) &&
- !IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) {
- context_.Say(designator.source,
- "List items specified in the ALLOCATE directive must not have the ALLOCATABLE attribute unless the directive is associated with an ALLOCATE statement"_err_en_US);
- }
- bool checkScope{ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective};
- // In 5.1 the scope check only applies to declarative allocate.
- if (version == 50 && !checkScope) {
- checkScope = ompFlag == Symbol::Flag::OmpExecutableAllocateDirective;
- }
- if (checkScope) {
- if (omp::GetScopingUnit(GetContext().scope) !=
- omp::GetScopingUnit(symbol->GetUltimate().owner())) {
- context_.Say(designator.source, // 2.15.3
- "List items must be declared in the same scoping unit in which the %s directive appears"_err_en_US,
- parser::ToUpperCaseLetters(
- llvm::omp::getOpenMPDirectiveName(directive, version)));
- }
- }
if (ompFlag == Symbol::Flag::OmpReduction) {
// Using variables inside of a namelist in OpenMP reductions
// is allowed by the standard, but is not allowed for
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 88cc446..db75437 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3645,6 +3645,20 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
}
}
}
+ // Go through the list of COMMON block symbols in the module scope and add
+ // their USE association to the current scope's USE-associated COMMON blocks.
+ for (const auto &[name, symbol] : useModuleScope_->commonBlocks()) {
+ if (!currScope().FindCommonBlockInVisibleScopes(name)) {
+ currScope().AddCommonBlockUse(
+ name, symbol->attrs(), symbol->GetUltimate());
+ }
+ }
+ // Go through the list of USE-associated COMMON block symbols in the module
+ // scope and add USE associations to their ultimate symbols to the current
+ // scope's USE-associated COMMON blocks.
+ for (const auto &[name, symbol] : useModuleScope_->commonBlockUses()) {
+ currScope().AddCommonBlockUse(name, symbol->attrs(), symbol->GetUltimate());
+ }
useModuleScope_ = nullptr;
}
diff --git a/flang/lib/Semantics/scope.cpp b/flang/lib/Semantics/scope.cpp
index 4af371f..ab75d4c 100644
--- a/flang/lib/Semantics/scope.cpp
+++ b/flang/lib/Semantics/scope.cpp
@@ -144,9 +144,8 @@ void Scope::add_crayPointer(const SourceName &name, Symbol &pointer) {
}
Symbol &Scope::MakeCommonBlock(SourceName name, SourceName location) {
- const auto it{commonBlocks_.find(name)};
- if (it != commonBlocks_.end()) {
- return *it->second;
+ if (auto *cb{FindCommonBlock(name)}) {
+ return *cb;
} else {
Symbol &symbol{MakeSymbol(
name, Attrs{}, CommonBlockDetails{name.empty() ? location : name})};
@@ -154,9 +153,25 @@ Symbol &Scope::MakeCommonBlock(SourceName name, SourceName location) {
return symbol;
}
}
-Symbol *Scope::FindCommonBlock(const SourceName &name) const {
- const auto it{commonBlocks_.find(name)};
- return it != commonBlocks_.end() ? &*it->second : nullptr;
+
+Symbol *Scope::FindCommonBlockInVisibleScopes(const SourceName &name) const {
+ if (Symbol * cb{FindCommonBlock(name)}) {
+ return cb;
+ } else if (Symbol * cb{FindCommonBlockUse(name)}) {
+ return &cb->GetUltimate();
+ } else if (IsSubmodule()) {
+ if (const Scope *parent{
+ symbol_ ? symbol_->get<ModuleDetails>().parent() : nullptr}) {
+ if (auto *cb{parent->FindCommonBlockInVisibleScopes(name)}) {
+ return cb;
+ }
+ }
+ } else if (!IsTopLevel() && parent_) {
+ if (auto *cb{parent_->FindCommonBlockInVisibleScopes(name)}) {
+ return cb;
+ }
+ }
+ return nullptr;
}
Scope *Scope::FindSubmodule(const SourceName &name) const {
@@ -167,6 +182,31 @@ Scope *Scope::FindSubmodule(const SourceName &name) const {
return &*it->second;
}
}
+
+bool Scope::AddCommonBlockUse(
+ const SourceName &name, Attrs attrs, Symbol &cbUltimate) {
+ CHECK(cbUltimate.has<CommonBlockDetails>());
+ // Make a symbol, but don't add it to the Scope, since it needs to
+ // be added to the USE-associated COMMON blocks
+ Symbol &useCB{MakeSymbol(name, attrs, UseDetails{name, cbUltimate})};
+ return commonBlockUses_.emplace(name, useCB).second;
+}
+
+Symbol *Scope::FindCommonBlock(const SourceName &name) const {
+ if (const auto it{commonBlocks_.find(name)}; it != commonBlocks_.end()) {
+ return &*it->second;
+ }
+ return nullptr;
+}
+
+Symbol *Scope::FindCommonBlockUse(const SourceName &name) const {
+ if (const auto it{commonBlockUses_.find(name)};
+ it != commonBlockUses_.end()) {
+ return &*it->second;
+ }
+ return nullptr;
+}
+
bool Scope::AddSubmodule(const SourceName &name, Scope &submodule) {
return submodules_.emplace(name, submodule).second;
}