diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -231,10 +231,13 @@ void CheckCycleConstraints(const parser::OpenMPLoopConstruct &x); template bool IsOperatorValid(const T &, const D &); void CheckAtomicMemoryOrderClause( - const parser::OmpAtomicClauseList &, const parser::OmpAtomicClauseList &); - void CheckAtomicMemoryOrderClause(const parser::OmpAtomicClauseList &); - void CheckAtomicUpdateAssignmentStmt(const parser::AssignmentStmt &); - void CheckAtomicConstructStructure(const parser::OpenMPAtomicConstruct &); + const parser::OmpAtomicClauseList *, const parser::OmpAtomicClauseList *); + void CheckAtomicHintClause( + const parser::OmpAtomicClauseList *, const parser::OmpAtomicClauseList *); + bool CheckAtomicCaptureStmt(const parser::AssignmentStmt &, bool); + bool CheckAtomicUpdateStmt(const parser::AssignmentStmt &, bool); + bool CheckAtomicWriteStmt(const parser::AssignmentStmt &, bool); + void CheckAtomicCaptureConstruct(const parser::OmpAtomicCapture &); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); void CheckTargetNest(const parser::OpenMPConstruct &x); @@ -262,6 +265,8 @@ const parser::OmpObjectList &ompObjectList); void CheckPredefinedAllocatorRestriction( const parser::CharBlock &source, const parser::Name &name); + bool CheckForAllocatableVariable( + const semantics::Symbol &, const parser::Variable &var, bool); bool isPredefinedAllocator{false}; void EnterDirectiveNest(const int index) { directiveNest_[index]++; } void ExitDirectiveNest(const int index) { directiveNest_[index]--; } diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1410,6 +1410,27 @@ } } +bool OmpStructureChecker::CheckForAllocatableVariable( + const semantics::Symbol &symbol, const parser::Variable &var, + bool shouldSuppressOutput) { + if (IsAllocatable(symbol)) { + const auto &designator = + std::get>(var.u); + if (const auto *dataRef = + std::get_if(&designator.value().u)) { + if (const auto name = std::get_if(&dataRef->u)) { + if (!shouldSuppressOutput) + context_.Say(name->source, + "%s must not have ALLOCATABLE " + "attribute"_err_en_US, + name->ToString()); + return true; + } + } + } + return false; +} + template bool OmpStructureChecker::IsOperatorValid(const T &node, const D &variable) { using AllowedBinaryOperators = @@ -1430,19 +1451,66 @@ if ((exprLeft.value().source.ToString() != variableName) && (exprRight.value().source.ToString() != variableName)) { context_.Say(variable.GetSource(), - "Atomic update variable '%s' not found in the RHS of the " - "assignment statement in an ATOMIC (UPDATE) construct"_err_en_US, - variableName); + "Atomic update statement should " + "be of form `%s = %s operator expr` OR `%s = expr operator %s`"_err_en_US, + variableName, variableName, variableName, variableName); } return common::HasMember; } return true; } -void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt( - const parser::AssignmentStmt &assignment) { +void OmpStructureChecker::CheckAtomicCaptureConstruct( + const parser::OmpAtomicCapture &atomicCaptureConstruct) { + const parser::AssignmentStmt &stmt1 = + std::get<3>(atomicCaptureConstruct.t).v.statement; + const parser::AssignmentStmt &stmt2 = + std::get<4>(atomicCaptureConstruct.t).v.statement; + + if (CheckAtomicCaptureStmt(stmt1, true)) { + // atomic-write statement is a proper superset of atomic-update. It is + // sufficient to thus check for it and report semantic errors (if any) in + // stmt2 from [capture-stmt, write-stmt] or [capture-stmt, update-stmt] + CheckAtomicWriteStmt(stmt2, false); + } else if (CheckAtomicWriteStmt(stmt2, true) || + CheckAtomicUpdateStmt(stmt2, true)) { + // Report semantic errors (if any) in stmt1 in [capture-stmt, write-stmt] or + // [capture-stmt, update-stmt] + CheckAtomicCaptureStmt(stmt1, false); + } else if (CheckAtomicCaptureStmt(stmt2, true)) { + // Report semantic errors (if any) in stmt1 in [update-stmt, capture-stmt] + CheckAtomicUpdateStmt(stmt1, false); + } else if (CheckAtomicUpdateStmt(stmt1, true)) { + // Report semantic errors (if any) in stmt2 in [update-stmt, capture-stmt] + CheckAtomicCaptureStmt(stmt2, false); + } else { + context_.Say(atomicCaptureConstruct.source, + "Invalid ATOMIC (CAPTURE) " + "construct statements. Expect one of [update-stmt, " + "capture-stmt], [capture-stmt, update-stmt]" + "or [capture-stmt, write-stmt]."_err_en_US); + } +} + +bool OmpStructureChecker::CheckAtomicWriteStmt( + const parser::AssignmentStmt &assignment, bool shouldSuppressOutput) { + const auto &expr{std::get(assignment.t)}; + const auto &var{std::get(assignment.t)}; + bool isValidAtomicWriteStmt{true}; + if (const auto *e{GetExpr(context_, var)}) { + for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { + if (CheckForAllocatableVariable(symbol, var, shouldSuppressOutput)) + isValidAtomicWriteStmt = false; + } + } + return isValidAtomicWriteStmt; +} + +bool OmpStructureChecker::CheckAtomicUpdateStmt( + const parser::AssignmentStmt &assignment, bool shouldSuppressOutput) { const auto &expr{std::get(assignment.t)}; const auto &var{std::get(assignment.t)}; + bool isValidAtomicUpdateStmt{true}; common::visit( common::visitors{ [&](const common::Indirection &x) { @@ -1454,9 +1522,11 @@ !(name->source == "max" || name->source == "min" || name->source == "iand" || name->source == "ior" || name->source == "ieor")) { - context_.Say(expr.source, - "Invalid intrinsic procedure name in " - "OpenMP ATOMIC (UPDATE) statement"_err_en_US); + if (!shouldSuppressOutput) + context_.Say(expr.source, + "Invalid intrinsic procedure name in " + "OpenMP ATOMIC (UPDATE) statement"_err_en_US); + isValidAtomicUpdateStmt = false; } else if (name) { bool foundMatch{false}; if (auto varDesignatorIndirection = @@ -1481,67 +1551,163 @@ } } if (!foundMatch) { - context_.Say(expr.source, - "Atomic update variable '%s' not found in the " - "argument list of intrinsic procedure"_err_en_US, - var.GetSource().ToString()); + if (!shouldSuppressOutput) + context_.Say(expr.source, + "Atomic update variable '%s' not found in the " + "argument list of intrinsic procedure"_err_en_US, + var.GetSource().ToString()); + isValidAtomicUpdateStmt = false; } } }, + [&](const parser::Expr::DefinedUnary &) { + if (!shouldSuppressOutput) + context_.Say(expr.source, + "User defined operator not allowed in" + " OpenMP ATOMIC (UPDATE) statement"_err_en_US); + isValidAtomicUpdateStmt = false; + }, + [&](const parser::Expr::DefinedBinary &) { + if (!shouldSuppressOutput) + context_.Say(expr.source, + "User defined operator not allowed in" + " OpenMP ATOMIC (UPDATE) statement"_err_en_US); + isValidAtomicUpdateStmt = false; + }, [&](const auto &x) { if (!IsOperatorValid(x, var)) { - context_.Say(expr.source, - "Invalid operator in OpenMP ATOMIC (UPDATE) statement"_err_en_US); + if (!shouldSuppressOutput) + context_.Say(expr.source, + "Invalid operator in OpenMP ATOMIC (UPDATE) statement"_err_en_US); + isValidAtomicUpdateStmt = false; } }, }, + expr.u); + if (const auto *e{GetExpr(context_, var)}) { + for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { + if (CheckForAllocatableVariable(symbol, var, shouldSuppressOutput)) + isValidAtomicUpdateStmt = false; + } + } + return isValidAtomicUpdateStmt; } void OmpStructureChecker::CheckAtomicMemoryOrderClause( - const parser::OmpAtomicClauseList &clauseList) { + const parser::OmpAtomicClauseList *leftOmpAtomicClauseList, + const parser::OmpAtomicClauseList *rightOmpAtomicClauseList) { int numMemoryOrderClause = 0; - for (const auto &clause : clauseList.v) { - if (std::get_if(&clause.u)) { - numMemoryOrderClause++; - if (numMemoryOrderClause > 1) { - context_.Say(clause.source, - "More than one memory order clause not allowed on OpenMP " - "Atomic construct"_err_en_US); - return; + if (leftOmpAtomicClauseList) { + for (const auto &clause : leftOmpAtomicClauseList->v) { + if (std::get_if(&clause.u)) { + numMemoryOrderClause++; + if (numMemoryOrderClause > 1) { + context_.Say(clause.source, + "More than one memory order clause not allowed on " + "OpenMP Atomic construct"_err_en_US); + return; + } + } + } + } + if (rightOmpAtomicClauseList) { + for (const auto &clause : rightOmpAtomicClauseList->v) { + if (std::get_if(&clause.u)) { + numMemoryOrderClause++; + if (numMemoryOrderClause > 1) { + context_.Say(clause.source, + "More than one memory order clause not " + "allowed on OpenMP Atomic construct"_err_en_US); + return; + } } } } } -void OmpStructureChecker::CheckAtomicMemoryOrderClause( - const parser::OmpAtomicClauseList &leftHandClauseList, - const parser::OmpAtomicClauseList &rightHandClauseList) { - int numMemoryOrderClause = 0; - for (const auto &clause : leftHandClauseList.v) { - if (std::get_if(&clause.u)) { - numMemoryOrderClause++; - if (numMemoryOrderClause > 1) { - context_.Say(clause.source, - "More than one memory order clause not allowed on " - "OpenMP Atomic construct"_err_en_US); - return; +void OmpStructureChecker::CheckAtomicHintClause( + const parser::OmpAtomicClauseList *leftOmpAtomicClauseList, + const parser::OmpAtomicClauseList *rightOmpAtomicClauseList) { + if (leftOmpAtomicClauseList) { + for (const auto &clause : leftOmpAtomicClauseList->v) { + if (const auto ompClause = + std::get_if(&clause.u)) { + if (auto hintClause = + std::get_if(&ompClause->u)) { + if (auto evaluatedValue{GetIntValue(hintClause->v)}) { + int hintValue = evaluatedValue.value(); + if (hintValue != 0x0 && hintValue != 0x1 && hintValue != 0x2 && + hintValue != 0x4 && hintValue != 0x8) + context_.Say(clause.source, + "Hint clause value " + "is not a valid OpenMP synchronization value"_err_en_US); + } + } } } } - for (const auto &clause : rightHandClauseList.v) { - if (std::get_if(&clause.u)) { - numMemoryOrderClause++; - if (numMemoryOrderClause > 1) { - context_.Say(clause.source, - "More than one memory order clause not " - "allowed on OpenMP Atomic construct"_err_en_US); - return; + if (rightOmpAtomicClauseList) { + for (const auto &clause : rightOmpAtomicClauseList->v) { + if (const auto ompClause = + std::get_if(&clause.u)) { + if (auto hintClause = + std::get_if(&ompClause->u)) { + if (auto evaluatedValue{GetIntValue(hintClause->v)}) { + int hintValue = evaluatedValue.value(); + if (hintValue != 0x0 && hintValue != 0x1 && hintValue != 0x2 && + hintValue != 0x4 && hintValue != 0x8) + context_.Say(clause.source, + "Hint clause value " + "is not a valid OpenMP synchronization value"_err_en_US); + } + } } } } } +bool OmpStructureChecker::CheckAtomicCaptureStmt( + const parser::AssignmentStmt &assignmentStmt, bool shouldSuppressOutput) { + const auto &expr{std::get(assignmentStmt.t)}; + const auto &var{std::get(assignmentStmt.t)}; + bool isVariable{false}; + bool isValidAtomicCaptureStmt{true}; + common::visit( + common::visitors{ + [&](const common::Indirection &designator) { + if (const auto *dataRef = std::get_if( + &designator.value().u)) { + if (const auto *name = + std::get_if(&dataRef->u)) { + if (IsVariableName(*name->symbol)) { + isVariable = true; + if (IsAllocatable(*name->symbol)) { + if (!shouldSuppressOutput) + context_.Say(name->source, + "%s must not have ALLOCATABLE " + "attribute"_err_en_US, + name->ToString()); + isValidAtomicCaptureStmt = false; + } + } + } + } + }, + [&](const auto &) {}, + }, + expr.u); + if (!isVariable) { + if (!shouldSuppressOutput) + context_.Say(expr.source, + "Expected scalar variable " + "of intrinsic type on RHS of OPENMP Atomic " + " (CAPTURE) assignment statement"_err_en_US); + isValidAtomicCaptureStmt = false; + } + return isValidAtomicCaptureStmt; +} + void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { common::visit( common::visitors{ @@ -1549,30 +1715,69 @@ const auto &dir{std::get(atomicConstruct.t)}; PushContextAndClauseSets( dir.source, llvm::omp::Directive::OMPD_atomic); - CheckAtomicUpdateAssignmentStmt( + CheckAtomicUpdateStmt( std::get>( atomicConstruct.t) - .statement); + .statement, + false); CheckAtomicMemoryOrderClause( - std::get(atomicConstruct.t)); + &std::get(atomicConstruct.t), + nullptr); + CheckAtomicHintClause( + &std::get(atomicConstruct.t), + nullptr); }, - [&](const parser::OmpAtomicUpdate &atomicConstruct) { - const auto &dir{std::get(atomicConstruct.t)}; + [&](const parser::OmpAtomicUpdate &atomicUpdate) { + const auto &dir{std::get(atomicUpdate.t)}; PushContextAndClauseSets( dir.source, llvm::omp::Directive::OMPD_atomic); - CheckAtomicUpdateAssignmentStmt( + CheckAtomicUpdateStmt( std::get>( - atomicConstruct.t) - .statement); + atomicUpdate.t) + .statement, + false); CheckAtomicMemoryOrderClause( - std::get<0>(atomicConstruct.t), std::get<2>(atomicConstruct.t)); + &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t)); + CheckAtomicHintClause( + &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t)); }, - [&](const auto &atomicConstruct) { - const auto &dir{std::get(atomicConstruct.t)}; + [&](const parser::OmpAtomicRead &atomicRead) { + const auto &dir{std::get(atomicRead.t)}; + PushContextAndClauseSets( + dir.source, llvm::omp::Directive::OMPD_atomic); + CheckAtomicMemoryOrderClause( + &std::get<0>(atomicRead.t), &std::get<2>(atomicRead.t)); + CheckAtomicHintClause( + &std::get<0>(atomicRead.t), &std::get<2>(atomicRead.t)); + CheckAtomicCaptureStmt( + std::get>( + atomicRead.t) + .statement, + false); + }, + [&](const parser::OmpAtomicWrite &atomicWrite) { + const auto &dir{std::get(atomicWrite.t)}; + PushContextAndClauseSets( + dir.source, llvm::omp::Directive::OMPD_atomic); + CheckAtomicMemoryOrderClause( + &std::get<0>(atomicWrite.t), &std::get<2>(atomicWrite.t)); + CheckAtomicHintClause( + &std::get<0>(atomicWrite.t), &std::get<2>(atomicWrite.t)); + CheckAtomicWriteStmt( + std::get>( + atomicWrite.t) + .statement, + false); + }, + [&](const parser::OmpAtomicCapture &atomicCapture) { + const auto &dir{std::get(atomicCapture.t)}; PushContextAndClauseSets( dir.source, llvm::omp::Directive::OMPD_atomic); CheckAtomicMemoryOrderClause( - std::get<0>(atomicConstruct.t), std::get<2>(atomicConstruct.t)); + &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t)); + CheckAtomicHintClause( + &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t)); + CheckAtomicCaptureConstruct(atomicCapture); }, }, x.u); diff --git a/flang/test/Semantics/omp-atomic-assignment-stmt.f90 b/flang/test/Semantics/omp-atomic-assignment-stmt.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic-assignment-stmt.f90 @@ -0,0 +1,64 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -fopenmp +! Semantic checks for various assignments related to atomic constructs + +program sample + use omp_lib + integer :: x, v + integer :: y(10) + integer, allocatable :: k + + !$omp atomic read + v = x + + !$omp atomic read + !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches scalar INTEGER(4) and rank 1 array of INTEGER(4) + !ERROR: Expected scalar variable of intrinsic type on RHS of OPENMP Atomic (CAPTURE) assignment statement + v = y(1:3) + + !$omp atomic read + !ERROR: Expected scalar variable of intrinsic type on RHS of OPENMP Atomic (CAPTURE) assignment statement + v = x * (10 + x) + + !$omp atomic read + !ERROR: Expected scalar variable of intrinsic type on RHS of OPENMP Atomic (CAPTURE) assignment statement + v = 4 + + !$omp atomic read + !ERROR: k must not have ALLOCATABLE attribute + v = k + + !$omp atomic write + !ERROR: k must not have ALLOCATABLE attribute + k = x + + !$omp atomic update + !ERROR: Atomic update statement should be of form `k = k operator expr` OR `k = expr operator k` + !ERROR: k must not have ALLOCATABLE attribute + k = v + k * (v * k) + + !$omp atomic + !ERROR: k must not have ALLOCATABLE attribute + k = v * k +end program + +subroutine omp_atomic_capture + integer :: x, v, y + integer, allocatable :: k + ! [capture-stmt, write-stmt] + !$omp atomic capture + x = v + v = max(x, v) + !$omp end atomic + + !$omp atomic capture + !ERROR: k must not have ALLOCATABLE attribute + x = k + v = max(x, y) + !$omp end atomic + + !$omp atomic capture + !ERROR: Expected scalar variable of intrinsic type on RHS of OPENMP Atomic (CAPTURE) assignment statement + x = 10 + x = 4 + x * 10 + !$omp end atomic +end subroutine diff --git a/flang/test/Semantics/omp-atomic-hint-clause.f90 b/flang/test/Semantics/omp-atomic-hint-clause.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic-hint-clause.f90 @@ -0,0 +1,48 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -fopenmp +! Semantic checks on hint clauses, as they appear on atomic constructs + +program sample + use omp_lib + integer :: x, y + !$omp atomic hint(1) write + y = 2 + + !$omp atomic read hint(2) + y = x + + !ERROR: Hint clause value is not a valid OpenMP synchronization value + !$omp atomic hint(3) + y = y + 10 + + !ERROR: Hint clause value is not a valid OpenMP synchronization value + !$omp atomic update hint(5) + y = x + + !ERROR: Hint clause value is not a valid OpenMP synchronization value + !$omp atomic hint(7) capture + y = x + x = y + !$omp end atomic + + !ERROR: Must be a constant value + !$omp atomic update hint(x) + y = y * 1 + + !$omp atomic read hint(4) + y = x + + !$omp atomic hint(8) + x = x * y + + !$omp atomic write hint(omp_sync_hint_uncontended) + x = 10 * y + + !$omp atomic hint(omp_lock_hint_speculative) + x = y + x + + !$omp atomic hint(omp_sync_hint_uncontended) read + y = x + + !$omp atomic hint(omp_sync_hint_nonspeculative) + y = y * 9 +end program diff --git a/flang/test/Semantics/omp-atomic-user-defined-operators.f90 b/flang/test/Semantics/omp-atomic-user-defined-operators.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic-user-defined-operators.f90 @@ -0,0 +1,58 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -fopenmp +! Semantics checks on user defined operators on atomic update statement +! TODO: Add error reporting for overloaded intrinsic operators + +module overload_asterisk + implicit none + private + public operator (*) + + interface operator(*) + module procedure logical_and + end interface + +contains + pure logical function logical_and(log1, log2) + logical, intent(in) :: log1, log2 + + logical_and = (log1 .and. log2) + end function +end module + +module new_operator + implicit none + private + public operator(.MYOPERATOR.) + + interface operator(.MYOPERATOR.) + module procedure myprocedure + end interface +contains + pure integer function myprocedure(param1, param2) + integer, intent(in) :: param1, param2 + myprocedure = param1 + param2 + end function +end module + +program sample + use omp_lib + use overload_asterisk + use new_operator + implicit none + + logical :: T = .true., F = .false. + integer :: x, y + !$omp atomic + T = T*T + + !$omp atomic update + x = x / y + + !$omp atomic update + !ERROR: User defined operator not allowed in OpenMP ATOMIC (UPDATE) statement + x = x .MYOPERATOR. y + + !$omp atomic + !ERROR: User defined operator not allowed in OpenMP ATOMIC (UPDATE) statement + x = x .MYOPERATOR. y +end program diff --git a/flang/test/Semantics/omp-atomic02.f90 b/flang/test/Semantics/omp-atomic02.f90 --- a/flang/test/Semantics/omp-atomic02.f90 +++ b/flang/test/Semantics/omp-atomic02.f90 @@ -32,27 +32,27 @@ !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement c = c//d !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .LT. b !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .LE. b !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .EQ. b !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .NE. b !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .GE. b !$omp atomic - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .GT. b !$omp atomic @@ -78,23 +78,23 @@ !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement c = c//d !$omp atomic update - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .LT. b !$omp atomic update - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .LE. b !$omp atomic update - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .EQ. b !$omp atomic update - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .GE. b !$omp atomic update - !ERROR: Atomic update variable 'l' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `l = l operator expr` OR `l = expr operator l` !ERROR: Invalid operator in OpenMP ATOMIC (UPDATE) statement l = a .GT. b !$omp atomic update diff --git a/flang/test/Semantics/omp-atomic04.f90 b/flang/test/Semantics/omp-atomic04.f90 --- a/flang/test/Semantics/omp-atomic04.f90 +++ b/flang/test/Semantics/omp-atomic04.f90 @@ -18,21 +18,22 @@ !$omp atomic x = 1 + x !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y + 1 !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct - x = 1 + y + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = 1 + (y + x) !$omp atomic - x = x - 1 + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = 1 - (10 * (y + x)) !$omp atomic x = 1 - x !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y - 1 !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = 1 - y !$omp atomic @@ -40,21 +41,21 @@ !$omp atomic x = 1*x !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct - x = y*1 + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = y*(10 + x) !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct - x = 1*y + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = (44 * x) * y !$omp atomic x = x/1 !$omp atomic x = 1/x !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y/1 !$omp atomic - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = 1/y !$omp atomic @@ -62,7 +63,7 @@ !$omp atomic m = n .AND. m !$omp atomic - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .AND. l !$omp atomic @@ -70,7 +71,7 @@ !$omp atomic m = n .OR. m !$omp atomic - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .OR. l !$omp atomic @@ -78,7 +79,7 @@ !$omp atomic m = n .EQV. m !$omp atomic - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .EQV. l !$omp atomic @@ -86,7 +87,7 @@ !$omp atomic m = n .NEQV. m !$omp atomic - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .NEQV. l !$omp atomic update @@ -94,10 +95,10 @@ !$omp atomic update x = 1 + x !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y + 1 !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = 1 + y !$omp atomic update @@ -105,10 +106,10 @@ !$omp atomic update x = 1 - x !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y - 1 !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = 1 - y !$omp atomic update @@ -116,10 +117,10 @@ !$omp atomic update x = 1*x !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = y*1 !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` x = 1*y !$omp atomic update @@ -127,18 +128,18 @@ !$omp atomic update x = 1/x !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct - x = y/1 + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = max(x, y) + 10 !$omp atomic update - !ERROR: Atomic update variable 'x' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct - x = 1/y + !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x` + x = y * min(x, y) !$omp atomic update m = m .AND. n !$omp atomic update m = n .AND. m !$omp atomic update - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .AND. l !$omp atomic update @@ -146,7 +147,7 @@ !$omp atomic update m = n .OR. m !$omp atomic update - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .OR. l !$omp atomic update @@ -154,7 +155,7 @@ !$omp atomic update m = n .EQV. m !$omp atomic update - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .EQV. l !$omp atomic update @@ -162,7 +163,7 @@ !$omp atomic update m = n .NEQV. m !$omp atomic update - !ERROR: Atomic update variable 'm' not found in the RHS of the assignment statement in an ATOMIC (UPDATE) construct + !ERROR: Atomic update statement should be of form `m = m operator expr` OR `m = expr operator m` m = n .NEQV. l end program OmpAtomic