Skip to content

Commit c129887

Browse files
authored
[OpenACC] Implement SubArray Parsing/Sema (#90796)
This implementation takes quite a bit from the OMP implementation of array sections, but only has to enforce the rules as applicable to OpenACC. Additionally, it does its best to create an AST node (with the assistance of RecoveryExprs) with as much checking done as soon as possible in the case of instantiations.
1 parent 3a1e559 commit c129887

12 files changed

+1131
-42
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12291,8 +12291,8 @@ def warn_acc_if_self_conflict
1229112291
"evaluates to true">,
1229212292
InGroup<DiagGroup<"openacc-self-if-potential-conflict">>;
1229312293
def err_acc_int_expr_requires_integer
12294-
: Error<"OpenACC %select{clause|directive}0 '%1' requires expression of "
12295-
"integer type (%2 invalid)">;
12294+
: Error<"OpenACC %select{clause '%1'|directive '%2'|sub-array bound}0 "
12295+
"requires expression of integer type (%3 invalid)">;
1229612296
def err_acc_int_expr_incomplete_class_type
1229712297
: Error<"OpenACC integer expression has incomplete class type %0">;
1229812298
def err_acc_int_expr_explicit_conversion
@@ -12310,4 +12310,24 @@ def err_acc_num_gangs_num_args
1231012310
def err_acc_not_a_var_ref
1231112311
: Error<"OpenACC variable is not a valid variable name, sub-array, array "
1231212312
"element, or composite variable member">;
12313+
def err_acc_typecheck_subarray_value
12314+
: Error<"OpenACC sub-array subscripted value is not an array or pointer">;
12315+
def err_acc_subarray_function_type
12316+
: Error<"OpenACC sub-array cannot be of function type %0">;
12317+
def err_acc_subarray_incomplete_type
12318+
: Error<"OpenACC sub-array base is of incomplete type %0">;
12319+
def err_acc_subarray_no_length
12320+
: Error<"OpenACC sub-array length is unspecified and cannot be inferred "
12321+
"because the subscripted value is %select{not an array|an array of "
12322+
"unknown bound}0">;
12323+
def err_acc_subarray_negative
12324+
: Error<"OpenACC sub-array %select{lower bound|length}0 evaluated to "
12325+
"negative value %1">;
12326+
def err_acc_subarray_out_of_range
12327+
: Error<"OpenACC sub-array %select{lower bound|length}0 evaluated to a "
12328+
"value (%1) that would be out of the range of the subscripted "
12329+
"array size of %2">;
12330+
def err_acc_subarray_base_plus_length_out_of_range
12331+
: Error<"OpenACC sub-array specified range [%0:%1] would be out of the "
12332+
"range of the subscripted array size of %2">;
1231312333
} // end of sema component.

clang/lib/Parse/ParseExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
20392039
if (Tok.is(tok::colon)) {
20402040
// Consume ':'
20412041
ColonLocFirst = ConsumeToken();
2042-
Length = Actions.CorrectDelayedTyposInExpr(ParseExpression());
2042+
if (Tok.isNot(tok::r_square))
2043+
Length = Actions.CorrectDelayedTyposInExpr(ParseExpression());
20432044
}
20442045
} else if (ArgExprs.size() <= 1 && getLangOpts().OpenMP) {
20452046
ColonProtectionRAIIObject RAII(*this);

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 220 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/Basic/DiagnosticSema.h"
1717
#include "clang/Basic/OpenACCKinds.h"
1818
#include "clang/Sema/Sema.h"
19+
#include "llvm/ADT/StringExtras.h"
1920
#include "llvm/Support/Casting.h"
2021

2122
using namespace clang;
@@ -367,14 +368,26 @@ ExprResult SemaOpenACC::ActOnIntExpr(OpenACCDirectiveKind DK,
367368
assert(((DK != OpenACCDirectiveKind::Invalid &&
368369
CK == OpenACCClauseKind::Invalid) ||
369370
(DK == OpenACCDirectiveKind::Invalid &&
370-
CK != OpenACCClauseKind::Invalid)) &&
371+
CK != OpenACCClauseKind::Invalid) ||
372+
(DK == OpenACCDirectiveKind::Invalid &&
373+
CK == OpenACCClauseKind::Invalid)) &&
371374
"Only one of directive or clause kind should be provided");
372375

