[clang] [clang][ExprConst] allow single element access of vector object to be constant expression (PR #101126)

Vikram Hegde via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 2 04:05:41 PDT 2024


https://github.com/vikramRH updated https://github.com/llvm/llvm-project/pull/101126

>From 690901f2370381285afa7cf7c2f7401d89e568f6 Mon Sep 17 00:00:00 2001
From: Vikram <Vikram.Hegde at amd.com>
Date: Mon, 29 Jul 2024 08:56:07 -0400
Subject: [PATCH 1/2] [clang][ExprConst] allow single element access of vector
 object to be constant expression

---
 clang/docs/ReleaseNotes.rst                   |   3 +
 clang/lib/AST/ExprConstant.cpp                | 102 +++++++++++++++++-
 clang/lib/AST/Interp/State.h                  |   3 +-
 clang/test/AST/Interp/builtin-functions.cpp   |  26 ++---
 clang/test/AST/Interp/vectors.cpp             |  50 ++++-----
 clang/test/CodeGenCXX/temporaries.cpp         |  41 ++++---
 .../constexpr-vectors-access-elements.cpp     |  29 +++++
 7 files changed, 190 insertions(+), 64 deletions(-)
 create mode 100644 clang/test/SemaCXX/constexpr-vectors-access-elements.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ddad083571eb1..2179aaea12387 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -64,6 +64,9 @@ sections with improvements to Clang's support for those languages.
 
 C++ Language Changes
 --------------------
+- Allow single element access of vector object to be constant expression.
+  Supports the `V.xyzw` syntax and other tidbits as seen in OpenCL.
+  Selecting multiple elements is left as a future work.
 
 C++17 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 558e20ed3e423..08f49ac896153 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -222,6 +222,11 @@ namespace {
         ArraySize = 2;
         MostDerivedLength = I + 1;
         IsArray = true;
+      } else if (const auto *VT = Type->getAs<VectorType>()) {
+        Type = VT->getElementType();
+        ArraySize = VT->getNumElements();
+        MostDerivedLength = I + 1;
+        IsArray = true;
       } else if (const FieldDecl *FD = getAsField(Path[I])) {
         Type = FD->getType();
         ArraySize = 0;
@@ -268,7 +273,6 @@ namespace {
     /// If the current array is an unsized array, the value of this is
     /// undefined.
     uint64_t MostDerivedArraySize;
-
     /// The type of the most derived object referred to by this address.
     QualType MostDerivedType;
 
@@ -442,6 +446,16 @@ namespace {
       MostDerivedArraySize = 2;
       MostDerivedPathLength = Entries.size();
     }
+
+    void addVectorElementUnchecked(QualType EltTy, uint64_t Size,
+                                   uint64_t Idx) {
+      Entries.push_back(PathEntry::ArrayIndex(Idx));
+      MostDerivedType = EltTy;
+      MostDerivedPathLength = Entries.size();
+      MostDerivedArraySize = 0;
+      MostDerivedIsArrayElement = false;
+    }
+
     void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
     void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
                                    const APSInt &N);
@@ -1737,6 +1751,11 @@ namespace {
       if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real))
         Designator.addComplexUnchecked(EltTy, Imag);
     }
+    void addVectorElement(EvalInfo &Info, const Expr *E, QualType EltTy,
+                          uint64_t Size, uint64_t Idx) {
+      if (checkSubobject(Info, E, CSK_VectorElement))
+        Designator.addVectorElementUnchecked(EltTy, Size, Idx);
+    }
     void clearIsNullPointer() {
       IsNullPtr = false;
     }
@@ -3310,6 +3329,19 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
   return true;
 }
 
+static bool HandleLValueVectorElement(EvalInfo &Info, const Expr *E,
+                                      LValue &LVal, QualType EltTy,
+                                      uint64_t Size, uint64_t Idx) {
+  if (Idx) {
+    CharUnits SizeOfElement;
+    if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfElement))
+      return false;
+    LVal.Offset += SizeOfElement * Idx;
+  }
+  LVal.addVectorElement(Info, E, EltTy, Size, Idx);
+  return true;
+}
+
 /// Try to evaluate the initializer for a variable declaration.
 ///
 /// \param Info   Information about the ongoing evaluation.
