[cfe-dev] OpenCL vec_step feature implementation - Updated patch
Peter Collingbourne
peter at pcc.me.uk
Wed Feb 23 12:44:36 PST 2011
On Tue, Feb 22, 2011 at 11:05:37PM +0200, Benyei, Guy wrote:
> Hello,
> Here is another update for our vec_step patch. The changes I did in this patch:
>
> Changed the new expression name that includes sizeof, alignof and vec_step to UnaryExprOrTypeTraitExpr.
> Fail compilation on vec_step of derived types.
> Added negative test cases.
> Fixed some comments.
>
> Please review.
Comments are inlined below.
> Index: include/clang/AST/Expr.h
> ===================================================================
> --- include/clang/AST/Expr.h (revision 126222)
> +++ include/clang/AST/Expr.h (working copy)
> @@ -1556,10 +1556,15 @@
> }
> };
>
> -/// SizeOfAlignOfExpr - [C99 6.5.3.4] - This is for sizeof/alignof, both of
> -/// types and expressions.
> -class SizeOfAlignOfExpr : public Expr {
> - bool isSizeof : 1; // true if sizeof, false if alignof.
> +/// UnaryExprOrTypeTraitExpr - either:
> +/// [C99 6.5.3.4] - This is for sizeof/alignof, both of types and expressions.
> +/// [OpenCL 1.1 6.11.12] - The vec_step builtin function, which works on both
> +/// types and expressions
> +class UnaryExprOrTypeTraitExpr : public Expr {
> +public:
> + enum Kind_t { SizeOf, AlignOf, VecStep };
This enum should live in include/clang/Basic/TypeTraits.h with the
other type trait kind enums.
> +private:
> + Kind_t Kind;
This should be a bit-field so that we share storage space with isType.
> bool isType : 1; // true if operand is a type, false if an expression
> union {
> TypeSourceInfo *Ty;
> @@ -1568,36 +1573,36 @@
> SourceLocation OpLoc, RParenLoc;
>
> public:
> - SizeOfAlignOfExpr(bool issizeof, TypeSourceInfo *TInfo,
> + UnaryExprOrTypeTraitExpr(Kind_t exprKind, TypeSourceInfo *TInfo,
> QualType resultType, SourceLocation op,
> SourceLocation rp) :
> - Expr(SizeOfAlignOfExprClass, resultType, VK_RValue, OK_Ordinary,
> + Expr(UnaryExprOrTypeTraitExprClass, resultType, VK_RValue, OK_Ordinary,
> false, // Never type-dependent (C++ [temp.dep.expr]p3).
> // Value-dependent if the argument is type-dependent.
> TInfo->getType()->isDependentType(),
> TInfo->getType()->containsUnexpandedParameterPack()),
> - isSizeof(issizeof), isType(true), OpLoc(op), RParenLoc(rp) {
> + Kind(exprKind), isType(true), OpLoc(op), RParenLoc(rp) {
> Argument.Ty = TInfo;
> }
>
> - SizeOfAlignOfExpr(bool issizeof, Expr *E,
> + UnaryExprOrTypeTraitExpr(Kind_t exprKind, Expr *E,
> QualType resultType, SourceLocation op,
> SourceLocation rp) :
> - Expr(SizeOfAlignOfExprClass, resultType, VK_RValue, OK_Ordinary,
> + Expr(UnaryExprOrTypeTraitExprClass, resultType, VK_RValue, OK_Ordinary,
> false, // Never type-dependent (C++ [temp.dep.expr]p3).
> // Value-dependent if the argument is type-dependent.
> E->isTypeDependent(),
> E->containsUnexpandedParameterPack()),
> - isSizeof(issizeof), isType(false), OpLoc(op), RParenLoc(rp) {
> + Kind(exprKind), isType(false), OpLoc(op), RParenLoc(rp) {
> Argument.Ex = E;
> }
>
> /// \brief Construct an empty sizeof/alignof expression.
> - explicit SizeOfAlignOfExpr(EmptyShell Empty)
> - : Expr(SizeOfAlignOfExprClass, Empty) { }
> + explicit UnaryExprOrTypeTraitExpr(EmptyShell Empty)
> + : Expr(UnaryExprOrTypeTraitExprClass, Empty) { }
>
> - bool isSizeOf() const { return isSizeof; }
> - void setSizeof(bool S) { isSizeof = S; }
> + Kind_t getKind() const { return Kind; }
> + void setKind(Kind_t K) { Kind = K; }
>
> bool isArgumentType() const { return isType; }
> QualType getArgumentType() const {
> @@ -1612,7 +1617,7 @@
> return static_cast<Expr*>(Argument.Ex);
> }
> const Expr *getArgumentExpr() const {
> - return const_cast<SizeOfAlignOfExpr*>(this)->getArgumentExpr();
> + return const_cast<UnaryExprOrTypeTraitExpr*>(this)->getArgumentExpr();
> }
>
> void setArgument(Expr *E) { Argument.Ex = E; isType = false; }
> @@ -1638,9 +1643,9 @@
> }
>
> static bool classof(const Stmt *T) {
> - return T->getStmtClass() == SizeOfAlignOfExprClass;
> + return T->getStmtClass() == UnaryExprOrTypeTraitExprClass;
> }
> - static bool classof(const SizeOfAlignOfExpr *) { return true; }
> + static bool classof(const UnaryExprOrTypeTraitExpr *) { return true; }
>
> // Iterators
> child_range children();
> Index: include/clang/Basic/TokenKinds.def
> ===================================================================
> --- include/clang/Basic/TokenKinds.def (revision 126222)
> +++ include/clang/Basic/TokenKinds.def (working copy)
> @@ -345,10 +345,12 @@
> KEYWORD(__thiscall , KEYALL)
> KEYWORD(__forceinline , KEYALL)
>
> -// OpenCL-specific keywords (see OpenCL 1.1 [6.1.9])
> +// OpenCL-specific keywords (see OpenCL 1.1)
> KEYWORD(__kernel , KEYOPENCL)
> ALIAS("kernel", __kernel , KEYOPENCL)
> +KEYWORD(vec_step , KEYOPENCL)
>
> +
You can remove this newline.
> // Borland Extensions.
> KEYWORD(__pascal , KEYALL)
>
> Index: lib/AST/ExprConstant.cpp
> ===================================================================
> --- lib/AST/ExprConstant.cpp (revision 126222)
> +++ lib/AST/ExprConstant.cpp (working copy)
> @@ -290,7 +290,8 @@
> bool VisitFloatingLiteral(FloatingLiteral *E) { return false; }
> bool VisitStringLiteral(StringLiteral *E) { return false; }
> bool VisitCharacterLiteral(CharacterLiteral *E) { return false; }
> - bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { return false; }
> + bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E)
> + { return false; }
> bool VisitArraySubscriptExpr(ArraySubscriptExpr *E)
> { return Visit(E->getLHS()) || Visit(E->getRHS()); }
> bool VisitChooseExpr(ChooseExpr *E)
> @@ -1015,7 +1016,7 @@
> bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E);
>
> bool VisitCastExpr(CastExpr* E);
> - bool VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E);
> + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E);
>
> bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) {
> return Success(E->getValue(), E);
> @@ -1598,15 +1599,36 @@
>
> /// VisitSizeAlignOfExpr - Evaluate a sizeof or alignof with a result as the
> /// expression's type.
> -bool IntExprEvaluator::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) {
> +bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
> + const UnaryExprOrTypeTraitExpr *E) {
> // Handle alignof separately.
> - if (!E->isSizeOf()) {
> + if (E->getKind() == UnaryExprOrTypeTraitExpr::AlignOf) {
> if (E->isArgumentType())
> return Success(GetAlignOfType(E->getArgumentType()).getQuantity(), E);
> else
> return Success(GetAlignOfExpr(E->getArgumentExpr()).getQuantity(), E);
> }
>
> + if (E->getKind() == UnaryExprOrTypeTraitExpr::VecStep) {
> + clang::QualType Ty;
> + if (E->isArgumentType())
> + Ty = E->getArgumentType();
> + else
> + Ty = E->getArgumentExpr()->getType();
> +
> + if (Ty->isVectorType()) {
> + unsigned n = Ty->getAs<clang::VectorType>()->getNumElements();
> +
> + // The vec_step built-in functions that take a 3-component
> + // vector return 4. (OpenCL 1.1 spec 6.11.12)
> + if (n == 3)
> + n = 4;
> +
> + return Success(n, E);
> + } else
> + return Success(1, E);
> + }
> +
> QualType SrcTy = E->getTypeOfArgument();
> // C++ [expr.sizeof]p2: "When applied to a reference or a reference type,
> // the result is the size of the referenced type."
This should use a case statement without a default so that we get a
compiler warning if we add another trait without changing this code.
> @@ -2879,9 +2901,10 @@
> // are ICEs, the value of the offsetof must be an integer constant.
> return CheckEvalInICE(E, Ctx);
> }
> - case Expr::SizeOfAlignOfExprClass: {
> - const SizeOfAlignOfExpr *Exp = cast<SizeOfAlignOfExpr>(E);
> - if (Exp->isSizeOf() && Exp->getTypeOfArgument()->isVariableArrayType())
> + case Expr::UnaryExprOrTypeTraitExprClass: {
> + const UnaryExprOrTypeTraitExpr *Exp = cast<UnaryExprOrTypeTraitExpr>(E);
> + if ((Exp->getKind() == UnaryExprOrTypeTraitExpr::SizeOf) &&
> + Exp->getTypeOfArgument()->isVariableArrayType())
> return ICEDiag(2, E->getLocStart());
> return NoDiag();
> }
> Index: lib/AST/ItaniumMangle.cpp
> ===================================================================
> --- lib/AST/ItaniumMangle.cpp (revision 126222)
> +++ lib/AST/ItaniumMangle.cpp (working copy)
> @@ -1874,10 +1874,11 @@
> break;
> }
>
> - case Expr::SizeOfAlignOfExprClass: {
> - const SizeOfAlignOfExpr *SAE = cast<SizeOfAlignOfExpr>(E);
> - if (SAE->isSizeOf()) Out << 's';
> - else Out << 'a';
> + case Expr::UnaryExprOrTypeTraitExprClass: {
> + const UnaryExprOrTypeTraitExpr *SAE = cast<UnaryExprOrTypeTraitExpr>(E);
> + if (SAE->getKind() == UnaryExprOrTypeTraitExpr::SizeOf) Out << 's';
> + else if (SAE->getKind() == UnaryExprOrTypeTraitExpr::AlignOf) Out << 'a';
> + else Out << 'v';
Case statement here also.
This encoding does not conform with the Itanium ABI (it clashes with
the encoding for 'vendor extended operators'). We should error out
here for vec_step until we have a proper encoding.
> if (SAE->isArgumentType()) {
> Out << 't';
> mangleType(SAE->getArgumentType());
> Index: lib/AST/StmtDumper.cpp
> ===================================================================
> --- lib/AST/StmtDumper.cpp (revision 126222)
> +++ lib/AST/StmtDumper.cpp (working copy)
> @@ -141,7 +141,7 @@
> void VisitFloatingLiteral(FloatingLiteral *Node);
> void VisitStringLiteral(StringLiteral *Str);
> void VisitUnaryOperator(UnaryOperator *Node);
> - void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node);
> + void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node);
> void VisitMemberExpr(MemberExpr *Node);
> void VisitExtVectorElementExpr(ExtVectorElementExpr *Node);
> void VisitBinaryOperator(BinaryOperator *Node);
> @@ -441,9 +441,21 @@
> OS << " " << (Node->isPostfix() ? "postfix" : "prefix")
> << " '" << UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
> }
> -void StmtDumper::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) {
> +void StmtDumper::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
> DumpExpr(Node);
> - OS << " " << (Node->isSizeOf() ? "sizeof" : "alignof") << " ";
> + switch(Node->getKind()) {
> + default:
> + assert(false && "Unknown kind for UnaryExprOrTypeTraitExpr");
> + case UnaryExprOrTypeTraitExpr::SizeOf:
> + OS << "sizeof";
> + break;
> + case UnaryExprOrTypeTraitExpr::AlignOf:
> + OS << "__alignof";
> + break;
> + case UnaryExprOrTypeTraitExpr::VecStep:
> + OS << "vec_step";
> + break;
> + }
No default here.
> if (Node->isArgumentType())
> DumpType(Node->getArgumentType());
> }
> Index: lib/AST/StmtPrinter.cpp
> ===================================================================
> --- lib/AST/StmtPrinter.cpp (revision 126222)
> +++ lib/AST/StmtPrinter.cpp (working copy)
> @@ -706,8 +706,21 @@
> OS << ")";
> }
>
> -void StmtPrinter::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) {
> - OS << (Node->isSizeOf() ? "sizeof" : "__alignof");
> +void StmtPrinter::VisitUnaryExprOrTypeTraitExpr(
> + UnaryExprOrTypeTraitExpr *Node) {
> + switch(Node->getKind()) {
> + default:
> + assert(false && "Unknown kind for UnaryExprOrTypeTraitExpr");
> + case UnaryExprOrTypeTraitExpr::SizeOf:
> + OS << "sizeof";
> + break;
> + case UnaryExprOrTypeTraitExpr::AlignOf:
> + OS << "__alignof";
> + break;
> + case UnaryExprOrTypeTraitExpr::VecStep:
> + OS << "vec_step";
> + break;
> + }
No default.
> if (Node->isArgumentType())
> OS << "(" << Node->getArgumentType().getAsString(Policy) << ")";
> else {
> Index: lib/Frontend/StmtXML.cpp
> ===================================================================
> --- lib/Frontend/StmtXML.cpp (revision 126222)
> +++ lib/Frontend/StmtXML.cpp (working copy)
> @@ -36,7 +36,7 @@
> Str->getString().size()));
> }
>
> - void addSpecialAttribute(const char* pName, SizeOfAlignOfExpr* S) {
> + void addSpecialAttribute(const char* pName, UnaryExprOrTypeTraitExpr* S) {
> if (S->isArgumentType())
> Doc.addAttribute(pName, S->getArgumentType());
> }
> @@ -126,7 +126,7 @@
> void VisitStringLiteral(StringLiteral *Str);
> void VisitUnaryOperator(UnaryOperator *Node);
> void VisitOffsetOfExpr(OffsetOfExpr *Node);
> - void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node);
> + void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node);
> void VisitMemberExpr(MemberExpr *Node);
> void VisitExtVectorElementExpr(ExtVectorElementExpr *Node);
> void VisitBinaryOperator(BinaryOperator *Node);
> @@ -310,7 +310,7 @@
> DumpExpr(Node);
> }
>
> -void StmtXML::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) {
> +void StmtXML::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
> DumpExpr(Node);
> Doc.addAttribute("is_sizeof", Node->isSizeOf() ? "sizeof" : "alignof");
This should be changed to use getKind.
> Index: lib/Sema/SemaExpr.cpp
> ===================================================================
> --- lib/Sema/SemaExpr.cpp (revision 126222)
> +++ lib/Sema/SemaExpr.cpp (working copy)
> @@ -2716,10 +2716,10 @@
>
> /// The UsualUnaryConversions() function is *not* called by this routine.
> /// See C99 6.3.2.1p[2-4] for more details.
> -bool Sema::CheckSizeOfAlignOfOperand(QualType exprType,
> - SourceLocation OpLoc,
> - SourceRange ExprRange,
> - bool isSizeof) {
> +bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType exprType,
> + SourceLocation OpLoc,
> + SourceRange ExprRange,
> + UnaryExprOrTypeTraitExpr::Kind_t exprKind) {
> if (exprType->isDependentType())
> return false;
>
> @@ -2733,28 +2733,36 @@
> // C99 6.5.3.4p1:
> if (exprType->isFunctionType()) {
> // alignof(function) is allowed as an extension.
> - if (isSizeof)
> - Diag(OpLoc, diag::ext_sizeof_function_type) << ExprRange;
> - return false;
> + if (exprKind != UnaryExprOrTypeTraitExpr::AlignOf)
> + Diag(OpLoc, diag::ext_sizeof_vecstep_function_type)
> + << exprKind << ExprRange;
> + return false;
> }
>
> // Allow sizeof(void)/alignof(void) as an extension.
> if (exprType->isVoidType()) {
> Diag(OpLoc, diag::ext_sizeof_void_type)
> - << (isSizeof ? "sizeof" : "__alignof") << ExprRange;
> + << exprKind << ExprRange;
> return false;
> }
>
> if (RequireCompleteType(OpLoc, exprType,
> PDiag(diag::err_sizeof_alignof_incomplete_type)
> - << int(!isSizeof) << ExprRange))
> + << exprKind << ExprRange))
> return true;
>
> + if(exprType->isDerivedType() && exprKind == UnaryExprOrTypeTraitExpr::VecStep)
> + {
> + Diag(OpLoc, diag::err_vecstep_derived_type) << exprType << ExprRange;
> + return true;
> + }
> +
> // Reject sizeof(interface) and sizeof(interface<proto>) in 64-bit mode.
> if (LangOpts.ObjCNonFragileABI && exprType->isObjCObjectType()) {
> Diag(OpLoc, diag::err_sizeof_nonfragile_interface)
> - << exprType << isSizeof << ExprRange;
> - return true;
> + << exprType << (exprKind == UnaryExprOrTypeTraitExpr::SizeOf)
> + << ExprRange;
> + return true;
"return true;" shouldn't change indentation here.
> }
>
> return false;
> @@ -2783,25 +2791,45 @@
> if (isa<FieldDecl>(ME->getMemberDecl()))
> return false;
>
> - return S.CheckSizeOfAlignOfOperand(E->getType(), OpLoc, ExprRange, false);
> + return S.CheckUnaryExprOrTypeTraitOperand(E->getType(), OpLoc, ExprRange,
> + UnaryExprOrTypeTraitExpr::AlignOf);
> }
>
> +bool Sema::CheckVecStepExpr(Expr *E, SourceLocation OpLoc,
> + SourceRange ExprRange) {
> + if (!getLangOptions().OpenCL) {
> + // vec_step is supported in OpenCL only
> + Diag(OpLoc, diag::err_vecstep_not_in_opencl);
> + return true;
> + }
We shouldn't reach here unless we're in OpenCL mode because the
keyword is OpenCL-only.
> Index: lib/StaticAnalyzer/Checkers/ExprEngine.cpp
> ===================================================================
> --- lib/StaticAnalyzer/Checkers/ExprEngine.cpp (revision 126222)
> +++ lib/StaticAnalyzer/Checkers/ExprEngine.cpp (working copy)
> @@ -1085,8 +1085,9 @@
> VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
> break;
>
> - case Stmt::SizeOfAlignOfExprClass:
> - VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), Pred, Dst);
> + case Stmt::UnaryExprOrTypeTraitExprClass:
> + VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S),
> + Pred, Dst);
> break;
>
> case Stmt::StmtExprClass: {
> @@ -2816,14 +2817,15 @@
> assert(0 && "unprocessed InitListExpr type");
> }
>
> -/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type).
> -void ExprEngine::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex,
> +/// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof(type).
> +void ExprEngine::VisitUnaryExprOrTypeTraitExpr(
> + const UnaryExprOrTypeTraitExpr* Ex,
> ExplodedNode* Pred,
> ExplodedNodeSet& Dst) {
> QualType T = Ex->getTypeOfArgument();
> CharUnits amt;
>
> - if (Ex->isSizeOf()) {
> + if (Ex->getKind() == UnaryExprOrTypeTraitExpr::SizeOf) {
> if (T == getContext().VoidTy) {
> // sizeof(void) == 1 byte.
> amt = CharUnits::One();
> @@ -2874,9 +2876,26 @@
> amt = getContext().getTypeSizeInChars(T);
> }
> }
> - else // Get alignment of the type.
> - amt = getContext().getTypeAlignInChars(T);
> + else if (Ex->getKind() == UnaryExprOrTypeTraitExpr::AlignOf) {
> + // Get alignment of the type.
> + amt = getContext().getTypeAlignInChars(T);
> + }
> + else {
> + if (T->isVectorType()) {
> + int n = T->getAs<clang::VectorType>()->getNumElements();
>
> + // The vec_step built-in functions that take a 3-component
> + // vector return 4. (OpenCL 1.1 spec 6.11.12)
> + if (n == 3)
> + n = 4;
> +
> + amt = CharUnits::fromQuantity(n);
> + }
> + else {
> + amt = CharUnits::fromQuantity(1);
> + }
> + }
> +
We should be able to replace all non-special cases here with a call
to the constant expression evaluator.
> Index: test/CodeGenOpenCL/vec_step.cl
> ===================================================================
> --- test/CodeGenOpenCL/vec_step.cl (revision 0)
> +++ test/CodeGenOpenCL/vec_step.cl (revision 0)
> @@ -0,0 +1,45 @@
> +// RUN: %clang_cc1 %s -emit-llvm -D NO_NEGATIVE_CASES -O0 -o - | FileCheck %s
> +// RUN: %clang_cc1 -fsyntax-only -verify %s
> +
> +typedef int int2 __attribute__((ext_vector_type(2)));
> +typedef int int3 __attribute__((ext_vector_type(3)));
> +typedef int int4 __attribute__((ext_vector_type(4)));
> +typedef int int8 __attribute__((ext_vector_type(8)));
> +typedef int int16 __attribute__((ext_vector_type(16)));
> +
> +void foo(int3 arg1, int8 arg2) {
> + int4 auto1;
> + int16 *auto2;
> + int auto3;
> + int2 auto4;
> + struct S *incomplete1; // expected-note{{forward declaration of 'struct S'}}
> +
> +
> + int res1 = vec_step(arg1);
> +// CHECK: store i32 4, i32* %res1
> + int res2 = vec_step(arg2);
> +// CHECK: store i32 8, i32* %res2
> + int res3 = vec_step(auto1);
> +// CHECK: store i32 4, i32* %res3
> + int res4 = vec_step(*auto2);
> +// CHECK: store i32 16, i32* %res4
> + int res5 = vec_step(auto3);
> +// CHECK: store i32 1, i32* %res5
> + int res6 = vec_step(auto4);
> +// CHECK: store i32 2, i32* %res6
> + int res7 = vec_step(int2);
> +// CHECK: store i32 2, i32* %res7
> + int res8 = vec_step(int3);
> +// CHECK: store i32 4, i32* %res8
> + int res9 = vec_step(int4);
> +// CHECK: store i32 4, i32* %res9
> + int res10 = vec_step(int8);
> +// CHECK: store i32 8, i32* %res10
> + int res11 = vec_step(int16);
> +// CHECK: store i32 16, i32* %res11
> +
> +#ifndef NO_NEGATIVE_CASES
> + int res12 = vec_step(*incomplete1); // expected-error{{invalid application of 'vec_step' to an incomplete type 'struct S'}}
> + int res13 = vec_step(int16*); // expected-error{{invalid application of vec_step to derived type 'int16 *'}}
> +#endif
> +}
Normally we test integral constant expressions in the Clang test
suite using the
int array[foo ? 1 : -1];
idiom. See for example the test/Sema/expr-comma.c test case.
You should be able to move this test case to SemaOpenCL and you
shouldn't need to use the NO_NEGATIVE_CASES macro if using this idiom.
Thanks,
--
Peter
More information about the cfe-dev
mailing list