373376
class IntExprConverter : public Sema::ICEConvertDiagnoser {
374377
OpenACCDirectiveKind DirectiveKind;
375378
OpenACCClauseKind ClauseKind;
376379
Expr *IntExpr;
377380

381+
// gets the index into the diagnostics so we can use this for clauses,
382+
// directives, and sub array.s
383+
unsigned getDiagKind() const {
384+
if (ClauseKind != OpenACCClauseKind::Invalid)
385+
return 0;
386+
if (DirectiveKind != OpenACCDirectiveKind::Invalid)
387+
return 1;
388+
return 2;
389+
}
390+
378391
public:
379392
IntExprConverter(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
380393
Expr *IntExpr)
@@ -390,12 +403,8 @@ ExprResult SemaOpenACC::ActOnIntExpr(OpenACCDirectiveKind DK,
390403
}
391404
SemaBase::SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
392405
QualType T) override {
393-
if (ClauseKind != OpenACCClauseKind::Invalid)
394-
return S.Diag(Loc, diag::err_acc_int_expr_requires_integer) <<
395-
/*Clause=*/0 << ClauseKind << T;
396-
397-
return S.Diag(Loc, diag::err_acc_int_expr_requires_integer) <<
398-
/*Directive=*/1 << DirectiveKind << T;
406+
return S.Diag(Loc, diag::err_acc_int_expr_requires_integer)
407+
<< getDiagKind() << ClauseKind << DirectiveKind << T;
399408
}
400409

