Skip to content

Commit 438863a

Browse files
committed
[OpenACC][Sema] Implement warning for 'cache' invalid var ref
The 'cache' construct is lowered as marking the acc.loop in ACC MLIR. This results in any variable references that are not inside of the acc.loop being invalid. This patch adds a warning to that effect, and ensures that the variable references won't be added to the AST during parsing so we don't try to lower them. This results in loss of instantiation-diagnostics for these, however that seems like an acceptable consequence to ignoring it.
1 parent d4e57c6 commit 438863a

13 files changed

+302
-28
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13217,8 +13217,12 @@ def err_acc_not_a_var_ref_use_device_declare
1321713217
: Error<"OpenACC variable %select{in 'use_device' clause|on 'declare' "
1321813218
"construct}0 is not a valid variable name or array name">;
1321913219
def err_acc_not_a_var_ref_cache
13220-
: Error<"OpenACC variable in cache directive is not a valid sub-array or "
13220+
: Error<"OpenACC variable in 'cache' directive is not a valid sub-array or "
1322113221
"array element">;
13222+
def warn_acc_cache_var_not_outside_loop
13223+
: Warning<"OpenACC variable in 'cache' directive was not declared outside "
13224+
"of the associated 'loop' directive; directive has no effect">,
13225+
InGroup<DiagGroup<"openacc-cache-var-inside-loop">>;
1322213226
def err_acc_typecheck_subarray_value
1322313227
: Error<"OpenACC sub-array subscripted value is not an array or pointer">;
1322413228
def err_acc_subarray_function_type

clang/include/clang/Sema/Scope.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,15 @@ class Scope {
158158
/// constructs, since they have the same behavior.
159159
OpenACCComputeConstructScope = 0x10000000,
160160

161+
/// This is the scope of an OpenACC Loop/Combined construct, which is used
162+
/// to determine whether a 'cache' construct variable reference is legal.
163+
OpenACCLoopConstructScope = 0x20000000,
164+
161165
/// This is a scope of type alias declaration.
162-
TypeAliasScope = 0x20000000,
166+
TypeAliasScope = 0x40000000,
163167

164168
/// This is a scope of friend declaration.
165-
FriendScope = 0x40000000,
169+
FriendScope = 0x80000000,
166170
};
167171

168172
private:
@@ -549,6 +553,10 @@ class Scope {
549553
return getFlags() & Scope::OpenACCComputeConstructScope;
550554
}
551555

556+
bool isOpenACCLoopConstructScope() const {
557+
return getFlags() & Scope::OpenACCLoopConstructScope;
558+
}
559+
552560
/// Determine if this scope (or its parents) are a compute construct. If the
553561
/// argument is provided, the search will stop at any of the specified scopes.
554562
/// Otherwise, it will stop only at the normal 'no longer search' scopes.

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ class SemaOpenACC : public SemaBase {
117117
OpenACCDirectiveKind DirectiveKind = OpenACCDirectiveKind::Invalid;
118118
} TileInfo;
119119

120+
/// The 'cache' var-list requires some additional work to track variable
121+
/// references to make sure they are on the 'other' side of a `loop`. This
122+
/// structure is used during parse time to track vardecl use while parsing a
123+
/// cache var list.
124+
struct CacheParseInfo {
125+
bool ParsingCacheVarList = false;
126+
bool IsInvalidCacheRef = false;
127+
} CacheInfo;
128+
120129
/// A list of the active reduction clauses, which allows us to check that all
121130
/// vars on nested constructs for the same reduction var have the same
122131
/// reduction operator. Currently this is enforced against all constructs
@@ -861,6 +870,12 @@ class SemaOpenACC : public SemaBase {
861870
ExprResult ActOnIntExpr(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
862871
SourceLocation Loc, Expr *IntExpr);
863872

873+
/// Called right before a 'var' is parsed, so we can set the state for parsing
874+
/// a 'cache' var.
875+
void ActOnStartParseVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK);
876+
/// Called only if the parse of a 'var' was invalid, else 'ActOnVar' should be
877+
/// called.
878+
void ActOnInvalidParseVar();
864879
/// Called when encountering a 'var' for OpenACC, ensures it is actually a
865880
/// declaration reference to a variable of the correct type.
866881
ExprResult ActOnVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
@@ -913,6 +928,10 @@ class SemaOpenACC : public SemaBase {
913928
OpenACCDirectiveKind DK, OpenACCGangKind GK,
914929
Expr *E);
915930

931+
// Called when a declaration is referenced, so that we can make sure certain
932+
// clauses don't do the 'wrong' thing/have incorrect references.
933+
void CheckDeclReference(SourceLocation Loc, Expr *E, Decl *D);
934+
916935
// Does the checking for a 'gang' clause that needs to be done in dependent
917936
// and not dependent cases.
918937
OpenACCClause *

