clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
470 lines
20 KiB
C++
470 lines
20 KiB
C++
//===----- SemaOpenACC.h - Semantic Analysis for OpenACC constructs -------===//
|
|
//
|
|
// 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 declares semantic analysis for OpenACC constructs and
|
|
/// clauses.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_SEMA_SEMAOPENACC_H
|
|
#define LLVM_CLANG_SEMA_SEMAOPENACC_H
|
|
|
|
#include "clang/AST/DeclGroup.h"
|
|
#include "clang/AST/StmtOpenACC.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Basic/OpenACCKinds.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Sema/Ownership.h"
|
|
#include "clang/Sema/SemaBase.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include <cassert>
|
|
#include <optional>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
namespace clang {
|
|
class IdentifierInfo;
|
|
class OpenACCClause;
|
|
|
|
class SemaOpenACC : public SemaBase {
|
|
private:
|
|
/// A collection of loop constructs in the compute construct scope that
|
|
/// haven't had their 'parent' compute construct set yet. Entires will only be
|
|
/// made to this list in the case where we know the loop isn't an orphan.
|
|
llvm::SmallVector<OpenACCLoopConstruct *> ParentlessLoopConstructs;
|
|
/// Whether we are inside of a compute construct, and should add loops to the
|
|
/// above collection.
|
|
bool InsideComputeConstruct = false;
|
|
|
|
public:
|
|
// Redeclaration of the version in OpenACCClause.h.
|
|
using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
|
|
|
|
/// A type to represent all the data for an OpenACC Clause that has been
|
|
/// parsed, but not yet created/semantically analyzed. This is effectively a
|
|
/// discriminated union on the 'Clause Kind', with all of the individual
|
|
/// clause details stored in a std::variant.
|
|
class OpenACCParsedClause {
|
|
OpenACCDirectiveKind DirKind;
|
|
OpenACCClauseKind ClauseKind;
|
|
SourceRange ClauseRange;
|
|
SourceLocation LParenLoc;
|
|
|
|
struct DefaultDetails {
|
|
OpenACCDefaultClauseKind DefaultClauseKind;
|
|
};
|
|
|
|
struct ConditionDetails {
|
|
Expr *ConditionExpr;
|
|
};
|
|
|
|
struct IntExprDetails {
|
|
SmallVector<Expr *> IntExprs;
|
|
};
|
|
|
|
struct VarListDetails {
|
|
SmallVector<Expr *> VarList;
|
|
bool IsReadOnly;
|
|
bool IsZero;
|
|
};
|
|
|
|
struct WaitDetails {
|
|
Expr *DevNumExpr;
|
|
SourceLocation QueuesLoc;
|
|
SmallVector<Expr *> QueueIdExprs;
|
|
};
|
|
|
|
struct DeviceTypeDetails {
|
|
SmallVector<DeviceTypeArgument> Archs;
|
|
};
|
|
struct ReductionDetails {
|
|
OpenACCReductionOperator Op;
|
|
SmallVector<Expr *> VarList;
|
|
};
|
|
|
|
std::variant<std::monostate, DefaultDetails, ConditionDetails,
|
|
IntExprDetails, VarListDetails, WaitDetails, DeviceTypeDetails,
|
|
ReductionDetails>
|
|
Details = std::monostate{};
|
|
|
|
public:
|
|
OpenACCParsedClause(OpenACCDirectiveKind DirKind,
|
|
OpenACCClauseKind ClauseKind, SourceLocation BeginLoc)
|
|
: DirKind(DirKind), ClauseKind(ClauseKind), ClauseRange(BeginLoc, {}) {}
|
|
|
|
OpenACCDirectiveKind getDirectiveKind() const { return DirKind; }
|
|
|
|
OpenACCClauseKind getClauseKind() const { return ClauseKind; }
|
|
|
|
SourceLocation getBeginLoc() const { return ClauseRange.getBegin(); }
|
|
|
|
SourceLocation getLParenLoc() const { return LParenLoc; }
|
|
|
|
SourceLocation getEndLoc() const { return ClauseRange.getEnd(); }
|
|
|
|
OpenACCDefaultClauseKind getDefaultClauseKind() const {
|
|
assert(ClauseKind == OpenACCClauseKind::Default &&
|
|
"Parsed clause is not a default clause");
|
|
return std::get<DefaultDetails>(Details).DefaultClauseKind;
|
|
}
|
|
|
|
const Expr *getConditionExpr() const {
|
|
return const_cast<OpenACCParsedClause *>(this)->getConditionExpr();
|
|
}
|
|
|
|
Expr *getConditionExpr() {
|
|
assert((ClauseKind == OpenACCClauseKind::If ||
|
|
(ClauseKind == OpenACCClauseKind::Self &&
|
|
DirKind != OpenACCDirectiveKind::Update)) &&
|
|
"Parsed clause kind does not have a condition expr");
|
|
|
|
// 'self' has an optional ConditionExpr, so be tolerant of that. This will
|
|
// assert in variant otherwise.
|
|
if (ClauseKind == OpenACCClauseKind::Self &&
|
|
std::holds_alternative<std::monostate>(Details))
|
|
return nullptr;
|
|
|
|
return std::get<ConditionDetails>(Details).ConditionExpr;
|
|
}
|
|
|
|
unsigned getNumIntExprs() const {
|
|
assert((ClauseKind == OpenACCClauseKind::NumGangs ||
|
|
ClauseKind == OpenACCClauseKind::NumWorkers ||
|
|
ClauseKind == OpenACCClauseKind::Async ||
|
|
ClauseKind == OpenACCClauseKind::VectorLength) &&
|
|
"Parsed clause kind does not have a int exprs");
|
|
|
|
// 'async' and 'wait' have an optional IntExpr, so be tolerant of that.
|
|
if ((ClauseKind == OpenACCClauseKind::Async ||
|
|
ClauseKind == OpenACCClauseKind::Wait) &&
|
|
std::holds_alternative<std::monostate>(Details))
|
|
return 0;
|
|
return std::get<IntExprDetails>(Details).IntExprs.size();
|
|
}
|
|
|
|
SourceLocation getQueuesLoc() const {
|
|
assert(ClauseKind == OpenACCClauseKind::Wait &&
|
|
"Parsed clause kind does not have a queues location");
|
|
|
|
if (std::holds_alternative<std::monostate>(Details))
|
|
return SourceLocation{};
|
|
|
|
return std::get<WaitDetails>(Details).QueuesLoc;
|
|
}
|
|
|
|
Expr *getDevNumExpr() const {
|
|
assert(ClauseKind == OpenACCClauseKind::Wait &&
|
|
"Parsed clause kind does not have a device number expr");
|
|
|
|
if (std::holds_alternative<std::monostate>(Details))
|
|
return nullptr;
|
|
|
|
return std::get<WaitDetails>(Details).DevNumExpr;
|
|
}
|
|
|
|
ArrayRef<Expr *> getQueueIdExprs() const {
|
|
assert(ClauseKind == OpenACCClauseKind::Wait &&
|
|
"Parsed clause kind does not have a queue id expr list");
|
|
|
|
if (std::holds_alternative<std::monostate>(Details))
|
|
return ArrayRef<Expr *>{std::nullopt};
|
|
|
|
return std::get<WaitDetails>(Details).QueueIdExprs;
|
|
}
|
|
|
|
ArrayRef<Expr *> getIntExprs() {
|
|
assert((ClauseKind == OpenACCClauseKind::NumGangs ||
|
|
ClauseKind == OpenACCClauseKind::NumWorkers ||
|
|
ClauseKind == OpenACCClauseKind::Async ||
|
|
ClauseKind == OpenACCClauseKind::VectorLength) &&
|
|
"Parsed clause kind does not have a int exprs");
|
|
|
|
return std::get<IntExprDetails>(Details).IntExprs;
|
|
}
|
|
|
|
ArrayRef<Expr *> getIntExprs() const {
|
|
return const_cast<OpenACCParsedClause *>(this)->getIntExprs();
|
|
}
|
|
|
|
OpenACCReductionOperator getReductionOp() const {
|
|
return std::get<ReductionDetails>(Details).Op;
|
|
}
|
|
|
|
ArrayRef<Expr *> getVarList() {
|
|
assert((ClauseKind == OpenACCClauseKind::Private ||
|
|
ClauseKind == OpenACCClauseKind::NoCreate ||
|
|
ClauseKind == OpenACCClauseKind::Present ||
|
|
ClauseKind == OpenACCClauseKind::Copy ||
|
|
ClauseKind == OpenACCClauseKind::PCopy ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopy ||
|
|
ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate ||
|
|
ClauseKind == OpenACCClauseKind::Attach ||
|
|
ClauseKind == OpenACCClauseKind::DevicePtr ||
|
|
ClauseKind == OpenACCClauseKind::Reduction ||
|
|
ClauseKind == OpenACCClauseKind::FirstPrivate) &&
|
|
"Parsed clause kind does not have a var-list");
|
|
|
|
if (ClauseKind == OpenACCClauseKind::Reduction)
|
|
return std::get<ReductionDetails>(Details).VarList;
|
|
|
|
return std::get<VarListDetails>(Details).VarList;
|
|
}
|
|
|
|
ArrayRef<Expr *> getVarList() const {
|
|
return const_cast<OpenACCParsedClause *>(this)->getVarList();
|
|
}
|
|
|
|
bool isReadOnly() const {
|
|
assert((ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn) &&
|
|
"Only copyin accepts 'readonly:' tag");
|
|
return std::get<VarListDetails>(Details).IsReadOnly;
|
|
}
|
|
|
|
bool isZero() const {
|
|
assert((ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate) &&
|
|
"Only copyout/create accepts 'zero' tag");
|
|
return std::get<VarListDetails>(Details).IsZero;
|
|
}
|
|
|
|
ArrayRef<DeviceTypeArgument> getDeviceTypeArchitectures() const {
|
|
assert((ClauseKind == OpenACCClauseKind::DeviceType ||
|
|
ClauseKind == OpenACCClauseKind::DType) &&
|
|
"Only 'device_type'/'dtype' has a device-type-arg list");
|
|
return std::get<DeviceTypeDetails>(Details).Archs;
|
|
}
|
|
|
|
void setLParenLoc(SourceLocation EndLoc) { LParenLoc = EndLoc; }
|
|
void setEndLoc(SourceLocation EndLoc) { ClauseRange.setEnd(EndLoc); }
|
|
|
|
void setDefaultDetails(OpenACCDefaultClauseKind DefKind) {
|
|
assert(ClauseKind == OpenACCClauseKind::Default &&
|
|
"Parsed clause is not a default clause");
|
|
Details = DefaultDetails{DefKind};
|
|
}
|
|
|
|
void setConditionDetails(Expr *ConditionExpr) {
|
|
assert((ClauseKind == OpenACCClauseKind::If ||
|
|
(ClauseKind == OpenACCClauseKind::Self &&
|
|
DirKind != OpenACCDirectiveKind::Update)) &&
|
|
"Parsed clause kind does not have a condition expr");
|
|
// In C++ we can count on this being a 'bool', but in C this gets left as
|
|
// some sort of scalar that codegen will have to take care of converting.
|
|
assert((!ConditionExpr || ConditionExpr->isInstantiationDependent() ||
|
|
ConditionExpr->getType()->isScalarType()) &&
|
|
"Condition expression type not scalar/dependent");
|
|
|
|
Details = ConditionDetails{ConditionExpr};
|
|
}
|
|
|
|
void setIntExprDetails(ArrayRef<Expr *> IntExprs) {
|
|
assert((ClauseKind == OpenACCClauseKind::NumGangs ||
|
|
ClauseKind == OpenACCClauseKind::NumWorkers ||
|
|
ClauseKind == OpenACCClauseKind::Async ||
|
|
ClauseKind == OpenACCClauseKind::VectorLength) &&
|
|
"Parsed clause kind does not have a int exprs");
|
|
Details = IntExprDetails{{IntExprs.begin(), IntExprs.end()}};
|
|
}
|
|
void setIntExprDetails(llvm::SmallVector<Expr *> &&IntExprs) {
|
|
assert((ClauseKind == OpenACCClauseKind::NumGangs ||
|
|
ClauseKind == OpenACCClauseKind::NumWorkers ||
|
|
ClauseKind == OpenACCClauseKind::Async ||
|
|
ClauseKind == OpenACCClauseKind::VectorLength) &&
|
|
"Parsed clause kind does not have a int exprs");
|
|
Details = IntExprDetails{std::move(IntExprs)};
|
|
}
|
|
|
|
void setVarListDetails(ArrayRef<Expr *> VarList, bool IsReadOnly,
|
|
bool IsZero) {
|
|
assert((ClauseKind == OpenACCClauseKind::Private ||
|
|
ClauseKind == OpenACCClauseKind::NoCreate ||
|
|
ClauseKind == OpenACCClauseKind::Present ||
|
|
ClauseKind == OpenACCClauseKind::Copy ||
|
|
ClauseKind == OpenACCClauseKind::PCopy ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopy ||
|
|
ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate ||
|
|
ClauseKind == OpenACCClauseKind::Attach ||
|
|
ClauseKind == OpenACCClauseKind::DevicePtr ||
|
|
ClauseKind == OpenACCClauseKind::FirstPrivate) &&
|
|
"Parsed clause kind does not have a var-list");
|
|
assert((!IsReadOnly || ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn) &&
|
|
"readonly: tag only valid on copyin");
|
|
assert((!IsZero || ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate) &&
|
|
"zero: tag only valid on copyout/create");
|
|
Details =
|
|
VarListDetails{{VarList.begin(), VarList.end()}, IsReadOnly, IsZero};
|
|
}
|
|
|
|
void setVarListDetails(llvm::SmallVector<Expr *> &&VarList, bool IsReadOnly,
|
|
bool IsZero) {
|
|
assert((ClauseKind == OpenACCClauseKind::Private ||
|
|
ClauseKind == OpenACCClauseKind::NoCreate ||
|
|
ClauseKind == OpenACCClauseKind::Present ||
|
|
ClauseKind == OpenACCClauseKind::Copy ||
|
|
ClauseKind == OpenACCClauseKind::PCopy ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopy ||
|
|
ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate ||
|
|
ClauseKind == OpenACCClauseKind::Attach ||
|
|
ClauseKind == OpenACCClauseKind::DevicePtr ||
|
|
ClauseKind == OpenACCClauseKind::FirstPrivate) &&
|
|
"Parsed clause kind does not have a var-list");
|
|
assert((!IsReadOnly || ClauseKind == OpenACCClauseKind::CopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PCopyIn ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyIn) &&
|
|
"readonly: tag only valid on copyin");
|
|
assert((!IsZero || ClauseKind == OpenACCClauseKind::CopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
|
|
ClauseKind == OpenACCClauseKind::Create ||
|
|
ClauseKind == OpenACCClauseKind::PCreate ||
|
|
ClauseKind == OpenACCClauseKind::PresentOrCreate) &&
|
|
"zero: tag only valid on copyout/create");
|
|
Details = VarListDetails{std::move(VarList), IsReadOnly, IsZero};
|
|
}
|
|
|
|
void setReductionDetails(OpenACCReductionOperator Op,
|
|
llvm::SmallVector<Expr *> &&VarList) {
|
|
assert(ClauseKind == OpenACCClauseKind::Reduction &&
|
|
"reduction details only valid on reduction");
|
|
Details = ReductionDetails{Op, std::move(VarList)};
|
|
}
|
|
|
|
void setWaitDetails(Expr *DevNum, SourceLocation QueuesLoc,
|
|
llvm::SmallVector<Expr *> &&IntExprs) {
|
|
assert(ClauseKind == OpenACCClauseKind::Wait &&
|
|
"Parsed clause kind does not have a wait-details");
|
|
Details = WaitDetails{DevNum, QueuesLoc, std::move(IntExprs)};
|
|
}
|
|
|
|
void setDeviceTypeDetails(llvm::SmallVector<DeviceTypeArgument> &&Archs) {
|
|
assert((ClauseKind == OpenACCClauseKind::DeviceType ||
|
|
ClauseKind == OpenACCClauseKind::DType) &&
|
|
"Only 'device_type'/'dtype' has a device-type-arg list");
|
|
Details = DeviceTypeDetails{std::move(Archs)};
|
|
}
|
|
};
|
|
|
|
SemaOpenACC(Sema &S);
|
|
|
|
/// Called after parsing an OpenACC Clause so that it can be checked.
|
|
OpenACCClause *ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
|
|
OpenACCParsedClause &Clause);
|
|
|
|
/// Called after the construct has been parsed, but clauses haven't been
|
|
/// parsed. This allows us to diagnose not-implemented, as well as set up any
|
|
/// state required for parsing the clauses.
|
|
void ActOnConstruct(OpenACCDirectiveKind K, SourceLocation DirLoc);
|
|
|
|
/// Called after the directive, including its clauses, have been parsed and
|
|
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
|
|
/// happen before any associated declarations or statements have been parsed.
|
|
/// This function is only called when we are parsing a 'statement' context.
|
|
bool ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc);
|
|
|
|
/// Called after the directive, including its clauses, have been parsed and
|
|
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
|
|
/// happen before any associated declarations or statements have been parsed.
|
|
/// This function is only called when we are parsing a 'Decl' context.
|
|
bool ActOnStartDeclDirective(OpenACCDirectiveKind K, SourceLocation StartLoc);
|
|
/// Called when we encounter an associated statement for our construct, this
|
|
/// should check legality of the statement as it appertains to this Construct.
|
|
StmtResult ActOnAssociatedStmt(SourceLocation DirectiveLoc,
|
|
OpenACCDirectiveKind K, StmtResult AssocStmt);
|
|
|
|
/// Called after the directive has been completely parsed, including the
|
|
/// declaration group or associated statement.
|
|
StmtResult ActOnEndStmtDirective(OpenACCDirectiveKind K,
|
|
SourceLocation StartLoc,
|
|
SourceLocation DirLoc,
|
|
SourceLocation EndLoc,
|
|
ArrayRef<OpenACCClause *> Clauses,
|
|
StmtResult AssocStmt);
|
|
|
|
/// Called after the directive has been completely parsed, including the
|
|
/// declaration group or associated statement.
|
|
DeclGroupRef ActOnEndDeclDirective();
|
|
|
|
/// Called when encountering an 'int-expr' for OpenACC, and manages
|
|
/// conversions and diagnostics to 'int'.
|
|
ExprResult ActOnIntExpr(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
|
|
SourceLocation Loc, Expr *IntExpr);
|
|
|
|
/// Called when encountering a 'var' for OpenACC, ensures it is actually a
|
|
/// declaration reference to a variable of the correct type.
|
|
ExprResult ActOnVar(OpenACCClauseKind CK, Expr *VarExpr);
|
|
|
|
/// Called while semantically analyzing the reduction clause, ensuring the var
|
|
/// is the correct kind of reference.
|
|
ExprResult CheckReductionVar(Expr *VarExpr);
|
|
|
|
/// Called to check the 'var' type is a variable of pointer type, necessary
|
|
/// for 'deviceptr' and 'attach' clauses. Returns true on success.
|
|
bool CheckVarIsPointerType(OpenACCClauseKind ClauseKind, Expr *VarExpr);
|
|
|
|
/// Checks and creates an Array Section used in an OpenACC construct/clause.
|
|
ExprResult ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc,
|
|
Expr *LowerBound,
|
|
SourceLocation ColonLocFirst, Expr *Length,
|
|
SourceLocation RBLoc);
|
|
|
|
/// Helper type for the registration/assignment of constructs that need to
|
|
/// 'know' about their parent constructs and hold a reference to them, such as
|
|
/// Loop needing its parent construct.
|
|
class AssociatedStmtRAII {
|
|
SemaOpenACC &SemaRef;
|
|
bool WasInsideComputeConstruct;
|
|
OpenACCDirectiveKind DirKind;
|
|
llvm::SmallVector<OpenACCLoopConstruct *> ParentlessLoopConstructs;
|
|
|
|
public:
|
|
AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind);
|
|
~AssociatedStmtRAII();
|
|
};
|
|
};
|
|
|
|
} // namespace clang
|
|
|
|
#endif // LLVM_CLANG_SEMA_SEMAOPENACC_H
|