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