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
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. 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(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 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(E->getExprLoc(),
79 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{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(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{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(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(Loc: E->getExprLoc(),
299 NoteDiag: SemaRef.PDiag(diag::DiagID: 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(diag::DiagID: 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(diag::DiagID: 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(diag::DiagID: note_acc_atomic_unsupported_unary_operator));
352 return IDACInfo::Fail();
353 }
354 bool Failed = CheckOperandVariable(
355 E: Inf.SubExpr,
356 PD: SemaRef.PDiag(diag::DiagID: 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(diag::DiagID: 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(diag::DiagID: 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(diag::DiagID: 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(diag::DiagID: 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 CheckOperandExpr(
436 BinInf->RHS,
437 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar
438 << /*right=*/1
439 << diag::OACCAtomicOpKind::CompoundAssign)),
440 IDACInfo::AssignBinOp, 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 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 << 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(E->getExprLoc(),
480 SemaRef.PDiag(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 BinInf->FoundExpr->getExprLoc(),
489 SemaRef.PDiag(
490 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(diag::note_acc_atomic_operand_lvalue_scalar)
505 << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign;
506 PartialDiagnostic RPD =
507 SemaRef.PDiag(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 AssocStmt.get(), SemaRef.PDiag(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(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 AssocStmt.get(), SemaRef.PDiag(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(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 AssocStmt.get(), SemaRef.PDiag(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(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(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(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 (*BodyItr)->getBeginLoc(),
674 SemaRef.PDiag(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 AssocStmt.get(), SemaRef.PDiag(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
700public:
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
724StmtResult 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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/lib/Sema/SemaOpenACCAtomic.cpp