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?= <[email protected]> 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()); + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
