clang 22.0.0git
SemaOpenACCAtomic.cpp
Go to the documentation of this file.
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"
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>(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(),
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>(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, 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>(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 sure
116 // 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 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>(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 overloaded
172 // 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 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(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 = 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{true, Invalid, nullptr}; };
343 };
344
345 // Helper for CheckIncDecAssignCompoundAssign, does checks for inc/dec.
346 IDACInfo CheckIncDec(UnaryOpInfo Inf) {
347
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=*/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, 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 expr
367 // binop x.
368 // If AllowSimpleAssign, also allows v = x;
369 IDACInfo CheckAssignmentWithBinOpOnRHS(BinaryOpInfo AssignInf,
370 SimpleAssignKind SAK) {
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 sure
382 // we permit that here.
383 if (SAK != SimpleAssignKind::None) {
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 to
394 // 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_scalar
438 << /*right=*/1
439 << 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 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(*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(*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: {
504 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
505 << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign;
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(BinInf->LHS, LPD) ||
512 CheckOperandExpr(BinInf->RHS, RPD);
513
514 return IDACInfo{Failed, IDACInfo::CompoundAssign, 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(AssocExpr);
529 if (!AssignRes)
530 return getRecoveryExpr();
531
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(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(AssocExpr);
552 if (!AssignRes)
553 return getRecoveryExpr();
554
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(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(AssocExpr, SimpleAssignKind::None)
573 .Failed)
574 return getRecoveryExpr();
575
576 return AssocStmt;
577 }
578
579 const Expr *IgnoreBeforeCompare(const Expr *E) {
581 SemaRef.getASTContext());
582 }
583
584 bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,
585 IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {
586 llvm::FoldingSetNodeID First_ID, Second_ID;
587 FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);
588 SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);
589
590 if (First_ID == Second_ID)
591 return false;
592
594 SemaRef.PDiag(diag::note_acc_atomic_mismatch_compound_operand)
595 << FirstKind << const_cast<Expr *>(FirstX) << SecondKind
596 << const_cast<Expr *>(SecondX);
597
598 return DiagnoseInvalidAtomic(SecondX->getExprLoc(), PD);
599 }
600
601 StmtResult CheckCapture() {
602 if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(AssocStmt.get())) {
603 auto *const *BodyItr = CmpdStmt->body().begin();
604 PartialDiagnostic PD = SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
605 << diag::OACCAtomicExpr::UnaryCompAssign;
606 // If we don't have at least 1 statement, error.
607 if (BodyItr == CmpdStmt->body().end()) {
608 DiagnoseInvalidAtomic(CmpdStmt->getBeginLoc(), PD);
609 return getRecoveryExpr();
610 }
611
612 // First Expr can be inc/dec, assign, or compound assign.
613 Expr *FirstExpr = RequireExpr(*BodyItr, PD);
614 if (!FirstExpr)
615 return getRecoveryExpr();
616
617 IDACInfo FirstExprResults =
618 CheckIncDecAssignCompoundAssign(FirstExpr, SimpleAssignKind::Var);
619 if (FirstExprResults.Failed)
620 return getRecoveryExpr();
621
622 ++BodyItr;
623
624 // If we don't have second statement, error.
625 if (BodyItr == CmpdStmt->body().end()) {
626 DiagnoseInvalidAtomic(CmpdStmt->getEndLoc(), PD);
627 return getRecoveryExpr();
628 }
629
630 Expr *SecondExpr = RequireExpr(*BodyItr, PD);
631 if (!SecondExpr)
632 return getRecoveryExpr();
633
634 assert(FirstExprResults.ExprKind != IDACInfo::Invalid);
635
636 switch (FirstExprResults.ExprKind) {
637 case IDACInfo::Invalid:
638 case IDACInfo::ExprAssign:
639 llvm_unreachable("Should have error'ed out by now");
640 case IDACInfo::Unary:
641 case IDACInfo::CompoundAssign:
642 case IDACInfo::AssignBinOp: {
643 // Everything but simple-assign can only be followed by a simple
644 // assignment.
645 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(SecondExpr);
646 if (!AssignRes)
647 return getRecoveryExpr();
648
650 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
651 << /*right=*/1 << diag::OACCAtomicOpKind::Assign;
652
653 if (CheckOperandVariable(AssignRes->RHS, PD))
654 return getRecoveryExpr();
655
656 if (CheckVarRefsSame(FirstExprResults.ExprKind,
657 IgnoreBeforeCompare(FirstExprResults.X_Var),
658 IDACInfo::SimpleAssign,
659 IgnoreBeforeCompare(AssignRes->RHS)))
660 return getRecoveryExpr();
661 break;
662 }
663 case IDACInfo::SimpleAssign: {
664 // If the first was v = x, anything but simple expression is allowed.
665 IDACInfo SecondExprResults =
666 CheckIncDecAssignCompoundAssign(SecondExpr, SimpleAssignKind::Expr);
667 if (SecondExprResults.Failed)
668 return getRecoveryExpr();
669
670 if (CheckVarRefsSame(FirstExprResults.ExprKind,
671 IgnoreBeforeCompare(FirstExprResults.X_Var),
672 SecondExprResults.ExprKind,
673 IgnoreBeforeCompare(SecondExprResults.X_Var)))
674 return getRecoveryExpr();
675 break;
676 }
677 }
678 ++BodyItr;
679 if (BodyItr != CmpdStmt->body().end()) {
680 DiagnoseInvalidAtomic(
681 (*BodyItr)->getBeginLoc(),
682 SemaRef.PDiag(diag::note_acc_atomic_too_many_stmts));
683 return getRecoveryExpr();
684 }
685 } else {
686 // This check doesn't need to happen if it is a compound stmt.
687 Expr *AssocExpr = RequireExpr(
688 AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
689 << diag::OACCAtomicExpr::Assign);
690 if (!AssocExpr)
691 return getRecoveryExpr();
692
693 // First, we require an assignment.
694 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
695
696 if (!AssignRes)
697 return getRecoveryExpr();
698
699 if (CheckIncDecAssignCompoundAssign(AssignRes->RHS,
700 SimpleAssignKind::None)
701 .Failed)
702 return getRecoveryExpr();
703 }
704
705 return AssocStmt;
706 }
707
708public:
709 AtomicOperandChecker(SemaOpenACC &S, OpenACCAtomicKind AtKind,
710 SourceLocation DirLoc, StmtResult AssocStmt)
711 : SemaRef(S), AtKind(AtKind), AtomicDirLoc(DirLoc), AssocStmt(AssocStmt) {
712 }
713
714 StmtResult Check() {
715
716 switch (AtKind) {
717 case OpenACCAtomicKind::Read:
718 return CheckRead();
719 case OpenACCAtomicKind::Write:
720 return CheckWrite();
721 case OpenACCAtomicKind::None:
722 case OpenACCAtomicKind::Update:
723 return CheckUpdate();
724 case OpenACCAtomicKind::Capture:
725 return CheckCapture();
726 }
727 llvm_unreachable("Unhandled atomic kind?");
728 }
729};
730} // namespace
731
733 OpenACCAtomicKind AtKind,
734 StmtResult AssocStmt) {
735 if (!AssocStmt.isUsable())
736 return AssocStmt;
737
738 if (isa<RecoveryExpr>(AssocStmt.get()))
739 return AssocStmt;
740
741 AtomicOperandChecker Checker{*this, AtKind, AtomicDirLoc, AssocStmt};
742 return Checker.Check();
743}
Expr * E
Defines the clang::Expr interface and subclasses for C++ expressions.
@ None
SourceLocation Loc
Definition: SemaObjC.cpp:754
This file declares semantic analysis for OpenACC constructs and clauses.
CanQualType DependentTy
Definition: ASTContext.h:1250
const LangOptions & getLangOpts() const
Definition: ASTContext.h:894
PtrTy get() const
Definition: Ownership.h:171
bool isUsable() const
Definition: Ownership.h:169
This represents one expression.
Definition: Expr.h:112
Expr * IgnoreParenNoopCasts(const ASTContext &Ctx) LLVM_READONLY
Skip past any parentheses and casts which do not change the value (including ptr->int casts of the sa...
Definition: Expr.cpp:3100
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:3073
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Definition: Expr.h:284
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3053
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:273
QualType getType() const
Definition: Expr.h:144
A (possibly-)qualified type.
Definition: TypeBase.h:937
static RecoveryExpr * Create(ASTContext &Ctx, QualType T, SourceLocation BeginLoc, SourceLocation EndLoc, ArrayRef< Expr * > SubExprs)
Definition: Expr.cpp:5265
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:61
PartialDiagnostic PDiag(unsigned DiagID=0)
Build a partial diagnostic.
Definition: SemaBase.cpp:33
ASTContext & getASTContext() const
Definition: SemaBase.cpp:9
StmtResult CheckAtomicAssociatedStmt(SourceLocation AtomicDirLoc, OpenACCAtomicKind AtKind, StmtResult AssocStmt)
Called to check the form of the atomic construct which has some fairly sizable restrictions.
Encodes a location in the source.
Stmt - This represents one statement.
Definition: Stmt.h:85
SourceLocation getEndLoc() const LLVM_READONLY
Definition: Stmt.cpp:358
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical, bool ProfileLambdaExpr=false) const
Produce a unique representation of the given statement.
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:346
bool isScalarType() const
Definition: TypeBase.h:9038
bool isInstantiationDependentType() const
Determine whether this type is an instantiation-dependent type, meaning that the type involves a temp...
Definition: TypeBase.h:2808
bool isIncrementDecrementOp() const
Definition: Expr.h:2343
The JSON file list parser is used to communicate input to InstallAPI.
OpenACCAtomicKind
Definition: OpenACCKinds.h:172
BinaryOperatorKind
StmtResult StmtError()
Definition: Ownership.h:266
UnaryOperatorKind
const FunctionProtoType * T