Skip to content

Commit 418920b

Browse files
authored
[flang][OpenMP] Diagnose non-variable symbols in OpenMP clauses (#111394)
The original motivation came from this scenario: ``` !$omp parallel do shared(xyz) xyz: do i = 1, 100 enddo xyz !$omp end parallel do ``` Implement a general check for validity of items listed in OpenMP clauses. In most cases they need to be variables, some clauses allow "extended list items", i.e. variables or procedures.
1 parent d17ad77 commit 418920b

File tree

3 files changed

+108
-3
lines changed

3 files changed

+108
-3
lines changed

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ bool OmpStructureChecker::CheckAllowedClause(llvmOmpClause clause) {
200200
return CheckAllowed(clause);
201201
}
202202

203+
bool OmpStructureChecker::IsExtendedListItem(const Symbol &sym) {
204+
return evaluate::IsVariable(sym) || sym.IsSubprogram();
205+
}
206+
203207
bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) {
204208
// Definition of close nesting:
205209
//
@@ -454,6 +458,14 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
454458
}
455459
}
456460

461+
void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
462+
for (const auto &[sym, source] : deferredNonVariables_) {
463+
context_.SayWithDecl(
464+
*sym, source, "'%s' must be a variable"_err_en_US, sym->name());
465+
}
466+
deferredNonVariables_.clear();
467+
}
468+
457469
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
458470
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
459471
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
@@ -1246,7 +1258,8 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetWithClause &x) {
12461258
context_.Say(x.source,
12471259
"If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US);
12481260
}
1249-
if (toClause) {
1261+
unsigned version{context_.langOptions().OpenMPVersion};
1262+
if (toClause && version >= 52) {
12501263
context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source,
12511264
"The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US);
12521265
}
@@ -2318,6 +2331,31 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
23182331

23192332
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
23202333
SetContextClause(x);
2334+
2335+
llvm::omp::Clause clauseId = std::visit(
2336+
[this](auto &&s) { return GetClauseKindForParserClass(s); }, x.u);
2337+
2338+
// The visitors for these clauses do their own checks.
2339+
switch (clauseId) {
2340+
case llvm::omp::Clause::OMPC_copyprivate:
2341+
case llvm::omp::Clause::OMPC_enter:
2342+
case llvm::omp::Clause::OMPC_lastprivate:
2343+
case llvm::omp::Clause::OMPC_reduction:
2344+
case llvm::omp::Clause::OMPC_to:
2345+
return;
2346+
default:
2347+
break;
2348+
}
2349+
2350+
if (const parser::OmpObjectList * objList{GetOmpObjectList(x)}) {
2351+
SymbolSourceMap symbols;
2352+
GetSymbolsInObjectList(*objList, symbols);
2353+
for (const auto &[sym, source] : symbols) {
2354+
if (!evaluate::IsVariable(sym)) {
2355+
deferredNonVariables_.insert({sym, source});
2356+
}
2357+
}
2358+
}
23212359
}
23222360

23232361
// Following clauses do not have a separate node in parse-tree.h.
@@ -2365,7 +2403,6 @@ CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
23652403
CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
23662404
CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes)
23672405
CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction)
2368-
CHECK_SIMPLE_CLAUSE(To, OMPC_to)
23692406
CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
23702407
CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
23712408
CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
@@ -2391,7 +2428,6 @@ CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type)
23912428
CHECK_SIMPLE_CLAUSE(Doacross, OMPC_doacross)
23922429
CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute)
23932430
CHECK_SIMPLE_CLAUSE(OmpxBare, OMPC_ompx_bare)
2394-
CHECK_SIMPLE_CLAUSE(Enter, OMPC_enter)
23952431
CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail)
23962432
CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak)
23972433

@@ -3357,6 +3393,47 @@ void OmpStructureChecker::Enter(const parser::OmpClause::HasDeviceAddr &x) {
33573393
}
33583394
}
33593395

3396+
void OmpStructureChecker::Enter(const parser::OmpClause::Enter &x) {
3397+
CheckAllowedClause(llvm::omp::Clause::OMPC_enter);
3398+
const parser::OmpObjectList &objList{x.v};
3399+
SymbolSourceMap symbols;
3400+
GetSymbolsInObjectList(objList, symbols);
3401+
for (const auto &[sym, source] : symbols) {
3402+
if (!IsExtendedListItem(*sym)) {
3403+
context_.SayWithDecl(*sym, source,
3404+
"'%s' must be a variable or a procedure"_err_en_US, sym->name());
3405+
}
3406+
}
3407+
}
3408+
3409+
void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
3410+
CheckAllowedClause(llvm::omp::Clause::OMPC_to);
3411+
if (dirContext_.empty()) {
3412+
return;
3413+
}
3414+
// The "to" clause is only allowed on "declare target" (pre-5.1), and
3415+
// "target update". In the former case it can take an extended list item,
3416+
// in the latter a variable (a locator).
3417+
3418+
// The "declare target" construct (and the "to" clause on it) are already
3419+
// handled (in the declare-target checkers), so just look at "to" in "target
3420+
// update".
3421+
if (GetContext().directive == llvm::omp::OMPD_declare_target) {
3422+
return;
3423+
}
3424+
assert(GetContext().directive == llvm::omp::OMPD_target_update);
3425+
3426+
const parser::OmpObjectList &objList{x.v};
3427+
SymbolSourceMap symbols;
3428+
GetSymbolsInObjectList(objList, symbols);
3429+
for (const auto &[sym, source] : symbols) {
3430+
if (!evaluate::IsVariable(*sym)) {
3431+
context_.SayWithDecl(
3432+
*sym, source, "'%s' must be a variable"_err_en_US, sym->name());
3433+
}
3434+
}
3435+
}
3436+
33603437
llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
33613438
return llvm::omp::getOpenMPClauseName(clause);
33623439
}

flang/lib/Semantics/check-omp-structure.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class OmpStructureChecker
6969
using llvmOmpClause = const llvm::omp::Clause;
7070

7171
void Enter(const parser::OpenMPConstruct &);
72+
void Leave(const parser::OpenMPConstruct &);
7273
void Enter(const parser::OpenMPLoopConstruct &);
7374
void Leave(const parser::OpenMPLoopConstruct &);
7475
void Enter(const parser::OmpEndLoopDirective &);
@@ -140,6 +141,7 @@ class OmpStructureChecker
140141

141142
private:
142143
bool CheckAllowedClause(llvmOmpClause clause);
144+
bool IsExtendedListItem(const Symbol &sym);
143145
void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
144146
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
145147
const std::string &clauseName);
@@ -246,6 +248,8 @@ class OmpStructureChecker
246248
LastType
247249
};
248250
int directiveNest_[LastType + 1] = {0};
251+
252+
SymbolSourceMap deferredNonVariables_;
249253
};
250254
} // namespace Fortran::semantics
251255
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
!RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
2+
module m
3+
4+
contains
5+
6+
subroutine foo1()
7+
integer :: baz1
8+
!ERROR: 'baz1' must be a variable
9+
!$omp parallel do shared(baz1)
10+
baz1: do i = 1, 100
11+
enddo baz1
12+
!$omp end parallel do
13+
end subroutine
14+
15+
subroutine foo2()
16+
!implicit baz2
17+
!ERROR: 'baz2' must be a variable
18+
!$omp parallel do shared(baz2)
19+
baz2: do i = 1, 100
20+
enddo baz2
21+
!$omp end parallel do
22+
end subroutine
23+
24+
end module m

0 commit comments

Comments
 (0)