@@ -1647,6 +1647,67 @@ void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) {
1647
1647
}
1648
1648
}
1649
1649
1650
+ inline void OmpStructureChecker::ErrIfAllocatableVariable (
1651
+ const parser::Variable &var) {
1652
+ // Err out if the given symbol has
1653
+ // ALLOCATABLE attribute
1654
+ if (const auto *e{GetExpr (context_, var)})
1655
+ for (const Symbol &symbol : evaluate::CollectSymbols (*e))
1656
+ if (IsAllocatable (symbol)) {
1657
+ const auto &designator =
1658
+ std::get<common::Indirection<parser::Designator>>(var.u );
1659
+ const auto *dataRef =
1660
+ std::get_if<Fortran::parser::DataRef>(&designator.value ().u );
1661
+ const Fortran::parser::Name *name =
1662
+ dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u ) : nullptr ;
1663
+ if (name)
1664
+ context_.Say (name->source ,
1665
+ " %s must not have ALLOCATABLE "
1666
+ " attribute" _err_en_US,
1667
+ name->ToString ());
1668
+ }
1669
+ }
1670
+
1671
+ inline void OmpStructureChecker::ErrIfLHSAndRHSSymbolsMatch (
1672
+ const parser::Variable &var, const parser::Expr &expr) {
1673
+ // Err out if the symbol on the LHS is also used on the RHS of the assignment
1674
+ // statement
1675
+ const auto *e{GetExpr (context_, expr)};
1676
+ const auto *v{GetExpr (context_, var)};
1677
+ if (e && v) {
1678
+ const Symbol &varSymbol = evaluate::GetSymbolVector (*v).front ();
1679
+ for (const Symbol &symbol : evaluate::GetSymbolVector (*e)) {
1680
+ if (varSymbol == symbol) {
1681
+ context_.Say (expr.source ,
1682
+ " RHS expression "
1683
+ " on atomic assignment statement"
1684
+ " cannot access '%s'" _err_en_US,
1685
+ var.GetSource ().ToString ());
1686
+ }
1687
+ }
1688
+ }
1689
+ }
1690
+
1691
+ inline void OmpStructureChecker::ErrIfNonScalarAssignmentStmt (
1692
+ const parser::Variable &var, const parser::Expr &expr) {
1693
+ // Err out if either the variable on the LHS or the expression on the RHS of
1694
+ // the assignment statement are non-scalar (i.e. have rank > 0)
1695
+ const auto *e{GetExpr (context_, expr)};
1696
+ const auto *v{GetExpr (context_, var)};
1697
+ if (e && v) {
1698
+ if (e->Rank () != 0 )
1699
+ context_.Say (expr.source ,
1700
+ " Expected scalar expression "
1701
+ " on the RHS of atomic assignment "
1702
+ " statement" _err_en_US);
1703
+ if (v->Rank () != 0 )
1704
+ context_.Say (var.GetSource (),
1705
+ " Expected scalar variable "
1706
+ " on the LHS of atomic assignment "
1707
+ " statement" _err_en_US);
1708
+ }
1709
+ }
1710
+
1650
1711
template <typename T, typename D>
1651
1712
bool OmpStructureChecker::IsOperatorValid (const T &node, const D &variable) {
1652
1713
using AllowedBinaryOperators =
@@ -1667,16 +1728,55 @@ bool OmpStructureChecker::IsOperatorValid(const T &node, const D &variable) {
1667
1728
if ((exprLeft.value ().source .ToString () != variableName) &&
1668
1729
(exprRight.value ().source .ToString () != variableName)) {
1669
1730
context_.Say (variable.GetSource (),
1670
- " Atomic update variable '%s' not found in the RHS of the "
1671
- " assignment statement in an ATOMIC (UPDATE) construct " _err_en_US,
1672
- variableName);
1731
+ " Atomic update statement should be of form "
1732
+ " `%s = %s operator expr` OR `%s = expr operator %s` " _err_en_US,
1733
+ variableName, variableName, variableName, variableName );
1673
1734
}
1674
1735
return common::HasMember<T, AllowedBinaryOperators>;
1675
1736
}
1676
1737
return true ;
1677
1738
}
1678
1739
1679
- void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt (
1740
+ void OmpStructureChecker::CheckAtomicCaptureStmt (
1741
+ const parser::AssignmentStmt &assignmentStmt) {
1742
+ const auto &var{std::get<parser::Variable>(assignmentStmt.t )};
1743
+ const auto &expr{std::get<parser::Expr>(assignmentStmt.t )};
1744
+ common::visit (
1745
+ common::visitors{
1746
+ [&](const common::Indirection<parser::Designator> &designator) {
1747
+ const auto *dataRef =
1748
+ std::get_if<Fortran::parser::DataRef>(&designator.value ().u );
1749
+ const auto *name = dataRef
1750
+ ? std::get_if<Fortran::parser::Name>(&dataRef->u )
1751
+ : nullptr ;
1752
+ if (name && IsAllocatable (*name->symbol ))
1753
+ context_.Say (name->source ,
1754
+ " %s must not have ALLOCATABLE "
1755
+ " attribute" _err_en_US,
1756
+ name->ToString ());
1757
+ },
1758
+ [&](const auto &) {
1759
+ // Anything other than a `parser::Designator` is not allowed
1760
+ context_.Say (expr.source ,
1761
+ " Expected scalar variable "
1762
+ " of intrinsic type on RHS of atomic "
1763
+ " assignment statement" _err_en_US);
1764
+ }},
1765
+ expr.u );
1766
+ ErrIfLHSAndRHSSymbolsMatch (var, expr);
1767
+ ErrIfNonScalarAssignmentStmt (var, expr);
1768
+ }
1769
+
1770
+ void OmpStructureChecker::CheckAtomicWriteStmt (
1771
+ const parser::AssignmentStmt &assignmentStmt) {
1772
+ const auto &var{std::get<parser::Variable>(assignmentStmt.t )};
1773
+ const auto &expr{std::get<parser::Expr>(assignmentStmt.t )};
1774
+ ErrIfAllocatableVariable (var);
1775
+ ErrIfLHSAndRHSSymbolsMatch (var, expr);
1776
+ ErrIfNonScalarAssignmentStmt (var, expr);
1777
+ }
1778
+
1779
+ void OmpStructureChecker::CheckAtomicUpdateStmt (
1680
1780
const parser::AssignmentStmt &assignment) {
1681
1781
const auto &expr{std::get<parser::Expr>(assignment.t )};
1682
1782
const auto &var{std::get<parser::Variable>(assignment.t )};
@@ -1734,6 +1834,7 @@ void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt(
1734
1834
},
1735
1835
},
1736
1836
expr.u );
1837
+ ErrIfAllocatableVariable (var);
1737
1838
}
1738
1839
1739
1840
void OmpStructureChecker::CheckAtomicMemoryOrderClause (
@@ -1772,7 +1873,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
1772
1873
const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t )};
1773
1874
PushContextAndClauseSets (
1774
1875
dir.source , llvm::omp::Directive::OMPD_atomic);
1775
- CheckAtomicUpdateAssignmentStmt (
1876
+ CheckAtomicUpdateStmt (
1776
1877
std::get<parser::Statement<parser::AssignmentStmt>>(
1777
1878
atomicConstruct.t )
1778
1879
.statement );
@@ -1787,7 +1888,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
1787
1888
const auto &dir{std::get<parser::Verbatim>(atomicUpdate.t )};
1788
1889
PushContextAndClauseSets (
1789
1890
dir.source , llvm::omp::Directive::OMPD_atomic);
1790
- CheckAtomicUpdateAssignmentStmt (
1891
+ CheckAtomicUpdateStmt (
1791
1892
std::get<parser::Statement<parser::AssignmentStmt>>(
1792
1893
atomicUpdate.t )
1793
1894
.statement );
@@ -1796,6 +1897,32 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
1796
1897
CheckHintClause<const parser::OmpAtomicClauseList>(
1797
1898
&std::get<0 >(atomicUpdate.t ), &std::get<2 >(atomicUpdate.t ));
1798
1899
},
1900
+ [&](const parser::OmpAtomicRead &atomicRead) {
1901
+ const auto &dir{std::get<parser::Verbatim>(atomicRead.t )};
1902
+ PushContextAndClauseSets (
1903
+ dir.source , llvm::omp::Directive::OMPD_atomic);
1904
+ CheckAtomicMemoryOrderClause (
1905
+ &std::get<0 >(atomicRead.t ), &std::get<2 >(atomicRead.t ));
1906
+ CheckHintClause<const parser::OmpAtomicClauseList>(
1907
+ &std::get<0 >(atomicRead.t ), &std::get<2 >(atomicRead.t ));
1908
+ CheckAtomicCaptureStmt (
1909
+ std::get<parser::Statement<parser::AssignmentStmt>>(
1910
+ atomicRead.t )
1911
+ .statement );
1912
+ },
1913
+ [&](const parser::OmpAtomicWrite &atomicWrite) {
1914
+ const auto &dir{std::get<parser::Verbatim>(atomicWrite.t )};
1915
+ PushContextAndClauseSets (
1916
+ dir.source , llvm::omp::Directive::OMPD_atomic);
1917
+ CheckAtomicMemoryOrderClause (
1918
+ &std::get<0 >(atomicWrite.t ), &std::get<2 >(atomicWrite.t ));
1919
+ CheckHintClause<const parser::OmpAtomicClauseList>(
1920
+ &std::get<0 >(atomicWrite.t ), &std::get<2 >(atomicWrite.t ));
1921
+ CheckAtomicWriteStmt (
1922
+ std::get<parser::Statement<parser::AssignmentStmt>>(
1923
+ atomicWrite.t )
1924
+ .statement );
1925
+ },
1799
1926
[&](const auto &atomicConstruct) {
1800
1927
const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t )};
1801
1928
PushContextAndClauseSets (
0 commit comments