401410
SemaBase::SemaDiagnosticBuilder
@@ -503,12 +512,211 @@ ExprResult SemaOpenACC::ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc,
503512
SourceLocation RBLoc) {
504513
ASTContext &Context = getASTContext();
505514

506-
// TODO OpenACC: We likely have to reproduce a lot of the same logic from the
507-
// OMP version of this, but at the moment we don't have a good way to test it,
508-
// so for now we'll just create the node.
515+
// Handle placeholders.
516+
if (Base->hasPlaceholderType() &&
517+
!Base->hasPlaceholderType(BuiltinType::ArraySection)) {
518+
ExprResult Result = SemaRef.CheckPlaceholderExpr(Base);
519+
if (Result.isInvalid())
520+
return ExprError();
521+
Base = Result.get();
522+
}
523+
if (LowerBound && LowerBound->getType()->isNonOverloadPlaceholderType()) {
524+
ExprResult Result = SemaRef.CheckPlaceholderExpr(LowerBound);
525+
if (Result.isInvalid())
526+
return ExprError();
527+
Result = SemaRef.DefaultLvalueConversion(Result.get());
528+
if (Result.isInvalid())
529+
return ExprError();
530+
LowerBound = Result.get();
531+
}
532+
if (Length && Length->getType()->isNonOverloadPlaceholderType()) {
533+
ExprResult Result = SemaRef.CheckPlaceholderExpr(Length);
534+
if (Result.isInvalid())
535+
return ExprError();
536+
Result = SemaRef.DefaultLvalueConversion(Result.get());
537+
if (Result.isInvalid())
538+
return ExprError();
539+
Length = Result.get();
540+
}
541+
542+
// Check the 'base' value, it must be an array or pointer type, and not to/of
543+
// a function type.
544+
QualType OriginalBaseTy = ArraySectionExpr::getBaseOriginalType(Base);
545+
QualType ResultTy;
546+
if (!Base->isTypeDependent()) {
547+
if (OriginalBaseTy->isAnyPointerType()) {
548+
ResultTy = OriginalBaseTy->getPointeeType();
549+
} else if (OriginalBaseTy->isArrayType()) {
550+
ResultTy = OriginalBaseTy->getAsArrayTypeUnsafe()->getElementType();
551+
} else {
552+
return ExprError(
553+
Diag(Base->getExprLoc(), diag::err_acc_typecheck_subarray_value)
554+
<< Base->getSourceRange());
555+
}
556+
557+
if (ResultTy->isFunctionType()) {
558+
Diag(Base->getExprLoc(), diag::err_acc_subarray_function_type)
559+
<< ResultTy << Base->getSourceRange();
560+
return ExprError();
561+
}
562+
563+
if (SemaRef.RequireCompleteType(Base->getExprLoc(), ResultTy,
564+
diag::err_acc_subarray_incomplete_type,
565+
Base))
566+
return ExprError();
567+
568+
if (!Base->hasPlaceholderType(BuiltinType::ArraySection)) {
569+
ExprResult Result = SemaRef.DefaultFunctionArrayLvalueConversion(Base);
570+
if (Result.isInvalid())
571+
return ExprError();
572+
Base = Result.get();
573+
}
574+
}
575+
576+
auto GetRecovery = [&](Expr *E, QualType Ty) {
577+
ExprResult Recovery =
578+
SemaRef.CreateRecoveryExpr(E->getBeginLoc(), E->getEndLoc(), E, Ty);
579+
return Recovery.isUsable() ? Recovery.get() : nullptr;
580+
};
581+
582+
// Ensure both of the expressions are int-exprs.
583+
if (LowerBound && !LowerBound->isTypeDependent()) {
584+
ExprResult LBRes =
585+
ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Invalid,
586+
LowerBound->getExprLoc(), LowerBound);
587+
588+
if (LBRes.isUsable())
589+
LBRes = SemaRef.DefaultLvalueConversion(LBRes.get());
590+
LowerBound =
591+
LBRes.isUsable() ? LBRes.get() : GetRecovery(LowerBound, Context.IntTy);
592+
}
593+
594+
if (Length && !Length->isTypeDependent()) {
595+
ExprResult LenRes =
596+
ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Invalid,
597+
Length->getExprLoc(), Length);
598+
599+
if (LenRes.isUsable())
600+
LenRes = SemaRef.DefaultLvalueConversion(LenRes.get());
601+
Length =
602+
LenRes.isUsable() ? LenRes.get() : GetRecovery(Length, Context.IntTy);
603+
}
604+
605+
// Length is required if the base type is not an array of known bounds.
606+
if (!Length && (OriginalBaseTy.isNull() ||
607+
(!OriginalBaseTy->isDependentType() &&
608+
!OriginalBaseTy->isConstantArrayType() &&
609+
!OriginalBaseTy->isDependentSizedArrayType()))) {
610+
bool IsArray = !OriginalBaseTy.isNull() && OriginalBaseTy->isArrayType();
611+
Diag(ColonLoc, diag::err_acc_subarray_no_length) << IsArray;
612+
// Fill in a dummy 'length' so that when we instantiate this we don't
613+
// double-diagnose here.
614+
ExprResult Recovery = SemaRef.CreateRecoveryExpr(
615+
ColonLoc, SourceLocation(), ArrayRef<Expr *>{std::nullopt},
616+
Context.IntTy);
617+
Length = Recovery.isUsable() ? Recovery.get() : nullptr;
618+
}
619+
620+
// Check the values of each of the arguments, they cannot be negative(we
621+
// assume), and if the array bound is known, must be within range. As we do
622+
// so, do our best to continue with evaluation, we can set the
623+
// value/expression to nullptr/nullopt if they are invalid, and treat them as
624+
// not present for the rest of evaluation.
625+
626+
// We don't have to check for dependence, because the dependent size is
627+
// represented as a different AST node.
628+
std::optional<llvm::APSInt> BaseSize;
629+
if (!OriginalBaseTy.isNull() && OriginalBaseTy->isConstantArrayType()) {
630+
const auto *ArrayTy = Context.getAsConstantArrayType(OriginalBaseTy);
631+
BaseSize = ArrayTy->getSize();
632+
}
633+
634+
auto GetBoundValue = [&](Expr *E) -> std::optional<llvm::APSInt> {
635+
if (!E || E->isInstantiationDependent())
636+
return std::nullopt;
637+
638+
Expr::EvalResult Res;
639+
if (!E->EvaluateAsInt(Res, Context))
640+
return std::nullopt;
641+
return Res.Val.getInt();
642+
};
643+
644+
std::optional<llvm::APSInt> LowerBoundValue = GetBoundValue(LowerBound);
645+
std::optional<llvm::APSInt> LengthValue = GetBoundValue(Length);
646+
647+
// Check lower bound for negative or out of range.
648+
if (LowerBoundValue.has_value()) {
649+
if (LowerBoundValue->isNegative()) {
650+
Diag(LowerBound->getExprLoc(), diag::err_acc_subarray_negative)
651+
<< /*LowerBound=*/0 << toString(*LowerBoundValue, /*Radix=*/10);
652+
LowerBoundValue.reset();
653+
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
654+
} else if (BaseSize.has_value() &&
655+
llvm::APSInt::compareValues(*LowerBoundValue, *BaseSize) >= 0) {
656+
// Lower bound (start index) must be less than the size of the array.
657+
Diag(LowerBound->getExprLoc(), diag::err_acc_subarray_out_of_range)
658+
<< /*LowerBound=*/0 << toString(*LowerBoundValue, /*Radix=*/10)
659+
<< toString(*BaseSize, /*Radix=*/10);
660+
LowerBoundValue.reset();
661+
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
662+
}
663+
}
664+
665+
// Check length for negative or out of range.
666+
if (LengthValue.has_value()) {
667+
if (LengthValue->isNegative()) {
668+
Diag(Length->getExprLoc(), diag::err_acc_subarray_negative)
669+
<< /*Length=*/1 << toString(*LengthValue, /*Radix=*/10);
670+
LengthValue.reset();
671+
Length = GetRecovery(Length, Length->getType());
672+
} else if (BaseSize.has_value() &&
673+
llvm::APSInt::compareValues(*LengthValue, *BaseSize) > 0) {
674+
// Length must be lessthan or EQUAL to the size of the array.
675+
Diag(Length->getExprLoc(), diag::err_acc_subarray_out_of_range)
676+
<< /*Length=*/1 << toString(*LengthValue, /*Radix=*/10)
677+
<< toString(*BaseSize, /*Radix=*/10);
678+
LengthValue.reset();
679+
Length = GetRecovery(Length, Length->getType());
680+
}
681+
}
682+
683+
// Adding two APSInts requires matching sign, so extract that here.
684+
auto AddAPSInt = [](llvm::APSInt LHS, llvm::APSInt RHS) -> llvm::APSInt {
685+
if (LHS.isSigned() == RHS.isSigned())
686+
return LHS + RHS;
687+
688+
unsigned Width = std::max(LHS.getBitWidth(), RHS.getBitWidth()) + 1;
689+
return llvm::APSInt(LHS.sext(Width) + RHS.sext(Width), /*Signed=*/true);
690+
};
691+
692+
// If we know all 3 values, we can diagnose that the total value would be out
693+
// of range.
694+
if (BaseSize.has_value() && LowerBoundValue.has_value() &&
695+
LengthValue.has_value() &&
696+
llvm::APSInt::compareValues(AddAPSInt(*LowerBoundValue, *LengthValue),
697+
*BaseSize) > 0) {
698+
Diag(Base->getExprLoc(),
699+
diag::err_acc_subarray_base_plus_length_out_of_range)
700+
<< toString(*LowerBoundValue, /*Radix=*/10)
701+
<< toString(*LengthValue, /*Radix=*/10)
702+
<< toString(*BaseSize, /*Radix=*/10);
703+
704+
LowerBoundValue.reset();
705+
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
706+
LengthValue.reset();
707+
Length = GetRecovery(Length, Length->getType());
708+
}
709+
710+
// If any part of the expression is dependent, return a dependent sub-array.
711+
QualType ArrayExprTy = Context.ArraySectionTy;
712+
if (Base->isTypeDependent() ||
713+
(LowerBound && LowerBound->isInstantiationDependent()) ||
714+
(Length && Length->isInstantiationDependent()))
715+
ArrayExprTy = Context.DependentTy;
716+
509717
return new (Context)
510-
ArraySectionExpr(Base, LowerBound, Length, Context.ArraySectionTy,
511-
VK_LValue, OK_Ordinary, ColonLoc, RBLoc);
718+
ArraySectionExpr(Base, LowerBound, Length, ArrayExprTy, VK_LValue,
719+
OK_Ordinary, ColonLoc, RBLoc);
512720
}
513721

