clang 24.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(),
53 E ? ArrayRef<Expr *>{E} : ArrayRef<Expr *>{});
54 }
55
56 // OpenACC 3.3 2.12: 'expr' is an expression with scalar type.
57 bool CheckOperandExpr(const Expr *E, PartialDiagnostic PD) {
58 QualType ExprTy = E->getType();
59
60 // Scalar allowed, plus we allow instantiation dependent to 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) {
371 PartialDiagnostic PD =
372 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
373 << /*left=*/0 << diag::OACCAtomicOpKind::Assign;
374 if (CheckOperandVariable(AssignInf.LHS, PD))
375 return IDACInfo::Fail();
376
377 std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(AssignInf.RHS);
378
379 if (!BinInf) {
380
381 // Capture in a compound statement allows v = x assignment. So make sure
382 // we permit that here.
383 if (SAK != SimpleAssignKind::None) {
384 PartialDiagnostic PD =
385 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
386 << /*right=*/1 << diag::OACCAtomicOpKind::Assign;
387 if (SAK == SimpleAssignKind::Var) {
388 // In the var version, everywhere we allow v = x;, X is the RHS.
389 return IDACInfo{CheckOperandVariable(AssignInf.RHS, PD),
390 IDACInfo::SimpleAssign, AssignInf.RHS};
391 }
392 assert(SAK == SimpleAssignKind::Expr);
393 // In the expression version, supported by v=x; x = expr;, we need 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 << diag::OACCAtomicOpKind::CompoundAssign),
439 IDACInfo::AssignBinOp, AssignInf.LHS};
440
441 BinInf->RHS->Profile(InnerRHS_ID, SemaRef.getASTContext(),
442 /*Canonical=*/true);
443 // This is X = expr binop X;
444 // Check the LHS is an expression
445 if (LHS_ID == InnerRHS_ID)
446 return IDACInfo{
447 CheckOperandExpr(
448 BinInf->LHS,
449 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
450 << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign),
451 IDACInfo::AssignBinOp, AssignInf.LHS};
452
453 // If nothing matches, error out.
454 DiagnoseInvalidAtomic(BinInf->FoundExpr->getExprLoc(),
455 SemaRef.PDiag(diag::note_acc_atomic_mismatch_operand)
456 << AssignInf.LHS << BinInf->LHS << BinInf->RHS);
457 return IDACInfo::Fail();
458 }
459
460 // Ensures that the expression is an increment/decrement, an assignment, or a
461 // compound assignment. If its an assignment, allows the x binop expr/x binop
462 // expr syntax. If it is a compound-assignment, allows any expr on the RHS.
463 IDACInfo CheckIncDecAssignCompoundAssign(const Expr *E,
464 SimpleAssignKind SAK) {
465 std::optional<UnaryOpInfo> UInf = GetUnaryOperatorInfo(E);
466
467 // If this is a unary operator, only increment/decrement are allowed, so get
468 // unary operator, then check everything we can.
469 if (UInf)
470 return CheckIncDec(*UInf);
471
472 std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(E);
473
474 // Unary or binary operator were the only choices, so error here.
475 if (!BinInf) {
476 DiagnoseInvalidAtomic(E->getExprLoc(),
477 SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
478 << diag::OACCAtomicExpr::UnaryCompAssign);
479 return IDACInfo::Fail();
480 }
481
482 switch (BinInf->Operator) {
483 default:
484 DiagnoseInvalidAtomic(
485 BinInf->FoundExpr->getExprLoc(),
486 SemaRef.PDiag(
487 diag::note_acc_atomic_unsupported_compound_binary_operator));
488 return IDACInfo::Fail();
489 case BO_Assign:
490 return CheckAssignmentWithBinOpOnRHS(*BinInf, SAK);
491 case BO_AddAssign:
492 case BO_MulAssign:
493 case BO_SubAssign:
494 case BO_DivAssign:
495 case BO_AndAssign:
496 case BO_XorAssign:
497 case BO_OrAssign:
498 case BO_ShlAssign:
499 case BO_ShrAssign: {
500 PartialDiagnostic LPD =
501 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
502 << /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign;
503 PartialDiagnostic RPD =
504 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
505 << /*right=*/1 << diag::OACCAtomicOpKind::CompoundAssign;
506 // nothing to do other than check the variable expressions.
507 // success or failure
508 bool Failed = CheckOperandVariable(BinInf->LHS, LPD) ||
509 CheckOperandExpr(BinInf->RHS, RPD);
510
511 return IDACInfo{Failed, IDACInfo::CompoundAssign, BinInf->LHS};
512 }
513 }
514 llvm_unreachable("all binary operator kinds should be checked above");
515 }
516
517 StmtResult CheckRead() {
518 Expr *AssocExpr = RequireExpr(
519 AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
520 << diag::OACCAtomicExpr::Assign);
521
522 if (!AssocExpr)
523 return getRecoveryExpr();
524
525 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
526 if (!AssignRes)
527 return getRecoveryExpr();
528
529 PartialDiagnostic PD =
530 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
531 << /*right=*/1 << diag::OACCAtomicOpKind::Assign;
532
533 // Finally, check the RHS.
534 if (CheckOperandVariable(AssignRes->RHS, PD))
535 return getRecoveryExpr();
536
537 return AssocStmt;
538 }
539
540 StmtResult CheckWrite() {
541 Expr *AssocExpr = RequireExpr(
542 AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
543 << diag::OACCAtomicExpr::Assign);
544
545 if (!AssocExpr)
546 return getRecoveryExpr();
547
548 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
549 if (!AssignRes)
550 return getRecoveryExpr();
551
552 PartialDiagnostic PD =
553 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
554 << /*right=*/1 << diag::OACCAtomicOpKind::Assign;
555
556 // Finally, check the RHS.
557 if (CheckOperandExpr(AssignRes->RHS, PD))
558 return getRecoveryExpr();
559
560 return AssocStmt;
561 }
562
563 StmtResult CheckUpdate() {
564 Expr *AssocExpr = RequireExpr(
565 AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
566 << diag::OACCAtomicExpr::UnaryCompAssign);
567
568 if (!AssocExpr ||
569 CheckIncDecAssignCompoundAssign(AssocExpr, SimpleAssignKind::None)
570 .Failed)
571 return getRecoveryExpr();
572
573 return AssocStmt;
574 }
575
576 const Expr *IgnoreBeforeCompare(const Expr *E) {
578 SemaRef.getASTContext());
579 }
580
581 bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,
582 IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {
583 llvm::FoldingSetNodeID First_ID, Second_ID;
584 FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);
585 SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);
586
587 if (First_ID == Second_ID)
588 return false;
589
590 PartialDiagnostic PD =
591 SemaRef.PDiag(diag::note_acc_atomic_mismatch_compound_operand)
592 << FirstKind << FirstX << SecondKind << SecondX;
593
594 return DiagnoseInvalidAtomic(SecondX->getExprLoc(), PD);
595 }
596
597 StmtResult CheckCapture() {
598 if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(AssocStmt.get())) {
599 auto *const *BodyItr = CmpdStmt->body().begin();
600 PartialDiagnostic PD = SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
601 << diag::OACCAtomicExpr::UnaryCompAssign;
602 // If we don't have at least 1 statement, error.
603 if (BodyItr == CmpdStmt->body().end()) {
604 DiagnoseInvalidAtomic(CmpdStmt->getBeginLoc(), PD);
605 return getRecoveryExpr();
606 }
607
608 // First Expr can be inc/dec, assign, or compound assign.
609 Expr *FirstExpr = RequireExpr(*BodyItr, PD);
610 if (!FirstExpr)
611 return getRecoveryExpr();
612
613 IDACInfo FirstExprResults =
614 CheckIncDecAssignCompoundAssign(FirstExpr, SimpleAssignKind::Var);
615 if (FirstExprResults.Failed)
616 return getRecoveryExpr();
617
618 ++BodyItr;
619
620 // If we don't have second statement, error.
621 if (BodyItr == CmpdStmt->body().end()) {
622 DiagnoseInvalidAtomic(CmpdStmt->getEndLoc(), PD);
623 return getRecoveryExpr();
624 }
625
626 Expr *SecondExpr = RequireExpr(*BodyItr, PD);
627 if (!SecondExpr)
628 return getRecoveryExpr();
629
630 assert(FirstExprResults.ExprKind != IDACInfo::Invalid);
631
632 switch (FirstExprResults.ExprKind) {
633 case IDACInfo::Invalid:
634 case IDACInfo::ExprAssign:
635 llvm_unreachable("Should have error'ed out by now");
636 case IDACInfo::Unary:
637 case IDACInfo::CompoundAssign:
638 case IDACInfo::AssignBinOp: {
639 // Everything but simple-assign can only be followed by a simple
640 // assignment.
641 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(SecondExpr);
642 if (!AssignRes)
643 return getRecoveryExpr();
644
645 PartialDiagnostic PD =
646 SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
647 << /*right=*/1 << diag::OACCAtomicOpKind::Assign;
648
649 if (CheckOperandVariable(AssignRes->RHS, PD))
650 return getRecoveryExpr();
651
652 if (CheckVarRefsSame(FirstExprResults.ExprKind,
653 IgnoreBeforeCompare(FirstExprResults.X_Var),
654 IDACInfo::SimpleAssign,
655 IgnoreBeforeCompare(AssignRes->RHS)))
656 return getRecoveryExpr();
657 break;
658 }
659 case IDACInfo::SimpleAssign: {
660 // If the first was v = x, anything but simple expression is allowed.
661 IDACInfo SecondExprResults =
662 CheckIncDecAssignCompoundAssign(SecondExpr, SimpleAssignKind::Expr);
663 if (SecondExprResults.Failed)
664 return getRecoveryExpr();
665
666 if (CheckVarRefsSame(FirstExprResults.ExprKind,
667 IgnoreBeforeCompare(FirstExprResults.X_Var),
668 SecondExprResults.ExprKind,
669 IgnoreBeforeCompare(SecondExprResults.X_Var)))
670 return getRecoveryExpr();
671 break;
672 }
673 }
674 ++BodyItr;
675 if (BodyItr != CmpdStmt->body().end()) {
676 DiagnoseInvalidAtomic(
677 (*BodyItr)->getBeginLoc(),
678 SemaRef.PDiag(diag::note_acc_atomic_too_many_stmts));
679 return getRecoveryExpr();
680 }
681 } else {
682 // This check doesn't need to happen if it is a compound stmt.
683 Expr *AssocExpr = RequireExpr(
684 AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
685 << diag::OACCAtomicExpr::Assign);
686 if (!AssocExpr)
687 return getRecoveryExpr();
688
689 // First, we require an assignment.
690 std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
691
692 if (!AssignRes)
693 return getRecoveryExpr();
694
695 if (CheckIncDecAssignCompoundAssign(AssignRes->RHS,
696 SimpleAssignKind::None)
697 .Failed)
698 return getRecoveryExpr();
699 }
700
701 return AssocStmt;
702 }
703
704public:
705 AtomicOperandChecker(SemaOpenACC &S, OpenACCAtomicKind AtKind,
706 SourceLocation DirLoc, StmtResult AssocStmt)
707 : SemaRef(S), AtKind(AtKind), AtomicDirLoc(DirLoc), AssocStmt(AssocStmt) {
708 }
709
710 StmtResult Check() {
711
712 switch (AtKind) {
713 case OpenACCAtomicKind::Read:
714 return CheckRead();
715 case OpenACCAtomicKind::Write:
716 return CheckWrite();
717 case OpenACCAtomicKind::None:
718 case OpenACCAtomicKind::Update:
719 return CheckUpdate();
720 case OpenACCAtomicKind::Capture:
721 return CheckCapture();
722 }
723 llvm_unreachable("Unhandled atomic kind?");
724 }
725};
726} // namespace
727
729 OpenACCAtomicKind AtKind,
730 StmtResult AssocStmt) {
731 if (!AssocStmt.isUsable())
732 return AssocStmt;
733
734 if (isa<RecoveryExpr>(AssocStmt.get()))
735 return AssocStmt;
736
737 AtomicOperandChecker Checker{*this, AtKind, AtomicDirLoc, AssocStmt};
738 return Checker.Check();
739}
Defines the clang::Expr interface and subclasses for C++ expressions.
This file declares semantic analysis for OpenACC constructs and clauses.
PtrTy get() const
Definition Ownership.h:171
bool isUsable() const
Definition Ownership.h:169
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:3153
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3126
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Definition Expr.h:285
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3106
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:283
QualType getType() const
Definition Expr.h:145
static RecoveryExpr * Create(ASTContext &Ctx, QualType T, SourceLocation BeginLoc, SourceLocation EndLoc, ArrayRef< Expr * > SubExprs)
Definition Expr.cpp:5507
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.
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:355
bool isIncrementDecrementOp() const
Definition Expr.h:2385
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
OpenACCAtomicKind
StmtResult StmtError()
Definition Ownership.h:266
const FunctionProtoType * T
@ None
The alignment was not explicit in code.
Definition ASTContext.h:176
ActionResult< Stmt * > StmtResult
Definition Ownership.h:250