clang/lib/Parse/ParseOpenACC.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,13 +606,20 @@ unsigned getOpenACCScopeFlags(OpenACCDirectiveKind DirKind) {
606606
case OpenACCDirectiveKind::Parallel:
607607
case OpenACCDirectiveKind::Serial:
608608
case OpenACCDirectiveKind::Kernels:
609+
// Mark this as a BreakScope/ContinueScope as well as a compute construct
610+
// so that we can diagnose trying to 'break'/'continue' inside of one.
611+
return Scope::BreakScope | Scope::ContinueScope |
612+
Scope::OpenACCComputeConstructScope;
609613
case OpenACCDirectiveKind::ParallelLoop:
610614
case OpenACCDirectiveKind::SerialLoop:
611615
case OpenACCDirectiveKind::KernelsLoop:
612616
// Mark this as a BreakScope/ContinueScope as well as a compute construct
613617
// so that we can diagnose trying to 'break'/'continue' inside of one.
614618
return Scope::BreakScope | Scope::ContinueScope |
615-
Scope::OpenACCComputeConstructScope;
619+
Scope::OpenACCComputeConstructScope |
620+
Scope::OpenACCLoopConstructScope;
621+
case OpenACCDirectiveKind::Loop:
622+
return Scope::OpenACCLoopConstructScope;
616623
case OpenACCDirectiveKind::Data:
617624
case OpenACCDirectiveKind::EnterData:
618625
case OpenACCDirectiveKind::ExitData:
@@ -621,7 +628,6 @@ unsigned getOpenACCScopeFlags(OpenACCDirectiveKind DirKind) {
621628
case OpenACCDirectiveKind::Init:
622629
case OpenACCDirectiveKind::Shutdown:
623630
case OpenACCDirectiveKind::Cache:
624-
case OpenACCDirectiveKind::Loop:
625631
case OpenACCDirectiveKind::Atomic:
626632
case OpenACCDirectiveKind::Declare:
627633
case OpenACCDirectiveKind::Routine:
@@ -1416,12 +1422,15 @@ Parser::OpenACCVarParseResult Parser::ParseOpenACCVar(OpenACCDirectiveKind DK,
14161422
OpenACCClauseKind CK) {
14171423
OpenACCArraySectionRAII ArraySections(*this);
14181424

1425+
getActions().OpenACC().ActOnStartParseVar(DK, CK);
14191426
ExprResult Res = ParseAssignmentExpression();
1420-
if (!Res.isUsable())
1427+
1428+
if (!Res.isUsable()) {
1429+
getActions().OpenACC().ActOnInvalidParseVar();
14211430
return {Res, OpenACCParseCanContinue::Cannot};
1431+
}
14221432

14231433
Res = getActions().OpenACC().ActOnVar(DK, CK, Res.get());
1424-
14251434
return {Res, OpenACCParseCanContinue::Can};
14261435
}
14271436

clang/lib/Sema/Scope.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,8 @@ void Scope::dumpImpl(raw_ostream &OS) const {
234234
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
235235
{TypeAliasScope, "TypeAliasScope"},
236236
{FriendScope, "FriendScope"},
237-
};
237+
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
238+
{OpenACCLoopConstructScope, "OpenACCLoopConstructScope"}};
238239

239240
for (auto Info : FlagInfo) {
240241
if (Flags & Info.first) {

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20169,6 +20169,9 @@ MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E,
2016920169
if (SemaRef.OpenMP().isInOpenMPDeclareTargetContext())
2017020170
SemaRef.OpenMP().checkDeclIsAllowedInOpenMPTarget(E, D);
2017120171

20172+
if (SemaRef.getLangOpts().OpenACC)
20173+
SemaRef.OpenACC().CheckDeclReference(Loc, E, D);
20174+
2017220175
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
2017320176
DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments);
2017420177
if (SemaRef.getLangOpts().CPlusPlus)

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,31 @@ bool SemaOpenACC::CheckVarIsPointerType(OpenACCClauseKind ClauseKind,
519519
return false;
520520
}
521521

522+
void SemaOpenACC::ActOnStartParseVar(OpenACCDirectiveKind DK,
523+
OpenACCClauseKind CK) {
524+
if (DK == OpenACCDirectiveKind::Cache) {
525+
CacheInfo.ParsingCacheVarList = true;
526+
CacheInfo.IsInvalidCacheRef = false;
527+
}
528+
}
529+
530+
void SemaOpenACC::ActOnInvalidParseVar() {
531+
CacheInfo.ParsingCacheVarList = false;
532+
CacheInfo.IsInvalidCacheRef = false;
533+
}
534+
522535
ExprResult SemaOpenACC::ActOnCacheVar(Expr *VarExpr) {
523536
Expr *CurVarExpr = VarExpr->IgnoreParenImpCasts();
537+
// Clear this here, so we can do the returns based on the invalid cache ref
538+
// here. Note all return statements in this function must return ExprError if
539+
// IsInvalidCacheRef. However, instead of doing an 'early return' in that
540+
// case, we can let the rest of the diagnostics happen, as the invalid decl
541+
// ref is a warning.
542+
bool WasParsingInvalidCacheRef =
543+
CacheInfo.ParsingCacheVarList && CacheInfo.IsInvalidCacheRef;
544+
CacheInfo.ParsingCacheVarList = false;
545+
CacheInfo.IsInvalidCacheRef = false;
546+
524547
if (!isa<ArraySectionExpr, ArraySubscriptExpr>(CurVarExpr)) {
525548
Diag(VarExpr->getExprLoc(), diag::err_acc_not_a_var_ref_cache);
526549
return ExprError();
@@ -540,19 +563,19 @@ ExprResult SemaOpenACC::ActOnCacheVar(Expr *VarExpr) {
540563
if (const auto *DRE = dyn_cast<DeclRefExpr>(CurVarExpr)) {
541564
if (isa<VarDecl, NonTypeTemplateParmDecl>(
542565
DRE->getFoundDecl()->getCanonicalDecl()))
543-
return VarExpr;
566+
return WasParsingInvalidCacheRef ? ExprEmpty() : VarExpr;
544567
}
545568

546569
if (const auto *ME = dyn_cast<MemberExpr>(CurVarExpr)) {
547570
if (isa<FieldDecl>(ME->getMemberDecl()->getCanonicalDecl())) {
548-
return VarExpr;
571+
return WasParsingInvalidCacheRef ? ExprEmpty() : VarExpr;
549572
}
550573
}
551574

552575
// Nothing really we can do here, as these are dependent. So just return they
553576
// are valid.
554577
if (isa<DependentScopeDeclRefExpr, CXXDependentScopeMemberExpr>(CurVarExpr))
555-
return VarExpr;
578+
return WasParsingInvalidCacheRef ? ExprEmpty() : VarExpr;
556579

557580
// There isn't really anything we can do in the case of a recovery expr, so
558581
// skip the diagnostic rather than produce a confusing diagnostic.
@@ -562,6 +585,45 @@ ExprResult SemaOpenACC::ActOnCacheVar(Expr *VarExpr) {
562585
Diag(VarExpr->getExprLoc(), diag::err_acc_not_a_var_ref_cache);
563586
return ExprError();
564587
}
588+
589+
void SemaOpenACC::CheckDeclReference(SourceLocation Loc, Expr *E, Decl *D) {
590+
if (!getLangOpts().OpenACC || !CacheInfo.ParsingCacheVarList || !D ||
591+
D->isInvalidDecl())
592+
return;
593+
// A 'cache' variable reference MUST be declared before the 'acc.loop' we
594+
// generate in codegen, so we have to mark it invalid here in some way. We do
595+
// so in a bit of a convoluted way as there is no good way to put this into
596+
// the AST, so we store it in SemaOpenACC State. We can check the Scope
597+
// during parsing to make sure there is a 'loop' before the decl is
598+
// declared(and skip during instantiation).
599+
// We only diagnose this as a warning, as this isn't required by the standard
600+
// (unless you take a VERY awkward reading of some awkward prose).
601+
602+
Scope *CurScope = SemaRef.getCurScope();
603+
604+
// if we are at TU level, we are either doing some EXTRA wacky, or are in a
605+
// template instantiation, so just give up.
606+
if (CurScope->getDepth() == 0)
607+
return;
608+
609+
while (CurScope) {
610+
// If we run into a loop construct scope, than this is 'correct' in that the
611+
// declaration is outside of the loop.
612+
if (CurScope->isOpenACCLoopConstructScope())
613+
return;
614+
615+
if (CurScope->isDeclScope(D)) {
616+
Diag(Loc, diag::warn_acc_cache_var_not_outside_loop);
617+
618+
CacheInfo.IsInvalidCacheRef = true;
619+
}
620+
621+
CurScope = CurScope->getParent();
622+
}
623+
// If we don't find the decl at all, we assume that it must be outside of the
624+
// loop (or we aren't in a loop!) so skip the diagnostic.
625+
}
626+
565627
ExprResult SemaOpenACC::ActOnVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
566628
Expr *VarExpr) {
567629
// This has unique enough restrictions that we should split it to a separate

clang/test/AST/ast-print-openacc-cache-construct.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
void foo() {
44
int Array[5];
5+
#pragma acc loop
6+
for(int i = 0; i < 5; ++i) {
57
// CHECK: #pragma acc cache(readonly: Array[1], Array[1:2])
68
#pragma acc cache(readonly:Array[1], Array[1:2])
79
// CHECK: #pragma acc cache(Array[1], Array[1:2])
810
#pragma acc cache(Array[1], Array[1:2])
11+
}
912
}

0 commit comments

Comments
 (0)