llvm-project/clang/lib/Sema/SemaOpenACCAtomic.cpp
erichkeane 99a9133a68 [OpenACC] Implement Sema/AST for 'atomic' construct
The atomic construct is a particularly complicated one.  The directive
itself is pretty simple, it has 5 options for the 'atomic-clause'.
However, the associated statement is fairly complicated.

'read' accepts:
  v = x;
'write' accepts:
  x = expr;
'update' (or no clause) accepts:
  x++;
  x--;
  ++x;
  --x;
  x binop= expr;
  x = x binop expr;
  x = expr binop x;

'capture' accepts either a compound statement, or:
  v = x++;
  v = x--;
  v = ++x;
  v = --x;
  v = x binop= expr;
  v = x = x binop expr;
  v = x = expr binop x;

IF 'capture' has a compound statement, it accepts:
  {v = x; x binop= expr; }
  {x binop= expr; v = x; }
  {v = x; x = x binop expr; }
  {v = x; x = expr binop x; }
  {x = x binop expr ;v = x; }
  {x = expr binop x; v = x; }
  {v = x; x = expr; }
  {v = x; x++; }
  {v = x; ++x; }
  {x++; v = x; }
  {++x; v = x; }
  {v = x; x--; }
  {v = x; --x; }
  {x--; v = x; }
  {--x; v = x; }

While these are all quite complicated, there is a significant amount
of similarity between the 'capture' and 'update' lists, so this patch
reuses a lot of the same functions.

This patch implements the entirety of 'atomic', creating a new Sema file
for the sema for it, as it is fairly sizable.
2025-02-03 07:22:22 -08:00

737 lines
24 KiB
C++