@@ -3855,6 +3887,19 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
         return handler.found(Index ? O->getComplexFloatImag()
                                    : O->getComplexFloatReal(), ObjType);
       }
+    } else if (const auto *VT = ObjType->getAs<VectorType>()) {
+      uint64_t Index = Sub.Entries[I].getAsArrayIndex();
+      if (Index >= VT->getNumElements()) {
+        if (Info.getLangOpts().CPlusPlus11)
+          Info.FFDiag(E, diag::note_constexpr_access_past_end)
+              << handler.AccessKind;
+        else
+          Info.FFDiag(E);
+        return handler.failed();
+      }
+      ObjType = VT->getElementType();
+      assert(I == N - 1 && "extracting subobject of scalar?");
+      return handler.found(O->getVectorElt(Index), ObjType);
     } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
       if (Field->isMutable() &&
           !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
@@ -8509,6 +8554,7 @@ class LValueExprEvaluator
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
   bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
+  bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
   bool VisitUnaryDeref(const UnaryOperator *E);
   bool VisitUnaryReal(const UnaryOperator *E);
   bool VisitUnaryImag(const UnaryOperator *E);
@@ -8850,15 +8896,63 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
   return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
 }
 
+bool LValueExprEvaluator::VisitExtVectorElementExpr(
+    const ExtVectorElementExpr *E) {
+  bool Success = true;
+
+  APValue Val;
+  if (!Evaluate(Val, Info, E->getBase())) {
+    if (!Info.noteFailure())
+      return false;
+    Success = false;
+  }
+
+  SmallVector<uint32_t, 4> Indices;
+  E->getEncodedElementAccess(Indices);
+  // FIXME: support accessing more than one element
+  if (Indices.size() > 1)
+    return false;
+
+  if (Success) {
+    Result.setFrom(Info.Ctx, Val);
+    const auto *VT = E->getBase()->getType()->castAs<VectorType>();
+    HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
+                              VT->getNumElements(), Indices[0]);
+  }
+
+  return Success;
+}
+
 bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
-  // FIXME: Deal with vectors as array subscript bases.
-  if (E->getBase()->getType()->isVectorType() ||
-      E->getBase()->getType()->isSveVLSBuiltinType())
+  if (E->getBase()->getType()->isSveVLSBuiltinType())
     return Error(E);
 
   APSInt Index;
   bool Success = true;
 
