| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// \file |
| 9 | /// 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 | |
| 19 | using namespace clang; |
| 20 | |
| 21 | namespace { |
| 22 | |
| 23 | class 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(Loc: AtomicDirLoc, DiagID: diag::err_acc_invalid_atomic) |
| 32 | << (AtKind != OpenACCAtomicKind::None) << AtKind; |
| 33 | SemaRef.Diag(Loc, PD: NoteDiag); |
| 34 | return true; |
| 35 | } |
| 36 | |
| 37 | // Create a replacement recovery expr in case we find an error here. This |
| 38 | // allows us to ignore this during template instantiation so we only get a |
| 39 | // 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>(Val: AssocStmt.get()); |
| 48 | QualType T = E ? E->getType() : SemaRef.getASTContext().DependentTy; |
| 49 | |
| 50 | return RecoveryExpr::Create(Ctx&: SemaRef.getASTContext(), T, |
| 51 | BeginLoc: AssocStmt.get()->getBeginLoc(), |
| 52 | EndLoc: AssocStmt.get()->getEndLoc(), |
| 53 | SubExprs: 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 support |
| 61 | // templates. |
| 62 | if (ExprTy->isInstantiationDependentType() || ExprTy->isScalarType()) |
| 63 | return false; |
| 64 | |
| 65 | return DiagnoseInvalidAtomic(Loc: E->getExprLoc(), |
| 66 | NoteDiag: PD << diag::OACCLValScalar::Scalar << ExprTy); |
| 67 | } |
| 68 | |
| 69 | // OpenACC 3.3 2.12: 'x' and 'v' (as applicable) are boht l-value expressoins |
| 70 | // 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(Loc: E->getExprLoc(), |
| 79 | NoteDiag: PD << diag::OACCLValScalar::LVal); |
| 80 | } |
| 81 | |
| 82 | Expr *RequireExpr(Stmt *Stmt, PartialDiagnostic ExpectedNote) { |
| 83 | if (Expr *E = dyn_cast<Expr>(Val: Stmt)) |
| 84 | return E->IgnoreImpCasts(); |
| 85 | |
| 86 | DiagnoseInvalidAtomic(Loc: Stmt->getBeginLoc(), NoteDiag: ExpectedNote); |
| 87 | return nullptr; |
| 88 | } |
| 89 | |
| 90 | // A struct to hold the return the inner components of any operands, which |
| 91 | // 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>(Val: E)) |
| 112 | return UnaryOpInfo{.FoundExpr: UO, .SubExpr: UO->getSubExpr()->IgnoreImpCasts(), |
| 113 | .Operator: UO->getOpcode()}; |
| 114 | |
| 115 | // This might be an overloaded operator or a dependent context, so make sure |
| 116 | // we can get as many details out of this as we can. |
| 117 | if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(Val: 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 sure |
| 154 | // 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(Arg: 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>(Val: E)) |
| 168 | return BinaryOpInfo{.FoundExpr: BO, .LHS: BO->getLHS()->IgnoreImpCasts(), |
| 169 | .RHS: BO->getRHS()->IgnoreImpCasts(), .Operator: BO->getOpcode()}; |
| 170 | |
| 171 | // In case this is an operator-call, which allows us to support overloaded |
| 172 | // operators and dependent expression. |
| 173 | if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(Val: 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 two |
| 283 | // arguments here. |
| 284 | Inf.LHS = OpCall->getArg(Arg: 0)->IgnoreImpCasts(); |
| 285 | Inf.RHS = OpCall->getArg(Arg: 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(Loc: E->getExprLoc(), |
| 299 | NoteDiag: SemaRef.PDiag(DiagID: 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(Loc: Inf->FoundExpr->getExprLoc(), |
| 306 | NoteDiag: SemaRef.PDiag(DiagID: 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 | E: Inf->LHS, PD: SemaRef.PDiag(DiagID: 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 = x |
| 327 | SimpleAssign, |
| 328 | // x = expr |
| 329 | ExprAssign, |
| 330 | // x binop= expr |
| 331 | CompoundAssign, |
| 332 | // x = x binop expr |
| 333 | // x = expr binop x |
| 334 | AssignBinOp |
| 335 | } ExprKind; |
| 336 | |
| 337 | // The variable referred to as 'x' in all of the grammar, such that it is |
| 338 | // needed in compound statement checking of capture to check between the two |
| 339 | // expressions. |
| 340 | const Expr *X_Var = nullptr; |
| 341 | |
| 342 | static IDACInfo Fail() { return IDACInfo{.Failed: true, .ExprKind: Invalid, .X_Var: nullptr}; }; |
| 343 | }; |
| 344 | |
| 345 | // Helper for CheckIncDecAssignCompoundAssign, does checks for inc/dec. |
| 346 | IDACInfo CheckIncDec(UnaryOpInfo Inf) { |
| 347 | |
| 348 | if (!UnaryOperator::isIncrementDecrementOp(Op: Inf.Operator)) { |
| 349 | DiagnoseInvalidAtomic( |
| 350 | Loc: Inf.FoundExpr->getExprLoc(), |
| 351 | NoteDiag: SemaRef.PDiag(DiagID: diag::note_acc_atomic_unsupported_unary_operator)); |
| 352 | return IDACInfo::Fail(); |
| 353 | } |
| 354 | bool Failed = CheckOperandVariable( |
| 355 | E: Inf.SubExpr, |
| 356 | PD: SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 357 | << /*none=*/2 |
| 358 | << (Inf.IsIncrementOp() ? diag::OACCAtomicOpKind::Inc |
| 359 | : diag::OACCAtomicOpKind::Dec)); |
| 360 | // For increment/decrements, the subexpr is the 'x' (x++, ++x, etc). |
| 361 | return IDACInfo{.Failed: Failed, .ExprKind: IDACInfo::Unary, .X_Var: 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 expr |
| 367 | // binop x. |
| 368 | // If AllowSimpleAssign, also allows v = x; |
| 369 | IDACInfo CheckAssignmentWithBinOpOnRHS(BinaryOpInfo AssignInf, |
| 370 | SimpleAssignKind SAK) { |
| 371 | PartialDiagnostic PD = |
| 372 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 373 | << /*left=*/0 << diag::OACCAtomicOpKind::Assign; |
| 374 | if (CheckOperandVariable(E: AssignInf.LHS, PD)) |
| 375 | return IDACInfo::Fail(); |
| 376 | |
| 377 | std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(E: AssignInf.RHS); |
| 378 | |
| 379 | if (!BinInf) { |
| 380 | |
| 381 | // Capture in a compound statement allows v = x assignment. So make sure |
| 382 | // we permit that here. |
| 383 | if (SAK != SimpleAssignKind::None) { |
| 384 | PartialDiagnostic PD = |
| 385 | SemaRef.PDiag(DiagID: 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{.Failed: CheckOperandVariable(E: AssignInf.RHS, PD), |
| 390 | .ExprKind: IDACInfo::SimpleAssign, .X_Var: AssignInf.RHS}; |
| 391 | } |
| 392 | assert(SAK == SimpleAssignKind::Expr); |
| 393 | // In the expression version, supported by v=x; x = expr;, we need to |
| 394 | // set to the LHS here. |
| 395 | return IDACInfo{.Failed: CheckOperandExpr(E: AssignInf.RHS, PD), |
| 396 | .ExprKind: IDACInfo::ExprAssign, .X_Var: AssignInf.LHS}; |
| 397 | } |
| 398 | |
| 399 | DiagnoseInvalidAtomic( |
| 400 | Loc: AssignInf.RHS->getExprLoc(), |
| 401 | NoteDiag: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expected_binop)); |
| 402 | |
| 403 | return IDACInfo::Fail(); |
| 404 | } |
| 405 | switch (BinInf->Operator) { |
| 406 | default: |
| 407 | DiagnoseInvalidAtomic( |
| 408 | Loc: BinInf->FoundExpr->getExprLoc(), |
| 409 | NoteDiag: SemaRef.PDiag(DiagID: 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(ID&: LHS_ID, Context: SemaRef.getASTContext(), |
| 427 | /*Canonical=*/true); |
| 428 | BinInf->LHS->Profile(ID&: InnerLHS_ID, Context: 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 | .Failed: CheckOperandExpr( |
| 436 | E: BinInf->RHS, |
| 437 | PD: SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar |
| 438 | << /*right=*/1 |
| 439 | << diag::OACCAtomicOpKind::CompoundAssign)), |
| 440 | .ExprKind: IDACInfo::AssignBinOp, .X_Var: AssignInf.LHS}; |
| 441 | |
| 442 | BinInf->RHS->Profile(ID&: InnerRHS_ID, Context: SemaRef.getASTContext(), |
| 443 | /*Canonical=*/true); |
| 444 | // This is X = expr binop X; |
| 445 | // Check the LHS is an expression |
| 446 | if (LHS_ID == InnerRHS_ID) |
| 447 | return IDACInfo{ |
| 448 | .Failed: CheckOperandExpr( |
| 449 | E: BinInf->LHS, |
| 450 | PD: SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 451 | << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign), |
| 452 | .ExprKind: IDACInfo::AssignBinOp, .X_Var: AssignInf.LHS}; |
| 453 | |
| 454 | // If nothing matches, error out. |
| 455 | DiagnoseInvalidAtomic(Loc: BinInf->FoundExpr->getExprLoc(), |
| 456 | NoteDiag: SemaRef.PDiag(DiagID: diag::note_acc_atomic_mismatch_operand) |
| 457 | << const_cast<Expr *>(AssignInf.LHS) |
| 458 | << const_cast<Expr *>(BinInf->LHS) |
| 459 | << const_cast<Expr *>(BinInf->RHS)); |
| 460 | return IDACInfo::Fail(); |
| 461 | } |
| 462 | |
| 463 | // Ensures that the expression is an increment/decrement, an assignment, or a |
| 464 | // compound assignment. If its an assignment, allows the x binop expr/x binop |
| 465 | // expr syntax. If it is a compound-assignment, allows any expr on the RHS. |
| 466 | IDACInfo CheckIncDecAssignCompoundAssign(const Expr *E, |
| 467 | SimpleAssignKind SAK) { |
| 468 | std::optional<UnaryOpInfo> UInf = GetUnaryOperatorInfo(E); |
| 469 | |
| 470 | // If this is a unary operator, only increment/decrement are allowed, so get |
| 471 | // unary operator, then check everything we can. |
| 472 | if (UInf) |
| 473 | return CheckIncDec(Inf: *UInf); |
| 474 | |
| 475 | std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(E); |
| 476 | |
| 477 | // Unary or binary operator were the only choices, so error here. |
| 478 | if (!BinInf) { |
| 479 | DiagnoseInvalidAtomic(Loc: E->getExprLoc(), |
| 480 | NoteDiag: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 481 | << diag::OACCAtomicExpr::UnaryCompAssign); |
| 482 | return IDACInfo::Fail(); |
| 483 | } |
| 484 | |
| 485 | switch (BinInf->Operator) { |
| 486 | default: |
| 487 | DiagnoseInvalidAtomic( |
| 488 | Loc: BinInf->FoundExpr->getExprLoc(), |
| 489 | NoteDiag: SemaRef.PDiag( |
| 490 | DiagID: diag::note_acc_atomic_unsupported_compound_binary_operator)); |
| 491 | return IDACInfo::Fail(); |
| 492 | case BO_Assign: |
| 493 | return CheckAssignmentWithBinOpOnRHS(AssignInf: *BinInf, SAK); |
| 494 | case BO_AddAssign: |
| 495 | case BO_MulAssign: |
| 496 | case BO_SubAssign: |
| 497 | case BO_DivAssign: |
| 498 | case BO_AndAssign: |
| 499 | case BO_XorAssign: |
| 500 | case BO_OrAssign: |
| 501 | case BO_ShlAssign: |
| 502 | case BO_ShrAssign: { |
| 503 | PartialDiagnostic LPD = |
| 504 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 505 | << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign; |
| 506 | PartialDiagnostic RPD = |
| 507 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 508 | << /*right=*/1 << diag::OACCAtomicOpKind::CompoundAssign; |
| 509 | // nothing to do other than check the variable expressions. |
| 510 | // success or failure |
| 511 | bool Failed = CheckOperandVariable(E: BinInf->LHS, PD: LPD) || |
| 512 | CheckOperandExpr(E: BinInf->RHS, PD: RPD); |
| 513 | |
| 514 | return IDACInfo{.Failed: Failed, .ExprKind: IDACInfo::CompoundAssign, .X_Var: BinInf->LHS}; |
| 515 | } |
| 516 | } |
| 517 | llvm_unreachable("all binary operator kinds should be checked above" ); |
| 518 | } |
| 519 | |
| 520 | StmtResult CheckRead() { |
| 521 | Expr *AssocExpr = RequireExpr( |
| 522 | Stmt: AssocStmt.get(), ExpectedNote: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 523 | << diag::OACCAtomicExpr::Assign); |
| 524 | |
| 525 | if (!AssocExpr) |
| 526 | return getRecoveryExpr(); |
| 527 | |
| 528 | std::optional<BinaryOpInfo> AssignRes = CheckAssignment(E: AssocExpr); |
| 529 | if (!AssignRes) |
| 530 | return getRecoveryExpr(); |
| 531 | |
| 532 | PartialDiagnostic PD = |
| 533 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 534 | << /*right=*/1 << diag::OACCAtomicOpKind::Assign; |
| 535 | |
| 536 | // Finally, check the RHS. |
| 537 | if (CheckOperandVariable(E: AssignRes->RHS, PD)) |
| 538 | return getRecoveryExpr(); |
| 539 | |
| 540 | return AssocStmt; |
| 541 | } |
| 542 | |
| 543 | StmtResult CheckWrite() { |
| 544 | Expr *AssocExpr = RequireExpr( |
| 545 | Stmt: AssocStmt.get(), ExpectedNote: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 546 | << diag::OACCAtomicExpr::Assign); |
| 547 | |
| 548 | if (!AssocExpr) |
| 549 | return getRecoveryExpr(); |
| 550 | |
| 551 | std::optional<BinaryOpInfo> AssignRes = CheckAssignment(E: AssocExpr); |
| 552 | if (!AssignRes) |
| 553 | return getRecoveryExpr(); |
| 554 | |
| 555 | PartialDiagnostic PD = |
| 556 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 557 | << /*right=*/1 << diag::OACCAtomicOpKind::Assign; |
| 558 | |
| 559 | // Finally, check the RHS. |
| 560 | if (CheckOperandExpr(E: AssignRes->RHS, PD)) |
| 561 | return getRecoveryExpr(); |
| 562 | |
| 563 | return AssocStmt; |
| 564 | } |
| 565 | |
| 566 | StmtResult CheckUpdate() { |
| 567 | Expr *AssocExpr = RequireExpr( |
| 568 | Stmt: AssocStmt.get(), ExpectedNote: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 569 | << diag::OACCAtomicExpr::UnaryCompAssign); |
| 570 | |
| 571 | if (!AssocExpr || |
| 572 | CheckIncDecAssignCompoundAssign(E: AssocExpr, SAK: SimpleAssignKind::None) |
| 573 | .Failed) |
| 574 | return getRecoveryExpr(); |
| 575 | |
| 576 | return AssocStmt; |
| 577 | } |
| 578 | |
| 579 | bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX, |
| 580 | IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) { |
| 581 | llvm::FoldingSetNodeID First_ID, Second_ID; |
| 582 | FirstX->Profile(ID&: First_ID, Context: SemaRef.getASTContext(), /*Canonical=*/true); |
| 583 | SecondX->Profile(ID&: Second_ID, Context: SemaRef.getASTContext(), /*Canonical=*/true); |
| 584 | |
| 585 | if (First_ID == Second_ID) |
| 586 | return false; |
| 587 | |
| 588 | PartialDiagnostic PD = |
| 589 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_mismatch_compound_operand) |
| 590 | << FirstKind << const_cast<Expr *>(FirstX) << SecondKind |
| 591 | << const_cast<Expr *>(SecondX); |
| 592 | |
| 593 | return DiagnoseInvalidAtomic(Loc: SecondX->getExprLoc(), NoteDiag: PD); |
| 594 | } |
| 595 | |
| 596 | StmtResult CheckCapture() { |
| 597 | if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(Val: AssocStmt.get())) { |
| 598 | auto *const *BodyItr = CmpdStmt->body().begin(); |
| 599 | PartialDiagnostic PD = SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 600 | << diag::OACCAtomicExpr::UnaryCompAssign; |
| 601 | // If we don't have at least 1 statement, error. |
| 602 | if (BodyItr == CmpdStmt->body().end()) { |
| 603 | DiagnoseInvalidAtomic(Loc: CmpdStmt->getBeginLoc(), NoteDiag: PD); |
| 604 | return getRecoveryExpr(); |
| 605 | } |
| 606 | |
| 607 | // First Expr can be inc/dec, assign, or compound assign. |
| 608 | Expr *FirstExpr = RequireExpr(Stmt: *BodyItr, ExpectedNote: PD); |
| 609 | if (!FirstExpr) |
| 610 | return getRecoveryExpr(); |
| 611 | |
| 612 | IDACInfo FirstExprResults = |
| 613 | CheckIncDecAssignCompoundAssign(E: FirstExpr, SAK: SimpleAssignKind::Var); |
| 614 | if (FirstExprResults.Failed) |
| 615 | return getRecoveryExpr(); |
| 616 | |
| 617 | ++BodyItr; |
| 618 | |
| 619 | // If we don't have second statement, error. |
| 620 | if (BodyItr == CmpdStmt->body().end()) { |
| 621 | DiagnoseInvalidAtomic(Loc: CmpdStmt->getEndLoc(), NoteDiag: PD); |
| 622 | return getRecoveryExpr(); |
| 623 | } |
| 624 | |
| 625 | Expr *SecondExpr = RequireExpr(Stmt: *BodyItr, ExpectedNote: PD); |
| 626 | if (!SecondExpr) |
| 627 | return getRecoveryExpr(); |
| 628 | |
| 629 | assert(FirstExprResults.ExprKind != IDACInfo::Invalid); |
| 630 | |
| 631 | switch (FirstExprResults.ExprKind) { |
| 632 | case IDACInfo::Invalid: |
| 633 | case IDACInfo::ExprAssign: |
| 634 | llvm_unreachable("Should have error'ed out by now" ); |
| 635 | case IDACInfo::Unary: |
| 636 | case IDACInfo::CompoundAssign: |
| 637 | case IDACInfo::AssignBinOp: { |
| 638 | // Everything but simple-assign can only be followed by a simple |
| 639 | // assignment. |
| 640 | std::optional<BinaryOpInfo> AssignRes = CheckAssignment(E: SecondExpr); |
| 641 | if (!AssignRes) |
| 642 | return getRecoveryExpr(); |
| 643 | |
| 644 | PartialDiagnostic PD = |
| 645 | SemaRef.PDiag(DiagID: diag::note_acc_atomic_operand_lvalue_scalar) |
| 646 | << /*right=*/1 << diag::OACCAtomicOpKind::Assign; |
| 647 | |
| 648 | if (CheckOperandVariable(E: AssignRes->RHS, PD)) |
| 649 | return getRecoveryExpr(); |
| 650 | |
| 651 | if (CheckVarRefsSame(FirstKind: FirstExprResults.ExprKind, FirstX: FirstExprResults.X_Var, |
| 652 | SecondKind: IDACInfo::SimpleAssign, SecondX: AssignRes->RHS)) |
| 653 | return getRecoveryExpr(); |
| 654 | break; |
| 655 | } |
| 656 | case IDACInfo::SimpleAssign: { |
| 657 | // If the first was v = x, anything but simple expression is allowed. |
| 658 | IDACInfo SecondExprResults = |
| 659 | CheckIncDecAssignCompoundAssign(E: SecondExpr, SAK: SimpleAssignKind::Expr); |
| 660 | if (SecondExprResults.Failed) |
| 661 | return getRecoveryExpr(); |
| 662 | |
| 663 | if (CheckVarRefsSame(FirstKind: FirstExprResults.ExprKind, FirstX: FirstExprResults.X_Var, |
| 664 | SecondKind: SecondExprResults.ExprKind, |
| 665 | SecondX: SecondExprResults.X_Var)) |
| 666 | return getRecoveryExpr(); |
| 667 | break; |
| 668 | } |
| 669 | } |
| 670 | ++BodyItr; |
| 671 | if (BodyItr != CmpdStmt->body().end()) { |
| 672 | DiagnoseInvalidAtomic( |
| 673 | Loc: (*BodyItr)->getBeginLoc(), |
| 674 | NoteDiag: SemaRef.PDiag(DiagID: diag::note_acc_atomic_too_many_stmts)); |
| 675 | return getRecoveryExpr(); |
| 676 | } |
| 677 | } else { |
| 678 | // This check doesn't need to happen if it is a compound stmt. |
| 679 | Expr *AssocExpr = RequireExpr( |
| 680 | Stmt: AssocStmt.get(), ExpectedNote: SemaRef.PDiag(DiagID: diag::note_acc_atomic_expr_must_be) |
| 681 | << diag::OACCAtomicExpr::Assign); |
| 682 | if (!AssocExpr) |
| 683 | return getRecoveryExpr(); |
| 684 | |
| 685 | // First, we require an assignment. |
| 686 | std::optional<BinaryOpInfo> AssignRes = CheckAssignment(E: AssocExpr); |
| 687 | |
| 688 | if (!AssignRes) |
| 689 | return getRecoveryExpr(); |
| 690 | |
| 691 | if (CheckIncDecAssignCompoundAssign(E: AssignRes->RHS, |
| 692 | SAK: SimpleAssignKind::None) |
| 693 | .Failed) |
| 694 | return getRecoveryExpr(); |
| 695 | } |
| 696 | |
| 697 | return AssocStmt; |
| 698 | } |
| 699 | |
| 700 | public: |
| 701 | AtomicOperandChecker(SemaOpenACC &S, OpenACCAtomicKind AtKind, |
| 702 | SourceLocation DirLoc, StmtResult AssocStmt) |
| 703 | : SemaRef(S), AtKind(AtKind), AtomicDirLoc(DirLoc), AssocStmt(AssocStmt) { |
| 704 | } |
| 705 | |
| 706 | StmtResult Check() { |
| 707 | |
| 708 | switch (AtKind) { |
| 709 | case OpenACCAtomicKind::Read: |
| 710 | return CheckRead(); |
| 711 | case OpenACCAtomicKind::Write: |
| 712 | return CheckWrite(); |
| 713 | case OpenACCAtomicKind::None: |
| 714 | case OpenACCAtomicKind::Update: |
| 715 | return CheckUpdate(); |
| 716 | case OpenACCAtomicKind::Capture: |
| 717 | return CheckCapture(); |
| 718 | } |
| 719 | llvm_unreachable("Unhandled atomic kind?" ); |
| 720 | } |
| 721 | }; |
| 722 | } // namespace |
| 723 | |
| 724 | StmtResult SemaOpenACC::CheckAtomicAssociatedStmt(SourceLocation AtomicDirLoc, |
| 725 | OpenACCAtomicKind AtKind, |
| 726 | StmtResult AssocStmt) { |
| 727 | if (!AssocStmt.isUsable()) |
| 728 | return AssocStmt; |
| 729 | |
| 730 | if (isa<RecoveryExpr>(Val: AssocStmt.get())) |
| 731 | return AssocStmt; |
| 732 | |
| 733 | AtomicOperandChecker Checker{*this, AtKind, AtomicDirLoc, AssocStmt}; |
| 734 | return Checker.Check(); |
| 735 | } |
| 736 | |