//== SemaOpenACCAtomic.cpp - Semantic Analysis for OpenACC Atomic Construct===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements semantic analysis for the OpenACC atomic construct.
///
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/SemaOpenACC.h"
#include <optional>
#include <variant>
using namespace clang;
namespace {
class AtomicOperandChecker {
SemaOpenACC &SemaRef;
OpenACCAtomicKind AtKind;
SourceLocation AtomicDirLoc;
StmtResult AssocStmt;
// Do a diagnostic, which sets the correct error, then displays passed note.
bool DiagnoseInvalidAtomic(SourceLocation Loc, PartialDiagnostic NoteDiag) {
SemaRef.Diag(AtomicDirLoc, diag::err_acc_invalid_atomic)
<< (AtKind != OpenACCAtomicKind::None) << AtKind;
SemaRef.Diag(Loc, NoteDiag);
return true;
}
// Create a replacement recovery expr in case we find an error here. This
// allows us to ignore this during template instantiation so we only get a
// single error.
StmtResult getRecoveryExpr() {
if (!AssocStmt.isUsable())
return AssocStmt;
if (!SemaRef.getASTContext().getLangOpts().RecoveryAST)
return StmtError();
Expr *E = dyn_cast<Expr>(AssocStmt.get());
QualType T = E ? E->getType() : SemaRef.getASTContext().DependentTy;
return RecoveryExpr::Create(SemaRef.getASTContext(), T,
AssocStmt.get()->getBeginLoc(),
AssocStmt.get()->getEndLoc(),
E ? ArrayRef<Expr *>{E} : ArrayRef<Expr *>{});
}
// OpenACC 3.3 2.12: 'expr' is an expression with scalar type.
bool CheckOperandExpr(const Expr *E, PartialDiagnostic PD) {
QualType ExprTy = E->getType();
// Scalar allowed, plus we allow instantiation dependent to support
// templates.
if (ExprTy->isInstantiationDependentType() || ExprTy->isScalarType())
return false;
return DiagnoseInvalidAtomic(E->getExprLoc(),
PD << diag::OACCLValScalar::Scalar << ExprTy);
}
// OpenACC 3.3 2.12: 'x' and 'v' (as applicable) are boht l-value expressoins
// with scalar type.
bool CheckOperandVariable(const Expr *E, PartialDiagnostic PD) {
if (CheckOperandExpr(E, PD))
return true;
if (E->isLValue())
return false;
return DiagnoseInvalidAtomic(E->getExprLoc(),
PD << diag::OACCLValScalar::LVal);
}
Expr *RequireExpr(Stmt *Stmt, PartialDiagnostic ExpectedNote) {
if (Expr *E = dyn_cast<Expr>(Stmt))
return E->IgnoreImpCasts();
DiagnoseInvalidAtomic(Stmt->getBeginLoc(), ExpectedNote);
return nullptr;
}
// A struct to hold the return the inner components of any operands, which
// allows for compound checking.
struct BinaryOpInfo {
const Expr *FoundExpr = nullptr;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
BinaryOperatorKind Operator;
};
struct UnaryOpInfo {
const Expr *FoundExpr = nullptr;
const Expr *SubExpr = nullptr;
UnaryOperatorKind Operator;
bool IsIncrementOp() {
return Operator == UO_PostInc || Operator == UO_PreInc;
}
};
std::optional<UnaryOpInfo> GetUnaryOperatorInfo(const Expr *E) {
// If this is a simple unary operator, just return its details.
if (const auto *UO = dyn_cast<UnaryOperator>(E))
return UnaryOpInfo{UO, UO->getSubExpr()->IgnoreImpCasts(),
UO->getOpcode()};
// This might be an overloaded operator or a dependent context, so make sure
// we can get as many details out of this as we can.
if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
UnaryOpInfo Inf;
Inf.FoundExpr = OpCall;
switch (OpCall->getOperator()) {
default:
return std::nullopt;
case OO_PlusPlus:
Inf.Operator = OpCall->getNumArgs() == 1 ? UO_PreInc : UO_PostInc;
break;
case OO_MinusMinus:
Inf.Operator = OpCall->getNumArgs() == 1 ? UO_PreDec : UO_PostDec;
break;
case OO_Amp:
Inf.Operator = UO_AddrOf;
break;
case OO_Star:
Inf.Operator = UO_Deref;
break;
case OO_Plus:
Inf.Operator = UO_Plus;
break;
case OO_Minus:
Inf.Operator = UO_Minus;
break;
case OO_Tilde:
Inf.Operator = UO_Not;
break;
case OO_Exclaim:
Inf.Operator = UO_LNot;
break;
case OO_Coawait:
Inf.Operator = UO_Coawait;
break;
}
// Some of the above can be both binary and unary operations, so make sure
// we get the right one.
if (Inf.Operator != UO_PostInc && Inf.Operator != UO_PostDec &&
OpCall->getNumArgs() != 1)
return std::nullopt;
Inf.SubExpr = OpCall->getArg(0);
return Inf;
}
return std::nullopt;
}
// Get a normalized version of a binary operator.
std::optional<BinaryOpInfo> GetBinaryOperatorInfo(const Expr *E) {
if (const auto *BO = dyn_cast<BinaryOperator>(E))
return BinaryOpInfo{BO, BO->getLHS()->IgnoreImpCasts(),
BO->getRHS()->IgnoreImpCasts(), BO->getOpcode()};
// In case this is an operator-call, which allows us to support overloaded
// operators and dependent expression.
if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
BinaryOpInfo Inf;
Inf.FoundExpr = OpCall;
switch (OpCall->getOperator()) {
default:
return std::nullopt;
case OO_Plus:
Inf.Operator = BO_Add;
break;
case OO_Minus:
Inf.Operator = BO_Sub;
break;
case OO_Star:
Inf.Operator = BO_Mul;
break;
case OO_Slash:
Inf.Operator = BO_Div;
break;
case OO_Percent:
Inf.Operator = BO_Rem;
break;
case OO_Caret:
Inf.Operator = BO_Xor;
break;
case OO_Amp:
Inf.Operator = BO_And;
break;
case OO_Pipe:
Inf.Operator = BO_Or;
break;
case OO_Equal:
Inf.Operator = BO_Assign;
break;
case OO_Spaceship:
Inf.Operator = BO_Cmp;
break;
case OO_Less:
Inf.Operator = BO_LT;
break;
case OO_Greater:
Inf.Operator = BO_GT;
break;
case OO_PlusEqual:
Inf.Operator = BO_AddAssign;
break;
case OO_MinusEqual:
Inf.Operator = BO_SubAssign;
break;
case OO_StarEqual:
Inf.Operator = BO_MulAssign;
break;
case OO_SlashEqual:
Inf.Operator = BO_DivAssign;
break;
case OO_PercentEqual:
Inf.Operator = BO_RemAssign;
break;
case OO_CaretEqual:
Inf.Operator = BO_XorAssign;
break;
case OO_AmpEqual:
Inf.Operator = BO_AndAssign;
break;
case OO_PipeEqual:
Inf.Operator = BO_OrAssign;
break;
case OO_LessLess:
Inf.Operator = BO_Shl;
break;
case OO_GreaterGreater:
Inf.Operator = BO_Shr;
break;
case OO_LessLessEqual:
Inf.Operator = BO_ShlAssign;
break;
case OO_GreaterGreaterEqual:
Inf.Operator = BO_ShrAssign;
break;
case OO_EqualEqual:
Inf.Operator = BO_EQ;
break;
case OO_ExclaimEqual:
Inf.Operator = BO_NE;
break;
case OO_LessEqual:
Inf.Operator = BO_LE;
break;
case OO_GreaterEqual:
Inf.Operator = BO_GE;
break;
case OO_AmpAmp:
Inf.Operator = BO_LAnd;
break;
case OO_PipePipe:
Inf.Operator = BO_LOr;
break;
case OO_Comma:
Inf.Operator = BO_Comma;
break;
case OO_ArrowStar:
Inf.Operator = BO_PtrMemI;
break;
}
// This isn't a binary operator unless there are two arguments.
if (OpCall->getNumArgs() != 2)
return std::nullopt;
// Callee is the call-operator, so we only need to extract the two
// arguments here.
Inf.LHS = OpCall->getArg(0)->IgnoreImpCasts();
Inf.RHS = OpCall->getArg(1)->IgnoreImpCasts();
return Inf;
}
return std::nullopt;
}
// Checks a required assignment operation, but don't check the LHS or RHS,
// callers have to do that here.
std::optional<BinaryOpInfo> CheckAssignment(const Expr *E) {
std::optional<BinaryOpInfo> Inf = GetBinaryOperatorInfo(E);
if (!Inf) {
DiagnoseInvalidAtomic(E->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::Assign);
return std::nullopt;
}
if (Inf->Operator != BO_Assign) {
DiagnoseInvalidAtomic(Inf->FoundExpr->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::Assign);
return std::nullopt;
}
// Assignment always requires an lvalue/scalar on the LHS.
if (CheckOperandVariable(
Inf->LHS, SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*left=*/0 << diag::OACCAtomicOpKind::Assign))
return std::nullopt;
return Inf;
}
struct IDACInfo {
bool Failed = false;
enum ExprKindTy {
Invalid,
// increment/decrement ops.
Unary,
// v = x
SimpleAssign,
// x = expr
ExprAssign,
// x binop= expr
CompoundAssign,
// x = x binop expr
// x = expr binop x
AssignBinOp
} ExprKind;
// The variable referred to as 'x' in all of the grammar, such that it is
// needed in compound statement checking of capture to check between the two
// expressions.
const Expr *X_Var = nullptr;
static IDACInfo Fail() { return IDACInfo{true, Invalid, nullptr}; };
};
// Helper for CheckIncDecAssignCompoundAssign, does checks for inc/dec.
IDACInfo CheckIncDec(UnaryOpInfo Inf) {
if (!UnaryOperator::isIncrementDecrementOp(Inf.Operator)) {
DiagnoseInvalidAtomic(
Inf.FoundExpr->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_unsupported_unary_operator));
return IDACInfo::Fail();
}
bool Failed = CheckOperandVariable(
Inf.SubExpr,
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*none=*/2
<< (Inf.IsIncrementOp() ? diag::OACCAtomicOpKind::Inc
: diag::OACCAtomicOpKind::Dec));
// For increment/decrements, the subexpr is the 'x' (x++, ++x, etc).
return IDACInfo{Failed, IDACInfo::Unary, Inf.SubExpr};
}
enum class SimpleAssignKind { None, Var, Expr };
// Check an assignment, and ensure the RHS is either x binop expr or expr
// binop x.
// If AllowSimpleAssign, also allows v = x;
IDACInfo CheckAssignmentWithBinOpOnRHS(BinaryOpInfo AssignInf,
SimpleAssignKind SAK) {
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*left=*/0 << diag::OACCAtomicOpKind::Assign;
if (CheckOperandVariable(AssignInf.LHS, PD))
return IDACInfo::Fail();
std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(AssignInf.RHS);
if (!BinInf) {
// Capture in a compound statement allows v = x assignment. So make sure
// we permit that here.
if (SAK != SimpleAssignKind::None) {
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*right=*/1 << diag::OACCAtomicOpKind::Assign;
if (SAK == SimpleAssignKind::Var) {
// In the var version, everywhere we allow v = x;, X is the RHS.
return IDACInfo{CheckOperandVariable(AssignInf.RHS, PD),
IDACInfo::SimpleAssign, AssignInf.RHS};
}
assert(SAK == SimpleAssignKind::Expr);
// In the expression version, supported by v=x; x = expr;, we need to
// set to the LHS here.
return IDACInfo{CheckOperandExpr(AssignInf.RHS, PD),
IDACInfo::ExprAssign, AssignInf.LHS};
}
DiagnoseInvalidAtomic(
AssignInf.RHS->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_expected_binop));
return IDACInfo::Fail();
}
switch (BinInf->Operator) {
default:
DiagnoseInvalidAtomic(
BinInf->FoundExpr->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_unsupported_binary_operator));
return IDACInfo::Fail();
// binop is one of +, *, -, /, &, ^, |, <<, or >>
case BO_Add:
case BO_Mul:
case BO_Sub:
case BO_Div:
case BO_And:
case BO_Xor:
case BO_Or:
case BO_Shl:
case BO_Shr:
// Handle these outside of the switch.
break;
}
llvm::FoldingSetNodeID LHS_ID, InnerLHS_ID, InnerRHS_ID;
AssignInf.LHS->Profile(LHS_ID, SemaRef.getASTContext(),
/*Canonical=*/true);
BinInf->LHS->Profile(InnerLHS_ID, SemaRef.getASTContext(),
/*Canonical=*/true);
// This is X = X binop expr;
// Check the RHS is an expression.
if (LHS_ID == InnerLHS_ID)
return IDACInfo{
CheckOperandExpr(
BinInf->RHS,
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar
<< /*right=*/1
<< diag::OACCAtomicOpKind::CompoundAssign)),
IDACInfo::AssignBinOp, AssignInf.LHS};
BinInf->RHS->Profile(InnerRHS_ID, SemaRef.getASTContext(),
/*Canonical=*/true);
// This is X = expr binop X;
// Check the LHS is an expression
if (LHS_ID == InnerRHS_ID)
return IDACInfo{
CheckOperandExpr(
BinInf->LHS,
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign),
IDACInfo::AssignBinOp, AssignInf.LHS};
// If nothing matches, error out.
DiagnoseInvalidAtomic(BinInf->FoundExpr->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_mismatch_operand)
<< const_cast<Expr *>(AssignInf.LHS)
<< const_cast<Expr *>(BinInf->LHS)
<< const_cast<Expr *>(BinInf->RHS));
return IDACInfo::Fail();
}
// Ensures that the expression is an increment/decrement, an assignment, or a
// compound assignment. If its an assignment, allows the x binop expr/x binop
// expr syntax. If it is a compound-assignment, allows any expr on the RHS.
IDACInfo CheckIncDecAssignCompoundAssign(const Expr *E,
SimpleAssignKind SAK) {
std::optional<UnaryOpInfo> UInf = GetUnaryOperatorInfo(E);
// If this is a unary operator, only increment/decrement are allowed, so get
// unary operator, then check everything we can.
if (UInf)
return CheckIncDec(*UInf);
std::optional<BinaryOpInfo> BinInf = GetBinaryOperatorInfo(E);
// Unary or binary operator were the only choices, so error here.
if (!BinInf) {
DiagnoseInvalidAtomic(E->getExprLoc(),
SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::UnaryCompAssign);
return IDACInfo::Fail();
}
switch (BinInf->Operator) {
default:
DiagnoseInvalidAtomic(
BinInf->FoundExpr->getExprLoc(),
SemaRef.PDiag(
diag::note_acc_atomic_unsupported_compound_binary_operator));
return IDACInfo::Fail();
case BO_Assign:
return CheckAssignmentWithBinOpOnRHS(*BinInf, SAK);
case BO_AddAssign:
case BO_MulAssign:
case BO_SubAssign:
case BO_DivAssign:
case BO_AndAssign:
case BO_XorAssign:
case BO_OrAssign:
case BO_ShlAssign:
case BO_ShrAssign: {
PartialDiagnostic LPD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*left=*/0 << diag::OACCAtomicOpKind::CompoundAssign;
PartialDiagnostic RPD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*right=*/1 << diag::OACCAtomicOpKind::CompoundAssign;
// nothing to do other than check the variable expressions.
// success or failure
bool Failed = CheckOperandVariable(BinInf->LHS, LPD) ||
CheckOperandExpr(BinInf->RHS, RPD);
return IDACInfo{Failed, IDACInfo::CompoundAssign, BinInf->LHS};
}
}
llvm_unreachable("all binary operator kinds should be checked above");
}
StmtResult CheckRead() {
Expr *AssocExpr = RequireExpr(
AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::Assign);
if (!AssocExpr)
return getRecoveryExpr();
std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
if (!AssignRes)
return getRecoveryExpr();
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*right=*/1 << diag::OACCAtomicOpKind::Assign;
// Finally, check the RHS.
if (CheckOperandVariable(AssignRes->RHS, PD))
return getRecoveryExpr();
return AssocStmt;
}
StmtResult CheckWrite() {
Expr *AssocExpr = RequireExpr(
AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::Assign);
if (!AssocExpr)
return getRecoveryExpr();
std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
if (!AssignRes)
return getRecoveryExpr();
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*right=*/1 << diag::OACCAtomicOpKind::Assign;
// Finally, check the RHS.
if (CheckOperandExpr(AssignRes->RHS, PD))
return getRecoveryExpr();
return AssocStmt;
}
StmtResult CheckUpdate() {
Expr *AssocExpr = RequireExpr(
AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::UnaryCompAssign);
if (!AssocExpr ||
CheckIncDecAssignCompoundAssign(AssocExpr, SimpleAssignKind::None)
.Failed)
return getRecoveryExpr();
return AssocStmt;
}
bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,
IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {
llvm::FoldingSetNodeID First_ID, Second_ID;
FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);
SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);
if (First_ID == Second_ID)
return false;
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_mismatch_compound_operand)
<< FirstKind << const_cast<Expr *>(FirstX) << SecondKind
<< const_cast<Expr *>(SecondX);
return DiagnoseInvalidAtomic(SecondX->getExprLoc(), PD);
}
StmtResult CheckCapture() {
if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(AssocStmt.get())) {
auto *const *BodyItr = CmpdStmt->body().begin();
PartialDiagnostic PD = SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::UnaryCompAssign;
// If we don't have at least 1 statement, error.
if (BodyItr == CmpdStmt->body().end()) {
DiagnoseInvalidAtomic(CmpdStmt->getBeginLoc(), PD);
return getRecoveryExpr();
}
// First Expr can be inc/dec, assign, or compound assign.
Expr *FirstExpr = RequireExpr(*BodyItr, PD);
if (!FirstExpr)
return getRecoveryExpr();
IDACInfo FirstExprResults =
CheckIncDecAssignCompoundAssign(FirstExpr, SimpleAssignKind::Var);
if (FirstExprResults.Failed)
return getRecoveryExpr();
++BodyItr;
// If we don't have second statement, error.
if (BodyItr == CmpdStmt->body().end()) {
DiagnoseInvalidAtomic(CmpdStmt->getEndLoc(), PD);
return getRecoveryExpr();
}
Expr *SecondExpr = RequireExpr(*BodyItr, PD);
if (!SecondExpr)
return getRecoveryExpr();
assert(FirstExprResults.ExprKind != IDACInfo::Invalid);
switch (FirstExprResults.ExprKind) {
case IDACInfo::Invalid:
case IDACInfo::ExprAssign:
llvm_unreachable("Should have error'ed out by now");
case IDACInfo::Unary:
case IDACInfo::CompoundAssign:
case IDACInfo::AssignBinOp: {
// Everything but simple-assign can only be followed by a simple
// assignment.
std::optional<BinaryOpInfo> AssignRes = CheckAssignment(SecondExpr);
if (!AssignRes)
return getRecoveryExpr();
PartialDiagnostic PD =
SemaRef.PDiag(diag::note_acc_atomic_operand_lvalue_scalar)
<< /*right=*/1 << diag::OACCAtomicOpKind::Assign;
if (CheckOperandVariable(AssignRes->RHS, PD))
return getRecoveryExpr();
if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var,
IDACInfo::SimpleAssign, AssignRes->RHS))
return getRecoveryExpr();
break;
}
case IDACInfo::SimpleAssign: {
// If the first was v = x, anything but simple expression is allowed.
IDACInfo SecondExprResults =
CheckIncDecAssignCompoundAssign(SecondExpr, SimpleAssignKind::Expr);
if (SecondExprResults.Failed)
return getRecoveryExpr();
if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var,
SecondExprResults.ExprKind,
SecondExprResults.X_Var))
return getRecoveryExpr();
break;
}
}
++BodyItr;
if (BodyItr != CmpdStmt->body().end()) {
DiagnoseInvalidAtomic(
(*BodyItr)->getBeginLoc(),
SemaRef.PDiag(diag::note_acc_atomic_too_many_stmts));
return getRecoveryExpr();
}
} else {
// This check doesn't need to happen if it is a compound stmt.
Expr *AssocExpr = RequireExpr(
AssocStmt.get(), SemaRef.PDiag(diag::note_acc_atomic_expr_must_be)
<< diag::OACCAtomicExpr::Assign);
if (!AssocExpr)
return getRecoveryExpr();
// First, we require an assignment.
std::optional<BinaryOpInfo> AssignRes = CheckAssignment(AssocExpr);
if (!AssignRes)
return getRecoveryExpr();
if (CheckIncDecAssignCompoundAssign(AssignRes->RHS,
SimpleAssignKind::None)
.Failed)
return getRecoveryExpr();
}
return AssocStmt;
}
public:
AtomicOperandChecker(SemaOpenACC &S, OpenACCAtomicKind AtKind,
SourceLocation DirLoc, StmtResult AssocStmt)
: SemaRef(S), AtKind(AtKind), AtomicDirLoc(DirLoc), AssocStmt(AssocStmt) {
}
StmtResult Check() {
switch (AtKind) {
case OpenACCAtomicKind::Read:
return CheckRead();
case OpenACCAtomicKind::Write:
return CheckWrite();
case OpenACCAtomicKind::None:
case OpenACCAtomicKind::Update:
return CheckUpdate();
case OpenACCAtomicKind::Capture:
return CheckCapture();
}
llvm_unreachable("Unhandled atomic kind?");
}
};
} // namespace
StmtResult SemaOpenACC::CheckAtomicAssociatedStmt(SourceLocation AtomicDirLoc,
OpenACCAtomicKind AtKind,
StmtResult AssocStmt) {
if (!AssocStmt.isUsable())
return AssocStmt;
if (isa<RecoveryExpr>(AssocStmt.get()))
return AssocStmt;
AtomicOperandChecker Checker{*this, AtKind, AtomicDirLoc, AssocStmt};
return Checker.Check();
}