514722
bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,

clang/test/ParserOpenACC/parse-cache-construct.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,8 @@ void func() {
134134
}
135135

136136
for (int i = 0; i < 10; ++i) {
137-
// expected-error@+2{{expected expression}}
138137
// expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}}
139-
#pragma acc cache(readonly:ArrayPtr[5:])
138+
#pragma acc cache(readonly:ArrayPtr[5:1])
140139
}
141140

142141
for (int i = 0; i < 10; ++i) {

clang/test/ParserOpenACC/parse-cache-construct.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ void use() {
7474
for (int i = 0; i < 10; ++i) {
7575
// expected-error@+2{{OpenACC sub-array is not allowed here}}
7676
// expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}}
77-
#pragma acc cache(Arrs.MemArr[3:4].array[1:4])
77+
#pragma acc cache(Arrs.MemArr[2:1].array[1:4])
7878
}
7979
for (int i = 0; i < 10; ++i) {
8080
// expected-error@+2{{OpenACC sub-array is not allowed here}}
8181
// expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}}
82-
#pragma acc cache(Arrs.MemArr[3:4].array[4])
82+
#pragma acc cache(Arrs.MemArr[2:1].array[4])
8383
}
8484
for (int i = 0; i < 10; ++i) {
8585
// expected-error@+3{{expected ']'}}
@@ -88,7 +88,7 @@ void use() {
8888
#pragma acc cache(Arrs.MemArr[3:4:].array[4])
8989
}
9090
for (int i = 0; i < 10; ++i) {
91-
// expected-error@+2{{expected expression}}
91+
// expected-error@+2{{OpenACC sub-array is not allowed here}}
9292
// expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}}
9393
#pragma acc cache(Arrs.MemArr[:].array[4])
9494
}
@@ -105,7 +105,7 @@ void use() {
105105
#pragma acc cache(Arrs.MemArr[: :].array[4])
106106
}
107107
for (int i = 0; i < 10; ++i) {
108-
// expected-error@+2{{expected expression}}
108+
// expected-error@+2{{OpenACC sub-array is not allowed here}}
109109
// expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}}
110110
#pragma acc cache(Arrs.MemArr[3:].array[4])
111111
}

clang/test/ParserOpenACC/parse-clauses.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,6 @@ void VarListClauses() {
499499
#pragma acc serial copy(HasMem.MemArr[1:3].array[1:2]), seq
500500
for(;;){}
501501

502-
// expected-error@+3{{expected expression}}
503502
// expected-warning@+2{{OpenACC clause 'copy' not yet implemented, clause ignored}}
504503
// expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}}
505504
#pragma acc serial copy(HasMem.MemArr[:]), seq
@@ -519,7 +518,6 @@ void VarListClauses() {
519518
#pragma acc serial copy(HasMem.MemArr[: :]), seq
520519
for(;;){}
521520

522-
// expected-error@+3{{expected expression}}
523521
// expected-warning@+2{{OpenACC clause 'copy' not yet implemented, clause ignored}}
524522
// expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}}
525523
#pragma acc serial copy(HasMem.MemArr[3:]), seq

0 commit comments

Comments
 (0)