+  if (const auto *VT = E->getBase()->getType()->getAs<VectorType>()) {
+    APValue Val;
+    if (!Evaluate(Val, Info, E->getBase())) {
+      if (!Info.noteFailure())
+        return false;
+      Success = false;
+    }
+
+    if (!EvaluateInteger(E->getIdx(), Index, Info)) {
+      if (!Info.noteFailure())
+        return false;
+      Success = false;
+    }
+
+    if (Success) {
+      Result.setFrom(Info.Ctx, Val);
+      HandleLValueVectorElement(Info, E, Result, VT->getElementType(),
+                                VT->getNumElements(), Index.getExtValue());
+    }
+
+    return Success;
+  }
+
   // C++17's rules require us to evaluate the LHS first, regardless of which
   // side is the base.
   for (const Expr *SubExpr : {E->getLHS(), E->getRHS()}) {
diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h
index f1e8e3618f34f..44d6c037c5ad9 100644
--- a/clang/lib/AST/Interp/State.h
+++ b/clang/lib/AST/Interp/State.h
@@ -44,7 +44,8 @@ enum CheckSubobjectKind {
   CSK_ArrayToPointer,
   CSK_ArrayIndex,
   CSK_Real,
-  CSK_Imag
+  CSK_Imag,
+  CSK_VectorElement
 };
 
 namespace interp {
diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp
index 0a17106449faf..b179298fee9bd 100644
--- a/clang/test/AST/Interp/builtin-functions.cpp
+++ b/clang/test/AST/Interp/builtin-functions.cpp
@@ -866,11 +866,11 @@ namespace convertvector {
   constexpr vector8BitInt128 from_vector8BitInt128_to_vector8BitInt128_var =
       __builtin_convertvector((vector8BitInt128){0, 1, 2, 3, 4, 5, 6, 7},
                               vector8BitInt128);
-  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, ""); // ref-error {{not an integral constant expression}}
-  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, ""); // ref-error {{not an integral constant expression}}
-  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, ""); // ref-error {{not an integral constant expression}}
-  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, ""); // ref-error {{not an integral constant expression}}
-  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, ""); // ref-error {{not an integral constant expression}}
+  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[0] == 0, ""); 
+  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[1] == 1, ""); 
+  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[2] == 2, ""); 
+  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[3] == 3, ""); 
+  static_assert(from_vector8BitInt128_to_vector8BitInt128_var[4] == 4, "");
 }
 
 namespace shufflevector {
@@ -890,14 +890,14 @@ namespace shufflevector {
   constexpr vector8char vectorShuffle6 = __builtin_shufflevector(
       vector4charConst1, vector4charConst2, 0, 2, 4, 6, 1, 3, 5, 7);
 
-  static_assert(vectorShuffle6[0] == 0, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[1] == 2, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[2] == 4, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[3] == 6, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[4] == 1, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[5] == 3, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[6] == 5, "");// ref-error {{not an integral constant expression}}
-  static_assert(vectorShuffle6[7] == 7, "");// ref-error {{not an integral constant expression}}
+  static_assert(vectorShuffle6[0] == 0, "");
+  static_assert(vectorShuffle6[1] == 2, "");
+  static_assert(vectorShuffle6[2] == 4, "");
+  static_assert(vectorShuffle6[3] == 6, "");
+  static_assert(vectorShuffle6[4] == 1, "");
+  static_assert(vectorShuffle6[5] == 3, "");
+  static_assert(vectorShuffle6[6] == 5, "");
+  static_assert(vectorShuffle6[7] == 7, "");
 
   constexpr vector4char  vectorShuffleFail1 = __builtin_shufflevector( // both-error {{must be initialized by a constant expression}}\
                                                                        // ref-error {{index for __builtin_shufflevector not within the bounds of the input vectors; index of -1 found at position 0 is not permitted in a constexpr context}}
diff --git a/clang/test/AST/Interp/vectors.cpp b/clang/test/AST/Interp/vectors.cpp
index 6991a348b07a9..b6a9579f67ee2 100644
--- a/clang/test/AST/Interp/vectors.cpp
+++ b/clang/test/AST/Interp/vectors.cpp
@@ -3,25 +3,25 @@
 
 typedef int __attribute__((vector_size(16))) VI4;
 constexpr VI4 A = {1,2,3,4};
-static_assert(A[0] == 1, ""); // ref-error {{not an integral constant expression}}
-static_assert(A[1] == 2, ""); // ref-error {{not an integral constant expression}}
-static_assert(A[2] == 3, ""); // ref-error {{not an integral constant expression}}
-static_assert(A[3] == 4, ""); // ref-error {{not an integral constant expression}}
+static_assert(A[0] == 1, "");
+static_assert(A[1] == 2, "");
+static_assert(A[2] == 3, "");
+static_assert(A[3] == 4, "");
 
 
 /// FIXME: It would be nice if the note said 'vector' instead of 'array'.
-static_assert(A[12] == 4, ""); // ref-error {{not an integral constant expression}} \
-                               // expected-error {{not an integral constant expression}} \
-                               // expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}
+static_assert(A[12] == 4, ""); // both-error {{not an integral constant expression}} \
+                               // expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}} \
+                               // ref-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
 
 
 /// VectorSplat casts
 typedef __attribute__(( ext_vector_type(4) )) float float4;
 constexpr float4 vec4_0 = (float4)0.5f;
-static_assert(vec4_0[0] == 0.5, ""); // ref-error {{not an integral constant expression}}
-static_assert(vec4_0[1] == 0.5, ""); // ref-error {{not an integral constant expression}}
-static_assert(vec4_0[2] == 0.5, ""); // ref-error {{not an integral constant expression}}
-static_assert(vec4_0[3] == 0.5, ""); // ref-error {{not an integral constant expression}}
+static_assert(vec4_0[0] == 0.5, "");
+static_assert(vec4_0[1] == 0.5, "");
+static_assert(vec4_0[2] == 0.5, "");
+static_assert(vec4_0[3] == 0.5, "");
 constexpr int vec4_0_discarded = ((float4)12.0f, 0);
 
 
@@ -29,14 +29,14 @@ constexpr int vec4_0_discarded = ((float4)12.0f, 0);
 constexpr float4 arr4[2] = {
   {1,2,3,4},
 };
-static_assert(arr4[0][0] == 1, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[0][1] == 2, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[0][2] == 3, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[0][3] == 4, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
-static_assert(arr4[1][0] == 0, ""); // ref-error {{not an integral constant expression}}
+static_assert(arr4[0][0] == 1, "");
+static_assert(arr4[0][1] == 2, "");
+static_assert(arr4[0][2] == 3, "");
+static_assert(arr4[0][3] == 4, "");
+static_assert(arr4[1][0] == 0, "");
+static_assert(arr4[1][0] == 0, "");
+static_assert(arr4[1][0] == 0, "");
+static_assert(arr4[1][0] == 0, "");
 
 
 /// From constant-expression-cxx11.cpp
@@ -65,10 +65,10 @@ namespace {
 namespace BoolToSignedIntegralCast{
   typedef __attribute__((__ext_vector_type__(4))) unsigned int int4;
   constexpr int4 intsT = (int4)true;
-  static_assert(intsT[0] == -1, "");// ref-error {{not an integral constant expression}}
-  static_assert(intsT[1] == -1, "");// ref-error {{not an integral constant expression}}
-  static_assert(intsT[2] == -1, "");// ref-error {{not an integral constant expression}}
-  static_assert(intsT[3] == -1, "");// ref-error {{not an integral constant expression}}
+  static_assert(intsT[0] == -1, "");
+  static_assert(intsT[1] == -1, "");
+  static_assert(intsT[2] == -1, "");
+  static_assert(intsT[3] == -1, "");
 }
 
 namespace VectorElementExpr {
@@ -78,8 +78,8 @@ namespace VectorElementExpr {
   static_assert(oneElt == 3);
 
   constexpr int2 twoElts = ((int4){11, 22, 33, 44}).yz;
-  static_assert(twoElts.x == 22, ""); // ref-error {{not an integral constant expression}}
-  static_assert(twoElts.y == 33, ""); // ref-error {{not an integral constant expression}}
+  static_assert(twoElts.x == 22, "");
+  static_assert(twoElts.y == 33, "");
 }
 
 namespace Temporaries {
diff --git a/clang/test/CodeGenCXX/temporaries.cpp b/clang/test/CodeGenCXX/temporaries.cpp
index 9f697bd9bf3ef..0990c804b8bcf 100644
--- a/clang/test/CodeGenCXX/temporaries.cpp
+++ b/clang/test/CodeGenCXX/temporaries.cpp
@@ -64,6 +64,26 @@ namespace RefTempSubobject {
   constexpr const SelfReferential &sr = SelfReferential();
 }
 
+namespace Vector {
+  typedef __attribute__((vector_size(16))) int vi4a;
+  typedef __attribute__((ext_vector_type(4))) int vi4b;
+  struct S {
+    vi4a v;
+    vi4b w;
+  };
+
+  int &&r = S().v[1];
+  // CHECK: @_ZGRN6Vector1rE_ = internal global i32 0, align 4
+  // CHECK: @_ZN6Vector1rE = constant ptr @_ZGRN6Vector1rE_, align 8
+
+  int &&s = S().w[1];
+  // CHECK: @_ZGRN6Vector1sE_ = internal global i32 0, align 4
+  // CHECK: @_ZN6Vector1sE = constant ptr @_ZGRN6Vector1sE_, align 8
+
+  int &&t = S().w.y;
+  // CHECK: @_ZGRN6Vector1tE_ = internal global i32 0, align 4
+  // CHECK: @_ZN6Vector1tE = constant ptr @_ZGRN6Vector1tE_, align 8
+}
 struct A {
   A();
   ~A();
@@ -665,27 +685,6 @@ namespace Bitfield {
   int &&r = S().a;
 }
 
-namespace Vector {
-  typedef __attribute__((vector_size(16))) int vi4a;
-  typedef __attribute__((ext_vector_type(4))) int vi4b;
-  struct S {
-    vi4a v;
-    vi4b w;
-  };
-  // CHECK: alloca
-  // CHECK: extractelement
-  // CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1rE_
-  // CHECK: store ptr @_ZGRN6Vector1rE_, ptr @_ZN6Vector1rE,
-  int &&r = S().v[1];
-
-  // CHECK: alloca
-  // CHECK: extractelement
-  // CHECK: store i32 {{.*}}, ptr @_ZGRN6Vector1sE_
-  // CHECK: store ptr @_ZGRN6Vector1sE_, ptr @_ZN6Vector1sE,
-  int &&s = S().w[1];
-  int &&ss = S().w.y;
-}
-
 namespace ImplicitTemporaryCleanup {
   struct A { A(int); ~A(); };
   void g();
diff --git a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
new file mode 100644
index 0000000000000..d31db4c496840
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 %s -Wno-uninitialized -std=c++17 -fsyntax-only -verify
+
+namespace Vector {
+
+using TwoIntsVecSize __attribute__((vector_size(8))) = int;
+
+constexpr TwoIntsVecSize a = {1,2};
+static_assert(a[1] == 2);
+static_assert(a[2]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
+
+}
+
+namespace ExtVector {
+
+using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;
+
+constexpr FourIntsExtVec b = {1,2,3,4};
+static_assert(b[0] == 1 && b[1] == 2 && b[2] == 3 && b[3] == 4);
+static_assert(b.s0 == 1 && b.s1 == 2 && b.s2 == 3 && b.s3 == 4);
+static_assert(b.x == 1 && b.y == 2 && b.z == 3 && b.w == 4);
+static_assert(b.r == 1 && b.g == 2 && b.b == 3 && b.a == 4);
+static_assert(b[5]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
+
+// FIXME: support selecting multiple elements
+static_assert(b.lo.lo == 1); // expected-error {{not an integral constant expression}}
+// static_assert(b.lo.lo==1 && b.lo.hi==2 && b.hi.lo == 3 && b.hi.hi == 4);
+// static_assert(b.odd[0]==1 && b.odd[1]==2 && b.even[0] == 3 && b.even[1] == 4);
+
+}

>From a2b8c84d553bd8e9a21f3c6a78ed5ef844e08f61 Mon Sep 17 00:00:00 2001
From: Vikram <Vikram.Hegde at amd.com>
Date: Fri, 2 Aug 2024 07:40:15 +0000
Subject: [PATCH 2/2] review comments

---
 clang/docs/ReleaseNotes.rst                   |  6 +++---
 clang/lib/AST/ExprConstant.cpp                | 10 +++++++++-
 clang/test/AST/Interp/arrays.cpp              |  2 +-
 clang/test/AST/Interp/vectors.cpp             |  3 +--
 .../constexpr-vectors-access-elements.cpp     | 19 ++++++++++++++++++-
 5 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2179aaea12387..ff6e57572c002 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -64,9 +64,9 @@ sections with improvements to Clang's support for those languages.
 
 C++ Language Changes
 --------------------
-- Allow single element access of vector object to be constant expression.
-  Supports the `V.xyzw` syntax and other tidbits as seen in OpenCL.
-  Selecting multiple elements is left as a future work.
+- Allow single element access of GCC vector/ext_vector_type object to be 
+  constant expression. Supports the `V.xyzw` syntax and other tidbits 
+  as seen in OpenCL. Selecting multiple elements is left as a future work.
 
 C++17 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 08f49ac896153..5d34d48d5c740 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3889,7 +3889,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
       }
     } else if (const auto *VT = ObjType->getAs<VectorType>()) {
       uint64_t Index = Sub.Entries[I].getAsArrayIndex();
-      if (Index >= VT->getNumElements()) {
+      unsigned NumElements = VT->getNumElements();
+      if (Index == NumElements) {
         if (Info.getLangOpts().CPlusPlus11)
           Info.FFDiag(E, diag::note_constexpr_access_past_end)
               << handler.AccessKind;
@@ -3897,6 +3898,13 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
           Info.FFDiag(E);
         return handler.failed();
       }
+
+      if (Index > NumElements) {
+        Info.CCEDiag(E, diag::note_constexpr_array_index)
+            << Index << /*array*/ 0 << NumElements;
+        return handler.failed();
+      }
+
       ObjType = VT->getElementType();
       assert(I == N - 1 && "extracting subobject of scalar?");
       return handler.found(O->getVectorElt(Index), ObjType);
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 612bd552aed48..98cd17276e0a0 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -114,7 +114,7 @@ static_assert(*(&arr[0]) == 1, "");
 static_assert(*(&arr[1]) == 2, "");
 
 constexpr const int *OOB = (arr + 3) - 3; // both-error {{must be initialized by a constant expression}} \
-                                          // both-note {{cannot refer to element 3 of array of 2}}
+                                          // both-note {{cannot refer to element 3 of array of 2 elements}}
 
 template<typename T>
 constexpr T getElementOf(T* array, int i) {
diff --git a/clang/test/AST/Interp/vectors.cpp b/clang/test/AST/Interp/vectors.cpp
index b6a9579f67ee2..b25d4d8a2df9a 100644
--- a/clang/test/AST/Interp/vectors.cpp
+++ b/clang/test/AST/Interp/vectors.cpp
@@ -11,8 +11,7 @@ static_assert(A[3] == 4, "");
 
 /// FIXME: It would be nice if the note said 'vector' instead of 'array'.
 static_assert(A[12] == 4, ""); // both-error {{not an integral constant expression}} \
-                               // expected-note {{cannot refer to element 12 of array of 4 elements in a constant expression}} \
-                               // ref-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
+                               // both-note {{cannot refer to element 12 of array of 4 elements in a constant expression}}
 
 
 /// VectorSplat casts
diff --git a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
index d31db4c496840..08223e15feb72 100644
--- a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
+++ b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
@@ -8,6 +8,20 @@ constexpr TwoIntsVecSize a = {1,2};
 static_assert(a[1] == 2);
 static_assert(a[2]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
 
+constexpr struct {
+    TwoIntsVecSize b;
+} Val = {{0,1}};
+
+static_assert(Val.b[1] == 1);
+
+constexpr TwoIntsVecSize c[3] = {{0,1}, {2,3}, {4,5}};
+static_assert(c[0][0] == 0);
+static_assert(c[1][1] == 3);
+static_assert(c[2][3]); // expected-error {{not an integral constant expression}} expected-note {{cannot refer to element 3 of array of 2 elements}}
+
+// make sure clang rejects taking address of a vector element
+static_assert(&a[0]); // expected-error {{address of vector element requested}}
+
 }
 
 namespace ExtVector {
@@ -19,11 +33,14 @@ static_assert(b[0] == 1 && b[1] == 2 && b[2] == 3 && b[3] == 4);
 static_assert(b.s0 == 1 && b.s1 == 2 && b.s2 == 3 && b.s3 == 4);
 static_assert(b.x == 1 && b.y == 2 && b.z == 3 && b.w == 4);
 static_assert(b.r == 1 && b.g == 2 && b.b == 3 && b.a == 4);
-static_assert(b[5]); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
+static_assert(b[5]); // expected-error {{not an integral constant expression}} expected-note {{cannot refer to element 5 of array of 4 elements}}
 
 // FIXME: support selecting multiple elements
 static_assert(b.lo.lo == 1); // expected-error {{not an integral constant expression}}
 // static_assert(b.lo.lo==1 && b.lo.hi==2 && b.hi.lo == 3 && b.hi.hi == 4);
 // static_assert(b.odd[0]==1 && b.odd[1]==2 && b.even[0] == 3 && b.even[1] == 4);
 
+// make sure clang rejects taking address of a vector element
+static_assert(&b[1]); // expected-error {{address of vector element requested}}
+
 }



More information about the cfe-commits mailing list