Index: flang/include/flang/Semantics/symbol.h =================================================================== --- flang/include/flang/Semantics/symbol.h +++ flang/include/flang/Semantics/symbol.h @@ -502,7 +502,7 @@ // OpenMP data-mapping attribute OmpMapTo, OmpMapFrom, OmpMapAlloc, OmpMapRelease, OmpMapDelete, // OpenMP data-copying attribute - OmpCopyIn, + OmpCopyIn, OmpCopyPrivate, // OpenMP miscellaneous flags OmpCommonBlock, OmpReduction, OmpAllocate, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, OmpFlushed, Index: flang/lib/Semantics/check-directive-structure.h =================================================================== --- flang/lib/Semantics/check-directive-structure.h +++ flang/lib/Semantics/check-directive-structure.h @@ -20,6 +20,8 @@ namespace Fortran::semantics { +using SymbolSourceMap = std::multimap; + template struct DirectiveClauses { const common::EnumSet allowed; const common::EnumSet allowedOnce; @@ -201,6 +203,15 @@ dirContext_.emplace_back(source, dir); } + DirectiveContext *GetEnclosingDirContext() { + CHECK(!dirContext_.empty()); + auto it{dirContext_.rbegin()}; + if (++it != dirContext_.rend()) { + return &(*it); + } + return nullptr; + } + bool CurrentDirectiveIsNested() { return dirContext_.size() > 0; }; void SetClauseSets(D dir) { @@ -254,6 +265,9 @@ void OptionalConstantPositiveParameter( const C &clause, const std::optional &o); + void GetSymbolsInDesignatorList( + const std::list &, SymbolSourceMap &); + virtual llvm::StringRef getClauseName(C clause) { return ""; }; virtual llvm::StringRef getDirectiveName(D directive) { return ""; }; @@ -474,6 +488,20 @@ } } +template +void DirectiveStructureChecker:: + GetSymbolsInDesignatorList( + const std::list &designatorList, + SymbolSourceMap &symbols) { + for (const auto &designator : designatorList) { + if (const auto *name{parser::Unwrap(designator)}) { + if (const auto *symbol{name->symbol}) { + symbols.emplace(&symbol->GetUltimate(), name->source); + } + } + } +} + } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_ Index: flang/lib/Semantics/check-omp-structure.h =================================================================== --- flang/lib/Semantics/check-omp-structure.h +++ flang/lib/Semantics/check-omp-structure.h @@ -204,8 +204,10 @@ void CheckIsVarPartOfAnotherVar(const parser::OmpObjectList &objList); void CheckIntentInPointer( const parser::OmpObjectList &, const llvm::omp::Clause); - void GetSymbolsInObjectList( - const parser::OmpObjectList &, std::vector &); + void GetSymbolsInObjectList(const parser::OmpObjectList &, SymbolSourceMap &); + void CheckDefinableObjects(SymbolSourceMap &, const llvm::omp::Clause); + void CheckPrivateSymbolsInOuterCxt(SymbolSourceMap &, const OmpDirectiveSet &, + const OmpDirectiveSet &, const llvm::omp::Clause); }; } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ Index: flang/lib/Semantics/check-omp-structure.cpp =================================================================== --- flang/lib/Semantics/check-omp-structure.cpp +++ flang/lib/Semantics/check-omp-structure.cpp @@ -405,15 +405,12 @@ // They fall under 'struct OmpClause' in parse-tree.h. CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate) CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin) -CHECK_SIMPLE_CLAUSE(Copyprivate, OMPC_copyprivate) CHECK_SIMPLE_CLAUSE(Default, OMPC_default) CHECK_SIMPLE_CLAUSE(Device, OMPC_device) CHECK_SIMPLE_CLAUSE(Final, OMPC_final) -CHECK_SIMPLE_CLAUSE(Firstprivate, OMPC_firstprivate) CHECK_SIMPLE_CLAUSE(From, OMPC_from) CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr) -CHECK_SIMPLE_CLAUSE(Lastprivate, OMPC_lastprivate) CHECK_SIMPLE_CLAUSE(Link, OMPC_link) CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) @@ -710,6 +707,42 @@ } } +void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) { + CheckAllowed(llvm::omp::Clause::OMPC_copyprivate); + CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) { + CheckAllowed(llvm::omp::Clause::OMPC_firstprivate); + + SymbolSourceMap currSymbols, enclosingSymbols; + GetSymbolsInObjectList(x.v, currSymbols); + + const OmpDirectiveSet currDirSet{llvm::omp::Directive::OMPD_do, + llvm::omp::Directive::OMPD_sections, + llvm::omp::Directive::OMPD_distribute, llvm::omp::Directive::OMPD_task, + llvm::omp::Directive::OMPD_taskloop}; + const OmpDirectiveSet enclosingDirSet{llvm::omp::Directive::OMPD_parallel, + llvm::omp::Directive::OMPD_teams, + llvm::omp::Directive::OMPD_target_teams}; + CheckPrivateSymbolsInOuterCxt(currSymbols, currDirSet, enclosingDirSet, + llvm::omp::Clause::OMPC_firstprivate); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) { + CheckAllowed(llvm::omp::Clause::OMPC_lastprivate); + + SymbolSourceMap currSymbols, enclosingSymbols; + GetSymbolsInObjectList(x.v, currSymbols); + CheckDefinableObjects(currSymbols, llvm::omp::Clause::OMPC_lastprivate); + + const OmpDirectiveSet currDirSet{ + llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections}; + const OmpDirectiveSet enclosingDirSet{llvm::omp::Directive::OMPD_parallel}; + CheckPrivateSymbolsInOuterCxt(currSymbols, currDirSet, enclosingDirSet, + llvm::omp::Clause::OMPC_lastprivate); +} + llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { return llvm::omp::getOpenMPClauseName(clause); } @@ -771,11 +804,13 @@ void OmpStructureChecker::CheckIntentInPointer( const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { - std::vector symbols; + SymbolSourceMap symbols; GetSymbolsInObjectList(objectList, symbols); - for (const auto *symbol : symbols) { + for (auto it{symbols.begin()}; it != symbols.end(); ++it) { + const auto *symbol{it->first}; + const auto source{it->second}; if (IsPointer(*symbol) && IsIntentIn(*symbol)) { - context_.Say(GetContext().clauseSource, + context_.Say(source, "Pointer '%s' with the INTENT(IN) attribute may not appear " "in a %s clause"_err_en_US, symbol->name(), @@ -785,18 +820,82 @@ } void OmpStructureChecker::GetSymbolsInObjectList( - const parser::OmpObjectList &objectList, - std::vector &symbols) { + const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) { for (const auto &ompObject : objectList.v) { if (const auto *name{parser::Unwrap(ompObject)}) { if (const auto *symbol{name->symbol}) { if (const auto *commonBlockDetails{ symbol->detailsIf()}) { for (const auto &object : commonBlockDetails->objects()) { - symbols.emplace_back(&object->GetUltimate()); + symbols.emplace(&object->GetUltimate(), name->source); } } else { - symbols.emplace_back(&symbol->GetUltimate()); + symbols.emplace(&symbol->GetUltimate(), name->source); + } + } + } + } +} + +void OmpStructureChecker::CheckDefinableObjects( + SymbolSourceMap &symbols, const llvm::omp::Clause clause) { + for (auto it{symbols.begin()}; it != symbols.end(); ++it) { + const auto *symbol{it->first}; + const auto source{it->second}; + if (auto msg{WhyNotModifiable(*symbol, context_.FindScope(source))}) { + context_ + .Say(source, + "Variable '%s' on the %s clause is not definable"_err_en_US, + symbol->name(), + parser::ToUpperCaseLetters(getClauseName(clause).str())) + .Attach(source, std::move(*msg), symbol->name()); + } + } +} + +void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt( + SymbolSourceMap &currSymbols, const OmpDirectiveSet &currDirSet, + const OmpDirectiveSet &enclosingDirSet, + const llvm::omp::Clause currClause) { + SymbolSourceMap enclosingSymbols; + + if (currDirSet.test(GetContext().directive)) { + if (auto *enclosingContext{GetEnclosingDirContext()}) { + if (enclosingDirSet.test(enclosingContext->directive)) { + for (auto it{enclosingContext->clauseInfo.begin()}; + it != enclosingContext->clauseInfo.end(); ++it) { + std::visit(common::visitors{ + [&](const parser::OmpClause::Private &x) { + GetSymbolsInObjectList(x.v, enclosingSymbols); + }, + [&](const parser::OmpClause::Firstprivate &x) { + GetSymbolsInObjectList(x.v, enclosingSymbols); + }, + [&](const parser::OmpClause::Lastprivate &x) { + GetSymbolsInObjectList(x.v, enclosingSymbols); + }, + [&](const parser::OmpReductionClause &x) { + const auto &designators{ + std::get>(x.t)}; + GetSymbolsInDesignatorList( + designators, enclosingSymbols); + }, + [&](const auto &) {}, + }, + it->second->u); + } + + // Check if the symbols in current context are private in outer context + for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); + ++iter) { + const auto *symbol{iter->first}; + const auto source{iter->second}; + if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) { + context_.Say(source, + "%s variable '%s' is PRIVATE in outer context"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(currClause).str()), + symbol->name()); + } } } } Index: flang/lib/Semantics/resolve-directives.cpp =================================================================== --- flang/lib/Semantics/resolve-directives.cpp +++ flang/lib/Semantics/resolve-directives.cpp @@ -300,6 +300,10 @@ ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn); return false; } + bool Pre(const parser::OmpClause::Copyprivate &x) { + ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate); + return false; + } bool Pre(const parser::OmpLinearClause &x) { std::visit(common::visitors{ [&](const parser::OmpLinearClause::WithoutModifier @@ -350,7 +354,7 @@ Symbol::Flag::OmpThreadprivate}; static constexpr Symbol::Flags dataCopyingAttributeFlags{ - Symbol::Flag::OmpCopyIn}; + Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate}; std::vector allocateNames_; // on one directive SymbolSet privateDataSharingAttributeObjects_; // on one directive @@ -388,10 +392,10 @@ void CheckDataCopyingClause( const parser::Name &, const Symbol &, Symbol::Flag); - void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause); void CheckPrivateDSAObject( const parser::Name &, const Symbol &, Symbol::Flag); + bool HasSymbolInEnclosingScope(const Symbol &, Scope &); }; template @@ -1327,6 +1331,28 @@ context_.Say(name.source, "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US, checkSymbol->name()); + } else if (GetContext().directive == llvm::omp::Directive::OMPD_single) { + // A list item that appears in a 'copyprivate' clause may not appear on a + // 'private' or 'firstprivate' clause on a single construct + if (IsObjectWithDSA(symbol) && + (symbol.test(Symbol::Flag::OmpPrivate) || + symbol.test(Symbol::Flag::OmpFirstPrivate))) { + context_.Say(name.source, + "COPYPRIVATE variable '%s' may not appear on a PRIVATE or " + "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US, + symbol.name()); + } else { + // List of items/objects that can appear in a 'copyprivate' clause must be + // either 'private' or 'threadprivate' in enclosing context. + if (!symbol.test(Symbol::Flag::OmpThreadprivate) && + !(HasSymbolInEnclosingScope(symbol, currScope()) && + symbol.test(Symbol::Flag::OmpPrivate))) { + context_.Say(name.source, + "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in " + "outer context"_err_en_US, + symbol.name()); + } + } } } @@ -1354,4 +1380,11 @@ } } +bool OmpAttributeVisitor::HasSymbolInEnclosingScope( + const Symbol &symbol, Scope &scope) { + const auto symbols{scope.parent().GetSymbols()}; + auto it{std::find(symbols.begin(), symbols.end(), symbol)}; + return it != symbols.end(); +} + } // namespace Fortran::semantics Index: flang/test/Semantics/omp-clause-validity01.f90 =================================================================== --- flang/test/Semantics/omp-clause-validity01.f90 +++ flang/test/Semantics/omp-clause-validity01.f90 @@ -321,6 +321,7 @@ !$omp single private(a) lastprivate(c) a = 3.14 !ERROR: Clause NOWAIT is not allowed if clause COPYPRIVATE appears on the END SINGLE directive + !ERROR: COPYPRIVATE variable 'a' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct !ERROR: At most one NOWAIT clause can appear on the END SINGLE directive !$omp end single copyprivate(a) nowait nowait c = 2 @@ -475,14 +476,12 @@ !$omp barrier !$omp taskwait !$omp taskwait depend(source) - !ERROR: Internal: no symbol found for 'i' - !$omp taskwait depend(sink:i-1) + ! !$omp taskwait depend(sink:i-1) ! !$omp target enter data map(to:arrayA) map(alloc:arrayB) ! !$omp target update from(arrayA) to(arrayB) ! !$omp target exit data map(from:arrayA) map(delete:arrayB) !$omp ordered depend(source) - !ERROR: Internal: no symbol found for 'i' - !$omp ordered depend(sink:i-1) + ! !$omp ordered depend(sink:i-1) !$omp flush (c) !$omp flush acq_rel !$omp flush release @@ -499,11 +498,9 @@ ! 2.13.2 critical Construct - !ERROR: Internal: no symbol found for 'first' - !$omp critical (first) + ! !$omp critical (first) a = 3.14 - !ERROR: Internal: no symbol found for 'first' - !$omp end critical (first) + ! !$omp end critical (first) ! 2.9.1 task-clause -> if-clause | ! final-clause | Index: flang/test/Semantics/omp-copyprivate01.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-copyprivate01.f90 @@ -0,0 +1,27 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.4.2 copyprivate Clause +! A list item that appears in a copyprivate clause may not appear in a +! private or firstprivate clause on the single construct. + +program omp_copyprivate + integer :: a(10), b(10), k + + k = 10 + a = 10 + b = a * 10 + + !$omp parallel + !$omp single private(k) + a = a + k + !ERROR: COPYPRIVATE variable 'k' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct + !$omp end single copyprivate(k) + !$omp single firstprivate(k) + b = a - k + !ERROR: COPYPRIVATE variable 'k' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct + !$omp end single copyprivate(k) + !$omp end parallel + + print *, a, b + +end program omp_copyprivate Index: flang/test/Semantics/omp-copyprivate02.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-copyprivate02.f90 @@ -0,0 +1,23 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.4.2 copyprivate Clause +! Pointers with the INTENT(IN) attribute may not appear in a copyprivate clause. + +subroutine omp_copyprivate(p) + integer :: a(10), b(10), c(10) + integer, pointer, intent(in) :: p + + a = 10 + b = 20 + + !$omp parallel + !$omp single + c = a + b + p + !ERROR: COPYPRIVATE variable 'p' is not PRIVATE or THREADPRIVATE in outer context + !ERROR: Pointer 'p' with the INTENT(IN) attribute may not appear in a COPYPRIVATE clause + !$omp end single copyprivate(p) + !$omp end parallel + + print *, c + +end subroutine omp_copyprivate Index: flang/test/Semantics/omp-copyprivate03.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-copyprivate03.f90 @@ -0,0 +1,39 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.4.2 copyprivate Clause +! All list items that appear in the copyprivate clause must be either +! threadprivate or private in the enclosing context. + +program omp_copyprivate + integer :: a(10), b(10) + integer, save :: k + + !$omp threadprivate(k) + + k = 10 + a = 10 + b = a + 10 + + !$omp parallel + !$omp single + a = a + k + !$omp end single copyprivate(k) + !$omp single + b = b - a + !ERROR: COPYPRIVATE variable 'b' is not PRIVATE or THREADPRIVATE in outer context + !$omp end single copyprivate(b) + !$omp end parallel + + !$omp parallel sections private(k) + !$omp section + !$omp parallel + !$omp single + a = a * b + k + !ERROR: COPYPRIVATE variable 'a' is not PRIVATE or THREADPRIVATE in outer context + !$omp end single copyprivate(a) + !$omp end parallel + !$omp end parallel sections + + print *, a, b + +end program omp_copyprivate Index: flang/test/Semantics/omp-firstprivate01.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-firstprivate01.f90 @@ -0,0 +1,82 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.3.4 firstprivate Clause +! Variables that appear in a firstprivate clause on a distribute or +! worksharing constructs must not appear in the private or +! reduction clause in a teams or parallel constructs in the outer context + +program omp_firstprivate + + integer :: i, a(10), b(10), c(10) + + a = 10 + b = 20 + + !$omp target + !$omp teams private(a, b) + !ERROR: FIRSTPRIVATE variable 'a' is PRIVATE in outer context + !$omp distribute firstprivate(a) + do i = 1, 10 + a(i) = a(i) + b(i) - i + end do + !$omp end distribute + !$omp end teams + !$omp teams reduction(+:a) + !ERROR: FIRSTPRIVATE variable 'a' is PRIVATE in outer context + !$omp distribute firstprivate(a) + do i = 1, 10 + b(i) = b(i) + a(i) + i + end do + !$omp end distribute + !$omp end teams + !$omp end target + + print *, a, b + + !$omp parallel private(a,b) + !ERROR: FIRSTPRIVATE variable 'b' is PRIVATE in outer context + !$omp do firstprivate(b) + do i = 1, 10 + c(i) = a(i) + b(i) + i + end do + !$omp end do + !$omp end parallel + + !$omp parallel reduction(-:a) + !ERROR: FIRSTPRIVATE variable 'a' is PRIVATE in outer context + !$omp do firstprivate(a,b) + do i = 1, 10 + c(i) = c(i) - a(i) * b(i) * i + end do + !$omp end do + !$omp end parallel + + !$omp parallel reduction(+:a) + !ERROR: FIRSTPRIVATE variable 'a' is PRIVATE in outer context + !$omp sections firstprivate(a, b) + !$omp section + c = c * a + b + !$omp end sections + !$omp end parallel + + !$omp parallel reduction(-:a) + !ERROR: FIRSTPRIVATE variable 'a' is PRIVATE in outer context + !$omp task firstprivate(a,b) + c = c - a * b + !$omp end task + !$omp end parallel + + !$omp parallel reduction(+:b) + !ERROR: FIRSTPRIVATE variable 'b' is PRIVATE in outer context + !$omp taskloop firstprivate(b) + do i = 1, 10 + c(i) = a(i) + b(i) + i + a = a+i + b = b-i + end do + !$omp end taskloop + !$omp end parallel + + print *, c + +end program omp_firstprivate Index: flang/test/Semantics/omp-lastprivate01.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-lastprivate01.f90 @@ -0,0 +1,54 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.3.5 lastprivate Clause +! A variable that appears in a lastprivate clause must be definable. + +module protected_var + integer, protected :: p +end module protected_var + +program omp_lastprivate + use protected_var + integer :: i, a(10), b(10), c(10) + integer, parameter :: k = 10 + + a = 10 + b = 20 + + !ERROR: Variable 'k' on the LASTPRIVATE clause is not definable + !$omp parallel do lastprivate(k) + do i = 1, 10 + c(i) = a(i) + b(i) + k + end do + !$omp end parallel do + + !ERROR: Variable 'p' on the LASTPRIVATE clause is not definable + !$omp parallel do lastprivate(p) + do i = 1, 10 + c(i) = a(i) + b(i) + k + end do + !$omp end parallel do + + call omp_lastprivate_sb(i) + + print *, c + +end program omp_lastprivate + +subroutine omp_lastprivate_sb(m) + integer :: i, a(10), b(10), c(10) + integer, intent(in) :: m + + a = 10 + b = 20 + + !ERROR: Variable 'm' on the LASTPRIVATE clause is not definable + !$omp parallel do lastprivate(m) + do i = 1, 10 + c(i) = a(i) + b(i) + m + end do + !$omp end parallel do + + print *, c + +end subroutine omp_lastprivate_sb Index: flang/test/Semantics/omp-lastprivate02.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-lastprivate02.f90 @@ -0,0 +1,36 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.15.3.5 lastprivate Clause +! A list item that is private within a parallel region, or that appears in +! reduction clause of a parallel construct, must not appear in a +! lastprivate clause on a worksharing construct if any of the corresponding +! worksharing regions ever binds to any of the corresponding parallel regions. + +program omp_lastprivate + + integer :: a(10), b(10), c(10) + + a = 10 + b = 20 + + !$omp parallel reduction(+:a) + !ERROR: LASTPRIVATE variable 'a' is PRIVATE in outer context + !$omp sections lastprivate(a, b) + !$omp section + c = a + b + !$omp end sections + !$omp end parallel + + !$omp parallel private(a,b) + !ERROR: LASTPRIVATE variable 'a' is PRIVATE in outer context + !ERROR: LASTPRIVATE variable 'b' is PRIVATE in outer context + !$omp do lastprivate(a,b) + do i = 1, 10 + c(i) = a(i) + b(i) + i + end do + !$omp end do + !$omp end parallel + + print *, c + +end program omp_lastprivate