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(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 | |
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 |
Definitions
- AtomicOperandChecker
- DiagnoseInvalidAtomic
- getRecoveryExpr
- CheckOperandExpr
- CheckOperandVariable
- RequireExpr
- BinaryOpInfo
- UnaryOpInfo
- IsIncrementOp
- GetUnaryOperatorInfo
- GetBinaryOperatorInfo
- CheckAssignment
- IDACInfo
- ExprKindTy
- Fail
- CheckIncDec
- SimpleAssignKind
- CheckAssignmentWithBinOpOnRHS
- CheckIncDecAssignCompoundAssign
- CheckRead
- CheckWrite
- CheckUpdate
- CheckVarRefsSame
- CheckCapture
- AtomicOperandChecker
- Check
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more