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