[clang] [clang][bytecode] Improve `getType()` (PR #200342)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri May 29 18:42:52 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/200342
>From c3f0c543c9c822c8a0f16d25e8f0e9ab4fdcc6a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 29 May 2026 09:29:50 +0200
Subject: [PATCH] gettype
---
clang/lib/AST/ByteCode/Descriptor.cpp | 53 ++--
clang/lib/AST/ByteCode/Descriptor.h | 6 +-
clang/lib/AST/ByteCode/DynamicAllocator.cpp | 8 +-
clang/lib/AST/ByteCode/Pointer.h | 25 +-
clang/lib/AST/ByteCode/Program.cpp | 29 ++-
clang/unittests/AST/ByteCode/CMakeLists.txt | 1 +
clang/unittests/AST/ByteCode/Pointer.cpp | 274 ++++++++++++++++++++
7 files changed, 348 insertions(+), 48 deletions(-)
create mode 100644 clang/unittests/AST/ByteCode/Pointer.cpp
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index 2d7561480f645..dfe539ca85e04 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -301,14 +301,14 @@ Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
}
/// Primitive arrays.
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
- size_t NumElems, bool IsConst, bool IsTemporary,
- bool IsMutable)
- : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
- MDSize(MD.value_or(0)),
+Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
+ MetadataSize MD, size_t NumElems, bool IsConst,
+ bool IsTemporary, bool IsMutable, bool IsVolatile)
+ : Source(D), SourceType(SourceTy), ElemSize(primSize(Type)),
+ Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
- IsArray(true), CtorFn(getCtorArrayPrim(Type)),
+ IsVolatile(IsVolatile), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)) {
assert(Source && "Missing source");
assert(NumElems <= (MaxArrayElemBytes / ElemSize));
@@ -375,8 +375,7 @@ Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
QualType Descriptor::getType() const {
if (SourceType)
return QualType(SourceType, 0);
- if (const auto *D = asValueDecl())
- return D->getType();
+
if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl()))
return T->getASTContext().getTypeDeclType(T);
@@ -384,10 +383,28 @@ QualType Descriptor::getType() const {
// we really save. Try to consult the Record first.
if (isRecord()) {
const RecordDecl *RD = ElemRecord->getDecl();
- return RD->getASTContext().getCanonicalTagType(RD);
+ QualType T = RD->getASTContext().getTagType(ElaboratedTypeKeyword::None,
+ std::nullopt, RD, false);
+ if (IsConst)
+ return T.withConst();
+ return T;
}
- if (const auto *E = asExpr())
+
+ if (const auto *E = asExpr()) {
+ if (isa<CXXNewExpr>(E))
+ return E->getType()->getPointeeType();
+
+ // std::allocator.allocate() call.
+ if (const auto *ME = dyn_cast<CXXMemberCallExpr>(E);
+ ME && ME->getRecordDecl()->getName() == "allocator" &&
+ ME->getMethodDecl()->getName() == "allocate")
+ return E->getType()->getPointeeType();
return E->getType();
+ }
+
+ if (const auto *D = asValueDecl())
+ return D->getType();
+
llvm_unreachable("Invalid descriptor type");
}
@@ -441,22 +458,6 @@ QualType Descriptor::getDataType(const ASTContext &Ctx) const {
return getType();
}
-QualType Descriptor::getDataElemType() const {
- if (const auto *E = asExpr()) {
- if (isa<CXXNewExpr>(E))
- return E->getType()->getPointeeType();
-
- // std::allocator.allocate() call.
- if (const auto *ME = dyn_cast<CXXMemberCallExpr>(E);
- ME && ME->getRecordDecl()->getName() == "allocator" &&
- ME->getMethodDecl()->getName() == "allocate")
- return E->getType()->getPointeeType();
- return E->getType();
- }
-
- return getType();
-}
-
SourceLocation Descriptor::getLocation() const {
if (auto *D = dyn_cast<const Decl *>(Source))
return D->getLocation();
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index 498a01e8f070c..a2df48cf1e7fb 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -179,8 +179,9 @@ struct Descriptor final {
bool IsVolatile);
/// Allocates a descriptor for an array of primitives.
- Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
- bool IsConst, bool IsTemporary, bool IsMutable);
+ Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
+ MetadataSize MD, size_t NumElems, bool IsConst, bool IsTemporary,
+ bool IsMutable, bool IsVolatile);
/// Allocates a descriptor for an array of primitives of unknown size.
Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize, bool IsConst,
@@ -205,7 +206,6 @@ struct Descriptor final {
QualType getType() const;
QualType getElemQualType() const;
QualType getDataType(const ASTContext &Ctx) const;
- QualType getDataElemType() const;
SourceLocation getLocation() const;
SourceInfo getLoc() const;
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index 5f53fac923682..36e0bfd666b74 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -39,9 +39,11 @@ Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
Form AllocForm) {
// Create a new descriptor for an array of the specified size and
// element type.
- const Descriptor *D = allocateDescriptor(
- Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
- /*IsTemporary=*/false, /*IsMutable=*/false);
+ const Descriptor *D =
+ allocateDescriptor(Source, nullptr, T, Descriptor::InlineDescMD,
+ NumElements, /*IsConst=*/false,
+ /*IsTemporary=*/false, /*IsMutable=*/false,
+ /*IsVolatile=*/false);
return allocate(D, EvalID, AllocForm);
}
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 3eb9cfc4e53db..1a4aa48e55161 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -349,6 +349,15 @@ class Pointer {
if (isFunctionPointer())
return Fn.Func->getDecl()->getType();
+ if (isRoot() && BS.Base == Offset) {
+ // If this pointer points to the root of a declaration, try to consult
+ // the ValueDecl directly, since that has a type with more information,
+ // e.g. the correct ElaboratedTypeKeyword.
+ if (const ValueDecl *VD = getDeclDesc()->asValueDecl())
+ return VD->getType();
+ return getDeclDesc()->getType();
+ }
+
if (inPrimitiveArray() && Offset != BS.Base) {
// Unfortunately, complex and vector types are not array types in clang,
// but they are for us.
@@ -360,7 +369,7 @@ class Pointer {
return CT->getElementType();
}
- return getFieldDesc()->getDataElemType();
+ return getFieldDesc()->getType();
}
[[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
@@ -902,8 +911,18 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
if (P.isArrayElement()) {
if (P.isOnePastEnd())
OS << " one-past-the-end";
- else
- OS << " index " << P.getIndex();
+ else {
+ OS << ' ';
+ std::string Indices;
+ llvm::raw_string_ostream SS(Indices);
+ Pointer K = P;
+ while (K.isArrayElement()) {
+ SS << ']' << K.expand().getIndex() << '[';
+ K = K.expand().getArray();
+ }
+ std::reverse(Indices.begin(), Indices.end());
+ OS << Indices;
+ }
} else if (P.isArrayRoot())
OS << " arrayroot";
diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp
index 1f251fef8a36d..28ebb21ccbe69 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -45,11 +45,13 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) {
Base = S;
// Create a descriptor for the string.
- Descriptor *Desc = allocateDescriptor(Base, *CharType, Descriptor::GlobalMD,
- StringLength + 1,
- /*IsConst=*/true,
- /*isTemporary=*/false,
- /*isMutable=*/false);
+ Descriptor *Desc =
+ allocateDescriptor(Base, S->getType().getTypePtr(), *CharType,
+ Descriptor::GlobalMD, StringLength + 1,
+ /*IsConst=*/true,
+ /*isTemporary=*/false,
+ /*isMutable=*/false,
+ /*IsVolatile=*/false);
// Allocate storage for the string.
// The byte length does not include the null terminator.
@@ -426,8 +428,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
if ((Descriptor::MaxArrayElemBytes / ElemSize) < NumElems) {
return nullptr;
}
- return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
- IsMutable);
+ return allocateDescriptor(D, CAT, *T, MDSize, NumElems, IsConst,
+ IsTemporary, IsMutable, IsVolatile);
}
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
@@ -472,8 +474,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
if (!ElemTy)
return nullptr;
- return allocateDescriptor(D, *ElemTy, MDSize, 2, IsConst, IsTemporary,
- IsMutable);
+ return allocateDescriptor(D, CT, *ElemTy, MDSize, 2, IsConst, IsTemporary,
+ IsMutable, IsVolatile);
}
// Same with vector types.
@@ -482,8 +484,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
if (!ElemTy)
return nullptr;
- return allocateDescriptor(D, *ElemTy, MDSize, VT->getNumElements(), IsConst,
- IsTemporary, IsMutable);
+ return allocateDescriptor(D, VT, *ElemTy, MDSize, VT->getNumElements(),
+ IsConst, IsTemporary, IsMutable, IsVolatile);
}
// Same with constant matrix types.
@@ -492,8 +494,9 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
if (!ElemTy)
return nullptr;
- return allocateDescriptor(D, *ElemTy, MDSize, MT->getNumElementsFlattened(),
- IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, MT, *ElemTy, MDSize,
+ MT->getNumElementsFlattened(), IsConst,
+ IsTemporary, IsMutable, IsVolatile);
}
return nullptr;
diff --git a/clang/unittests/AST/ByteCode/CMakeLists.txt b/clang/unittests/AST/ByteCode/CMakeLists.txt
index 1469cd6b2a8ea..0d49aa3eec30d 100644
--- a/clang/unittests/AST/ByteCode/CMakeLists.txt
+++ b/clang/unittests/AST/ByteCode/CMakeLists.txt
@@ -2,6 +2,7 @@ add_clang_unittest(InterpTests
BitcastBuffer.cpp
Descriptor.cpp
toAPValue.cpp
+ Pointer.cpp
CLANG_LIBS
clangAST
clangASTMatchers
diff --git a/clang/unittests/AST/ByteCode/Pointer.cpp b/clang/unittests/AST/ByteCode/Pointer.cpp
new file mode 100644
index 0000000000000..0b93c40aaa202
--- /dev/null
+++ b/clang/unittests/AST/ByteCode/Pointer.cpp
@@ -0,0 +1,274 @@
+#include "../../../lib/AST/ByteCode/Context.h"
+#include "../../../lib/AST/ByteCode/Descriptor.h"
+#include "../../../lib/AST/ByteCode/Integral.h"
+#include "../../../lib/AST/ByteCode/Program.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::interp;
+using namespace clang::ast_matchers;
+
+TEST(Pointer, TypesRecord) {
+ constexpr char Code[] = "struct A { bool a; bool b; };\n"
+ "constexpr A arr[3][2] = {\n"
+ " {{ false, false }, {false, true} },\n"
+ " {{ false, false }, {false, true} },\n"
+ " {{ false, false }, {false, true} },\n"
+ "};\n";
+
+ auto AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-fexperimental-new-constant-interpreter"});
+ ASTContext &ASTCtx = AST->getASTContext();
+ const VarDecl *D =
+ selectFirst<VarDecl>("arr", match(varDecl().bind("arr"), ASTCtx));
+ ASSERT_NE(D, nullptr);
+
+ const auto &Ctx = AST->getASTContext().getInterpContext();
+ Program &Prog = Ctx.getProgram();
+ // Global is registered.
+ ASSERT_TRUE(Prog.getGlobal(D));
+
+ // Get a Pointer to the global.
+ const Pointer &GlobalPtr = Prog.getPtrGlobal(*Prog.getGlobal(D));
+
+ // Type of the global ptr should be A[][]
+ {
+ QualType T = GlobalPtr.getType();
+ ASSERT_TRUE(T->isArrayType());
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)3);
+
+ QualType ElemTy = ArrTy->getElementType();
+ const auto *ElemArrTy =
+ cast<ConstantArrayType>(ElemTy->getAsArrayTypeUnsafe());
+ ASSERT_NE(ElemArrTy, nullptr);
+ ASSERT_EQ(ElemArrTy->getZExtSize(), (uint64_t)2);
+ }
+
+ // This is still A[][] because we didn't narrow().
+ {
+ Pointer Elem = GlobalPtr.atIndex(0);
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)3);
+
+ QualType ElemTy = ArrTy->getElementType();
+ const auto *ElemArrTy =
+ cast<ConstantArrayType>(ElemTy->getAsArrayTypeUnsafe());
+ ASSERT_NE(ElemArrTy, nullptr);
+ ASSERT_EQ(ElemArrTy->getZExtSize(), (uint64_t)2);
+ }
+
+ // Now with narrow(). This is just A[2], the type of the first element.
+ {
+ Pointer Elem = GlobalPtr.atIndex(0).narrow();
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+
+ // Same with element 1.
+ {
+ Pointer Elem = GlobalPtr.atIndex(1).narrow();
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+ // And 2.
+ {
+ Pointer Elem = GlobalPtr.atIndex(2).narrow();
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+
+ // This is arr[I][0], but we didn't narrow() at the end so the type is that of
+ // arr[I].
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ Pointer Elem = GlobalPtr.atIndex(I).narrow().atIndex(0);
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+ }
+ // Now WITH a narrow, the type should be just A.
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ for (unsigned J = 0; J != 2; ++J) {
+ Pointer Elem = GlobalPtr.atIndex(I).narrow().atIndex(J).narrow();
+ QualType T = Elem.getType();
+ ASSERT_TRUE(T->isRecordType());
+ }
+ }
+ }
+
+ // Same as the above, but we expand() again to undo the last narrow.
+ // The type should therefore be A[].
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ for (unsigned J = 0; J != 2; ++J) {
+ Pointer Elem =
+ GlobalPtr.atIndex(I).narrow().atIndex(J).narrow().expand();
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+ }
+ }
+
+ // Check narrow/expand invariants.
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ for (unsigned J = 0; J != 2; ++J) {
+ ASSERT_EQ(GlobalPtr.atIndex(I).narrow().atIndex(J),
+ GlobalPtr.atIndex(I).narrow().atIndex(J).narrow().expand());
+ ASSERT_EQ(GlobalPtr.atIndex(I).narrow().atIndex(J), GlobalPtr.atIndex(I)
+ .narrow()
+ .atIndex(J)
+ .narrow()
+ .expand()
+ .expand());
+ ASSERT_EQ(GlobalPtr.atIndex(I), GlobalPtr.atIndex(I).narrow().expand());
+ ASSERT_EQ(GlobalPtr.atIndex(I),
+ GlobalPtr.atIndex(I).narrow().expand().expand());
+ ASSERT_EQ(GlobalPtr.atIndex(I).atIndex(1), GlobalPtr.atIndex(1));
+ }
+ }
+ }
+
+ // getIndex()
+ {
+ // First dimension.
+ ASSERT_EQ(GlobalPtr.atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(2).getIndex(), 2);
+
+ // First dimension, with narrow().
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().getIndex(), 0);
+
+ // Second dimension.
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(1).getIndex(), 1);
+
+ // Second dimension, with narrow().
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(0).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(1).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(0).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(1).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(0).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(1).narrow().getIndex(), 0);
+ }
+}
+
+TEST(Pointer, TypesPrimitive) {
+ constexpr char Code[] = "constexpr int arr[3][2] = {\n"
+ " { 1, 2 },\n"
+ " { 3, 4 },\n"
+ " { 5, 6 },\n"
+ "};\n";
+
+ auto AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-fexperimental-new-constant-interpreter"});
+ ASTContext &ASTCtx = AST->getASTContext();
+ const VarDecl *D =
+ selectFirst<VarDecl>("arr", match(varDecl().bind("arr"), ASTCtx));
+ ASSERT_NE(D, nullptr);
+
+ const auto &Ctx = AST->getASTContext().getInterpContext();
+ Program &Prog = Ctx.getProgram();
+ // Global is registered.
+ ASSERT_TRUE(Prog.getGlobal(D));
+
+ // Get a Pointer to the global.
+ const Pointer &GlobalPtr = Prog.getPtrGlobal(*Prog.getGlobal(D));
+
+ // Type of the global ptr should be int[3][2].
+ {
+ QualType T = GlobalPtr.getType();
+ ASSERT_TRUE(T->isArrayType());
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)3);
+
+ QualType ElemTy = ArrTy->getElementType();
+ const auto *ElemArrTy =
+ cast<ConstantArrayType>(ElemTy->getAsArrayTypeUnsafe());
+ ASSERT_NE(ElemArrTy, nullptr);
+ ASSERT_EQ(ElemArrTy->getZExtSize(), (uint64_t)2);
+ }
+
+ // Type of the elements of the first dimension should be int[2].
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ Pointer Elem = GlobalPtr.atIndex(I).narrow();
+ QualType T = Elem.getType();
+ const auto *ArrTy = cast<ConstantArrayType>(T->getAsArrayTypeUnsafe());
+ ASSERT_NE(ArrTy, nullptr);
+ ASSERT_EQ(ArrTy->getZExtSize(), (uint64_t)2);
+ }
+ }
+
+ // Inner dimension is just int.
+ {
+ for (unsigned I = 0; I != 3; ++I) {
+ for (unsigned J = 0; J != 2; ++J) {
+ Pointer Elem = GlobalPtr.atIndex(I).narrow().atIndex(J);
+ QualType T = Elem.getType();
+ ASSERT_TRUE(T->isIntegerType());
+ }
+ }
+ }
+
+ {
+ // First dimension.
+ ASSERT_EQ(GlobalPtr.atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(2).getIndex(), 2);
+
+ // First dimension, with narrow().
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().getIndex(), 0);
+
+ // Second dimension.
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(1).narrow().atIndex(1).getIndex(), 1);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(0).getIndex(), 0);
+ ASSERT_EQ(GlobalPtr.atIndex(2).narrow().atIndex(1).getIndex(), 1);
+ }
+
+ {
+ // narrow() does not affect pointers into primitive arrays.
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(1),
+ GlobalPtr.atIndex(0).narrow().atIndex(1).narrow());
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow().atIndex(1),
+ GlobalPtr.atIndex(0).narrow().atIndex(1).expand());
+
+ ASSERT_EQ(GlobalPtr.atIndex(0).narrow(),
+ GlobalPtr.atIndex(0).narrow().atIndex(1).getArray());
+
+ ASSERT_EQ(GlobalPtr, GlobalPtr.atIndex(2).getArray());
+ ASSERT_EQ(GlobalPtr, GlobalPtr.atIndex(2).narrow().expand().getArray());
+ }
+}
More information about the cfe-commits
mailing list