brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.9 KiB · ad21129 Raw
741 lines · cpp
1//== SemaOpenACCAtomic.cpp - Semantic Analysis for OpenACC Atomic Construct===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8/// \file9/// This file implements semantic analysis for the OpenACC atomic construct.10///11//===----------------------------------------------------------------------===//12 13#include "clang/AST/ExprCXX.h"14#include "clang/Basic/DiagnosticSema.h"15#include "clang/Sema/SemaOpenACC.h"16 17#include <optional>18 19using namespace clang;20 21namespace {22 23class AtomicOperandChecker {24  SemaOpenACC &SemaRef;25  OpenACCAtomicKind AtKind;26  SourceLocation AtomicDirLoc;27  StmtResult AssocStmt;28 29  // Do a diagnostic, which sets the correct error, then displays passed note.30  bool DiagnoseInvalidAtomic(SourceLocation Loc, PartialDiagnostic NoteDiag) {31    SemaRef.Diag(AtomicDirLoc, diag::err_acc_invalid_atomic)32        << (AtKind != OpenACCAtomicKind::None) << AtKind;33    SemaRef.Diag(Loc, NoteDiag);34    return true;35  }36 37  // Create a replacement recovery expr in case we find an error here.  This38  // allows us to ignore this during template instantiation so we only get a39  // single error.40  StmtResult getRecoveryExpr() {41    if (!AssocStmt.isUsable())42      return AssocStmt;43 44    if (!SemaRef.getASTContext().getLangOpts().RecoveryAST)45      return StmtError();46 47    Expr *E = dyn_cast<Expr>(AssocStmt.get());48    QualType T = E ? E->getType() : SemaRef.getASTContext().DependentTy;49 50    return RecoveryExpr::Create(SemaRef.getASTContext(), T,51                                AssocStmt.get()->getBeginLoc(),52                                AssocStmt.get()->getEndLoc(),53                                E ? ArrayRef<Expr *>{E} : ArrayRef<Expr *>{});54  }55 56  // OpenACC 3.3 2.12: 'expr' is an expression with scalar type.57  bool CheckOperandExpr(const Expr *E, PartialDiagnostic PD) {58    QualType ExprTy = E->getType();59 60    // Scalar allowed, plus we allow instantiation dependent to support61    // templates.62    if (ExprTy->isInstantiationDependentType() || ExprTy->isScalarType())63      return false;64 65    return DiagnoseInvalidAtomic(E->getExprLoc(),66                                 PD << diag::OACCLValScalar::Scalar << ExprTy);67  }68 69  // OpenACC 3.3 2.12: 'x' and 'v' (as applicable) are boht l-value expressoins70  // with scalar type.71  bool CheckOperandVariable(const Expr *E, PartialDiagnostic PD) {72    if (CheckOperandExpr(E, PD))73      return true;74 75    if (E->isLValue())76      return false;77 78    return DiagnoseInvalidAtomic(E->getExprLoc(),79                                 PD << diag::OACCLValScalar::LVal);80  }81 82  Expr *RequireExpr(Stmt *Stmt, PartialDiagnostic ExpectedNote) {83    if (Expr *E = dyn_cast<Expr>(Stmt))84      return E->IgnoreImpCasts();85 86    DiagnoseInvalidAtomic(Stmt->getBeginLoc(), ExpectedNote);87    return nullptr;88  }89 90  // A struct to hold the return the inner components of any operands, which91  // allows for compound checking.92  struct BinaryOpInfo {93    const Expr *FoundExpr = nullptr;94    const Expr *LHS = nullptr;95    const Expr *RHS = nullptr;96    BinaryOperatorKind Operator;97  };98 99  struct UnaryOpInfo {100    const Expr *FoundExpr = nullptr;101    const Expr *SubExpr = nullptr;102    UnaryOperatorKind Operator;103 104    bool IsIncrementOp() {105      return Operator == UO_PostInc || Operator == UO_PreInc;106    }107  };108 109  std::optional<UnaryOpInfo> GetUnaryOperatorInfo(const Expr *E) {110    // If this is a simple unary operator, just return its details.111    if (const auto *UO = dyn_cast<UnaryOperator>(E))112      return UnaryOpInfo{UO, UO->getSubExpr()->IgnoreImpCasts(),113                         UO->getOpcode()};114 115    // This might be an overloaded operator or a dependent context, so make sure116    // we can get as many details out of this as we can.117    if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {118      UnaryOpInfo Inf;119      Inf.FoundExpr = OpCall;120 121      switch (OpCall->getOperator()) {122      default:123        return std::nullopt;124      case OO_PlusPlus:125        Inf.Operator = OpCall->getNumArgs() == 1 ? UO_PreInc : UO_PostInc;126        break;127      case OO_MinusMinus:128        Inf.Operator = OpCall->getNumArgs() == 1 ? UO_PreDec : UO_PostDec;129        break;130      case OO_Amp:131        Inf.Operator = UO_AddrOf;132        break;133      case OO_Star:134        Inf.Operator = UO_Deref;135        break;136      case OO_Plus:137        Inf.Operator = UO_Plus;138        break;139      case OO_Minus:140        Inf.Operator = UO_Minus;141        break;142      case OO_Tilde:143        Inf.Operator = UO_Not;144        break;145      case OO_Exclaim:146        Inf.Operator = UO_LNot;147        break;148      case OO_Coawait:149        Inf.Operator = UO_Coawait;150        break;151      }152 153      // Some of the above can be both binary and unary operations, so make sure154      // we get the right one.155      if (Inf.Operator != UO_PostInc && Inf.Operator != UO_PostDec &&156          OpCall->getNumArgs() != 1)157        return std::nullopt;158 159      Inf.SubExpr = OpCall->getArg(0);160      return Inf;161    }162    return std::nullopt;163  }164 165  // Get a normalized version of a binary operator.166  std::optional<BinaryOpInfo> GetBinaryOperatorInfo(const Expr *E) {167    if (const auto *BO = dyn_cast<BinaryOperator>(E))168      return BinaryOpInfo{BO, BO->getLHS()->IgnoreImpCasts(),169                          BO->getRHS()->IgnoreImpCasts(), BO->getOpcode()};170 171    // In case this is an operator-call, which allows us to support overloaded172    // operators and dependent expression.173    if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {174      BinaryOpInfo Inf;175      Inf.FoundExpr = OpCall;176 177      switch (OpCall->getOperator()) {178      default:179        return std::nullopt;180      case OO_Plus:181        Inf.Operator = BO_Add;182        break;183      case OO_Minus:184        Inf.Operator = BO_Sub;185        break;186      case OO_Star:187        Inf.Operator = BO_Mul;188        break;189      case OO_Slash:190        Inf.Operator = BO_Div;191        break;192      case OO_Percent:193        Inf.Operator = BO_Rem;194        break;195      case OO_Caret:196        Inf.Operator = BO_Xor;197        break;198      case OO_Amp:199        Inf.Operator = BO_And;200        break;201      case OO_Pipe:202        Inf.Operator = BO_Or;203        break;204      case OO_Equal:205        Inf.Operator = BO_Assign;206        break;207      case OO_Spaceship:208        Inf.Operator = BO_Cmp;209        break;210      case OO_Less:211        Inf.Operator = BO_LT;212        break;213      case OO_Greater:214        Inf.Operator = BO_GT;215        break;216      case OO_PlusEqual:217        Inf.Operator = BO_AddAssign;218        break;219      case OO_MinusEqual:220        Inf.Operator = BO_SubAssign;221        break;222      case OO_StarEqual:223        Inf.Operator = BO_MulAssign;224        break;225      case OO_SlashEqual:226        Inf.Operator = BO_DivAssign;227        break;228      case OO_PercentEqual:229        Inf.Operator = BO_RemAssign;230        break;231      case OO_CaretEqual:232        Inf.Operator = BO_XorAssign;233        break;234      case OO_AmpEqual:235        Inf.Operator = BO_AndAssign;236        break;237      case OO_PipeEqual:238        Inf.Operator = BO_OrAssign;239        break;240      case OO_LessLess:241        Inf.Operator = BO_Shl;242        break;243      case OO_GreaterGreater:244        Inf.Operator = BO_Shr;245        break;246      case OO_LessLessEqual:247        Inf.Operator = BO_ShlAssign;248        break;249      case OO_GreaterGreaterEqual:250        Inf.Operator = BO_ShrAssign;251        break;252      case OO_EqualEqual:253        Inf.Operator = BO_EQ;254        break;255      case OO_ExclaimEqual:256        Inf.Operator = BO_NE;257        break;258      case OO_LessEqual:259        Inf.Operator = BO_LE;260        break;261      case OO_GreaterEqual:262        Inf.Operator = BO_GE;263        break;264      case OO_AmpAmp:265        Inf.Operator = BO_LAnd;266        break;267      case OO_PipePipe:268        Inf.Operator = BO_LOr;269        break;270      case OO_Comma:271        Inf.Operator = BO_Comma;272        break;273      case OO_ArrowStar:274        Inf.Operator = BO_PtrMemI;275        break;276      }277 278      // This isn't a binary operator unless there are two arguments.279      if (OpCall->getNumArgs() != 2)280        return std::nullopt;281 282      // Callee is the call-operator, so we only need to extract the two283      // arguments here.284      Inf.LHS = OpCall->getArg(0)->IgnoreImpCasts();285      Inf.RHS = OpCall->getArg(1)->IgnoreImpCasts();286      return Inf;287    }288 289    return std::nullopt;290  }291 292  // Checks a required assignment operation, but don't check the LHS or RHS,293  // callers have to do that here.294  std::optional<BinaryOpInfo> CheckAssignment(const Expr *E) {295    std::optional<BinaryOpInfo> Inf = GetBinaryOperatorInfo(E);296 297    if (!Inf) {298      DiagnoseInvalidAtomic(E->getExprLoc(),299                            SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)300                                << diag::OACCAtomicExpr::Assign);301      return std::nullopt;302    }303 304    if (Inf->Operator != BO_Assign) {305      DiagnoseInvalidAtomic(Inf->FoundExpr->getExprLoc(),306                            SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)307                                << diag::OACCAtomicExpr::Assign);308      return std::nullopt;309    }310 311    // Assignment always requires an lvalue/scalar on the LHS.312    if (CheckOperandVariable(313            Inf->LHS, SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)314                          << /*left=*/0 << diag::OACCAtomicOpKind::Assign))315      return std::nullopt;316 317    return Inf;318  }319 320  struct IDACInfo {321    bool Failed = false;322    enum ExprKindTy {323      Invalid,324      // increment/decrement ops.325      Unary,326      // v = x327      SimpleAssign,328      // x = expr329      ExprAssign,330      // x binop= expr331      CompoundAssign,332      // x = x binop expr333      // x = expr binop x334      AssignBinOp335    } ExprKind;336 337    // The variable referred to as 'x' in all of the grammar, such that it is338    // needed in compound statement checking of capture to check between the two339    // expressions.340    const Expr *X_Var = nullptr;341 342    static IDACInfo Fail() { return IDACInfo{true, Invalid, nullptr}; };343  };344 345  // Helper for CheckIncDecAssignCompoundAssign, does checks for inc/dec.346  IDACInfo CheckIncDec(UnaryOpInfo Inf) {347 348    if (!UnaryOperator::isIncrementDecrementOp(Inf.Operator)) {349      DiagnoseInvalidAtomic(350          Inf.FoundExpr->getExprLoc(),351          SemaRef.PDiag(diag::note_acc_atomic_unsupported_unary_operator));352      return IDACInfo::Fail();353    }354    bool Failed = CheckOperandVariable(355        Inf.SubExpr,356        SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)357            << /*none=*/2358            << (Inf.IsIncrementOp() ? diag::OACCAtomicOpKind::Inc359                                    : diag::OACCAtomicOpKind::Dec));360    // For increment/decrements, the subexpr is the 'x' (x++, ++x, etc).361    return IDACInfo{Failed, IDACInfo::Unary, Inf.SubExpr};362  }363 364  enum class SimpleAssignKind { None, Var, Expr };365 366  // Check an assignment, and ensure the RHS is either x binop expr or expr367  // binop x.368  // If AllowSimpleAssign, also allows v = x;369  IDACInfo CheckAssignmentWithBinOpOnRHS(BinaryOpInfo AssignInf,370                                         SimpleAssignKind SAK) {371    PartialDiagnostic PD =372        SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)373        << /*left=*/0 << diag::OACCAtomicOpKind::Assign;374    if (CheckOperandVariable(AssignInf.LHS, PD))375      return IDACInfo::Fail();376 377    std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(AssignInf.RHS);378 379    if (!BinInf) {380 381      // Capture in a compound statement allows v = x assignment.  So make sure382      // we permit that here.383      if (SAK != SimpleAssignKind::None) {384        PartialDiagnostic PD =385            SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)386            << /*right=*/1 << diag::OACCAtomicOpKind::Assign;387        if (SAK == SimpleAssignKind::Var) {388          // In the var version, everywhere we allow v = x;, X is the RHS.389          return IDACInfo{CheckOperandVariable(AssignInf.RHS, PD),390                          IDACInfo::SimpleAssign, AssignInf.RHS};391        }392        assert(SAK == SimpleAssignKind::Expr);393        // In the expression version, supported by v=x; x = expr;, we need to394        // set to the LHS here.395        return IDACInfo{CheckOperandExpr(AssignInf.RHS, PD),396                        IDACInfo::ExprAssign, AssignInf.LHS};397      }398 399      DiagnoseInvalidAtomic(400          AssignInf.RHS->getExprLoc(),401          SemaRef.PDiag(diag::note_acc_atomic_expected_binop));402 403      return IDACInfo::Fail();404    }405    switch (BinInf->Operator) {406    default:407      DiagnoseInvalidAtomic(408          BinInf->FoundExpr->getExprLoc(),409          SemaRef.PDiag(diag::note_acc_atomic_unsupported_binary_operator));410      return IDACInfo::Fail();411      // binop is one of +, *, -, /, &, ^, |, <<, or >>412    case BO_Add:413    case BO_Mul:414    case BO_Sub:415    case BO_Div:416    case BO_And:417    case BO_Xor:418    case BO_Or:419    case BO_Shl:420    case BO_Shr:421      // Handle these outside of the switch.422      break;423    }424 425    llvm::FoldingSetNodeID LHS_ID, InnerLHS_ID, InnerRHS_ID;426    AssignInf.LHS->Profile(LHS_ID, SemaRef.getASTContext(),427                           /*Canonical=*/true);428    BinInf->LHS->Profile(InnerLHS_ID, SemaRef.getASTContext(),429                         /*Canonical=*/true);430 431    // This is X = X binop expr;432    // Check the RHS is an expression.433    if (LHS_ID == InnerLHS_ID)434      return IDACInfo{435          CheckOperandExpr(436              BinInf->RHS,437              SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar438                            << /*right=*/1439                            << diag::OACCAtomicOpKind::CompoundAssign)),440          IDACInfo::AssignBinOp, AssignInf.LHS};441 442    BinInf->RHS->Profile(InnerRHS_ID, SemaRef.getASTContext(),443                         /*Canonical=*/true);444    // This is X = expr binop X;445    // Check the LHS is an expression446    if (LHS_ID == InnerRHS_ID)447      return IDACInfo{448          CheckOperandExpr(449              BinInf->LHS,450              SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)451                  << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign),452          IDACInfo::AssignBinOp, AssignInf.LHS};453 454    // If nothing matches, error out.455    DiagnoseInvalidAtomic(BinInf->FoundExpr->getExprLoc(),456                          SemaRef.PDiag(diag::note_acc_atomic_mismatch_operand)457                              << AssignInf.LHS << BinInf->LHS << BinInf->RHS);458    return IDACInfo::Fail();459  }460 461  // Ensures that the expression is an increment/decrement, an assignment, or a462  // compound assignment. If its an assignment, allows the x binop expr/x binop463  // expr syntax. If it is a compound-assignment, allows any expr on the RHS.464  IDACInfo CheckIncDecAssignCompoundAssign(const Expr *E,465                                           SimpleAssignKind SAK) {466    std::optional<UnaryOpInfo> UInf = GetUnaryOperatorInfo(E);467 468    // If this is a unary operator, only increment/decrement are allowed, so get469    // unary operator, then check everything we can.470    if (UInf)471      return CheckIncDec(*UInf);472 473    std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(E);474 475    // Unary or binary operator were the only choices, so error here.476    if (!BinInf) {477      DiagnoseInvalidAtomic(E->getExprLoc(),478                            SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)479                                << diag::OACCAtomicExpr::UnaryCompAssign);480      return IDACInfo::Fail();481    }482 483    switch (BinInf->Operator) {484    default:485      DiagnoseInvalidAtomic(486          BinInf->FoundExpr->getExprLoc(),487          SemaRef.PDiag(488              diag::note_acc_atomic_unsupported_compound_binary_operator));489      return IDACInfo::Fail();490    case BO_Assign:491      return CheckAssignmentWithBinOpOnRHS(*BinInf, SAK);492    case BO_AddAssign:493    case BO_MulAssign:494    case BO_SubAssign:495    case BO_DivAssign:496    case BO_AndAssign:497    case BO_XorAssign:498    case BO_OrAssign:499    case BO_ShlAssign:500    case BO_ShrAssign: {501      PartialDiagnostic LPD =502          SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)503          << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign;504      PartialDiagnostic RPD =505          SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)506          << /*right=*/1 << diag::OACCAtomicOpKind::CompoundAssign;507      // nothing to do other than check the variable expressions.508      // success or failure509      bool Failed = CheckOperandVariable(BinInf->LHS, LPD) ||510                    CheckOperandExpr(BinInf->RHS, RPD);511 512      return IDACInfo{Failed, IDACInfo::CompoundAssign, BinInf->LHS};513    }514    }515    llvm_unreachable("all binary operator kinds should be checked above");516  }517 518  StmtResult CheckRead() {519    Expr *AssocExpr = RequireExpr(520        AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)521                             << diag::OACCAtomicExpr::Assign);522 523    if (!AssocExpr)524      return getRecoveryExpr();525 526    std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);527    if (!AssignRes)528      return getRecoveryExpr();529 530    PartialDiagnostic PD =531        SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)532        << /*right=*/1 << diag::OACCAtomicOpKind::Assign;533 534    // Finally, check the RHS.535    if (CheckOperandVariable(AssignRes->RHS, PD))536      return getRecoveryExpr();537 538    return AssocStmt;539  }540 541  StmtResult CheckWrite() {542    Expr *AssocExpr = RequireExpr(543        AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)544                             << diag::OACCAtomicExpr::Assign);545 546    if (!AssocExpr)547      return getRecoveryExpr();548 549    std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);550    if (!AssignRes)551      return getRecoveryExpr();552 553    PartialDiagnostic PD =554        SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)555        << /*right=*/1 << diag::OACCAtomicOpKind::Assign;556 557    // Finally, check the RHS.558    if (CheckOperandExpr(AssignRes->RHS, PD))559      return getRecoveryExpr();560 561    return AssocStmt;562  }563 564  StmtResult CheckUpdate() {565    Expr *AssocExpr = RequireExpr(566        AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)567                             << diag::OACCAtomicExpr::UnaryCompAssign);568 569    if (!AssocExpr ||570        CheckIncDecAssignCompoundAssign(AssocExpr, SimpleAssignKind::None)571            .Failed)572      return getRecoveryExpr();573 574    return AssocStmt;575  }576 577  const Expr *IgnoreBeforeCompare(const Expr *E) {578    return E->IgnoreParenImpCasts()->IgnoreParenNoopCasts(579        SemaRef.getASTContext());580  }581 582  bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,583                        IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {584    llvm::FoldingSetNodeID First_ID, Second_ID;585    FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);586    SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);587 588    if (First_ID == Second_ID)589      return false;590 591    PartialDiagnostic PD =592        SemaRef.PDiag(diag::note_acc_atomic_mismatch_compound_operand)593        << FirstKind << FirstX << SecondKind << SecondX;594 595    return DiagnoseInvalidAtomic(SecondX->getExprLoc(), PD);596  }597 598  StmtResult CheckCapture() {599    if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(AssocStmt.get())) {600      auto *const *BodyItr = CmpdStmt->body().begin();601      PartialDiagnostic PD = SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)602                             << diag::OACCAtomicExpr::UnaryCompAssign;603      // If we don't have at least 1 statement, error.604      if (BodyItr == CmpdStmt->body().end()) {605        DiagnoseInvalidAtomic(CmpdStmt->getBeginLoc(), PD);606        return getRecoveryExpr();607      }608 609      // First Expr can be inc/dec, assign, or compound assign.610      Expr *FirstExpr = RequireExpr(*BodyItr, PD);611      if (!FirstExpr)612        return getRecoveryExpr();613 614      IDACInfo FirstExprResults =615          CheckIncDecAssignCompoundAssign(FirstExpr, SimpleAssignKind::Var);616      if (FirstExprResults.Failed)617        return getRecoveryExpr();618 619      ++BodyItr;620 621      // If we don't have second statement, error.622      if (BodyItr == CmpdStmt->body().end()) {623        DiagnoseInvalidAtomic(CmpdStmt->getEndLoc(), PD);624        return getRecoveryExpr();625      }626 627      Expr *SecondExpr = RequireExpr(*BodyItr, PD);628      if (!SecondExpr)629        return getRecoveryExpr();630 631      assert(FirstExprResults.ExprKind != IDACInfo::Invalid);632 633      switch (FirstExprResults.ExprKind) {634      case IDACInfo::Invalid:635      case IDACInfo::ExprAssign:636        llvm_unreachable("Should have error'ed out by now");637      case IDACInfo::Unary:638      case IDACInfo::CompoundAssign:639      case IDACInfo::AssignBinOp: {640        // Everything but simple-assign can only be followed by a simple641        // assignment.642        std::optional<BinaryOpInfo> AssignRes = CheckAssignment(SecondExpr);643        if (!AssignRes)644          return getRecoveryExpr();645 646        PartialDiagnostic PD =647            SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)648            << /*right=*/1 << diag::OACCAtomicOpKind::Assign;649 650        if (CheckOperandVariable(AssignRes->RHS, PD))651          return getRecoveryExpr();652 653        if (CheckVarRefsSame(FirstExprResults.ExprKind,654                             IgnoreBeforeCompare(FirstExprResults.X_Var),655                             IDACInfo::SimpleAssign,656                             IgnoreBeforeCompare(AssignRes->RHS)))657          return getRecoveryExpr();658        break;659      }660      case IDACInfo::SimpleAssign: {661        // If the first was v = x, anything but simple expression is allowed.662        IDACInfo SecondExprResults =663            CheckIncDecAssignCompoundAssign(SecondExpr, SimpleAssignKind::Expr);664        if (SecondExprResults.Failed)665          return getRecoveryExpr();666 667        if (CheckVarRefsSame(FirstExprResults.ExprKind,668                             IgnoreBeforeCompare(FirstExprResults.X_Var),669                             SecondExprResults.ExprKind,670                             IgnoreBeforeCompare(SecondExprResults.X_Var)))671          return getRecoveryExpr();672        break;673      }674      }675      ++BodyItr;676      if (BodyItr != CmpdStmt->body().end()) {677        DiagnoseInvalidAtomic(678            (*BodyItr)->getBeginLoc(),679            SemaRef.PDiag(diag::note_acc_atomic_too_many_stmts));680        return getRecoveryExpr();681      }682    } else {683      // This check doesn't need to happen if it is a compound stmt.684      Expr *AssocExpr = RequireExpr(685          AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)686                               << diag::OACCAtomicExpr::Assign);687      if (!AssocExpr)688        return getRecoveryExpr();689 690      // First, we require an assignment.691      std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);692 693      if (!AssignRes)694        return getRecoveryExpr();695 696      if (CheckIncDecAssignCompoundAssign(AssignRes->RHS,697                                          SimpleAssignKind::None)698              .Failed)699        return getRecoveryExpr();700    }701 702    return AssocStmt;703  }704 705public:706  AtomicOperandChecker(SemaOpenACC &S, OpenACCAtomicKind AtKind,707                       SourceLocation DirLoc, StmtResult AssocStmt)708      : SemaRef(S), AtKind(AtKind), AtomicDirLoc(DirLoc), AssocStmt(AssocStmt) {709  }710 711  StmtResult Check() {712 713    switch (AtKind) {714    case OpenACCAtomicKind::Read:715      return CheckRead();716    case OpenACCAtomicKind::Write:717      return CheckWrite();718    case OpenACCAtomicKind::None:719    case OpenACCAtomicKind::Update:720      return CheckUpdate();721    case OpenACCAtomicKind::Capture:722      return CheckCapture();723    }724    llvm_unreachable("Unhandled atomic kind?");725  }726};727} // namespace728 729StmtResult SemaOpenACC::CheckAtomicAssociatedStmt(SourceLocation AtomicDirLoc,730                                                  OpenACCAtomicKind AtKind,731                                                  StmtResult AssocStmt) {732  if (!AssocStmt.isUsable())733    return AssocStmt;734 735  if (isa<RecoveryExpr>(AssocStmt.get()))736    return AssocStmt;737 738  AtomicOperandChecker Checker{*this, AtKind, AtomicDirLoc, AssocStmt};739  return Checker.Check();740}741