From 59e13666dd2e81e58253488a29635fb2992ed741 Mon Sep 17 00:00:00 2001 From: Timm Baeder Date: Tue, 6 Aug 2024 13:37:11 +0200 Subject: [clang][Interp] Fix getField() for integral pointers (#102120) Instead of just adding the Record::Field offset, instead get the FieldDecl offset in the RecordLayout. Unfortunately, the offset we pass to the ops here is not made to easily go back to a FieldDecl, so we have to iterate over the parent Record. --- clang/lib/AST/Interp/Compiler.cpp | 4 ++++ clang/lib/AST/Interp/Interp.h | 11 +++++++++++ clang/lib/AST/Interp/Pointer.cpp | 27 +++++++++++++++++++++++++++ clang/lib/AST/Interp/Pointer.h | 8 ++++++-- clang/test/AST/Interp/c.c | 7 +------ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp index e528049..02cbe38 100644 --- a/clang/lib/AST/Interp/Compiler.cpp +++ b/clang/lib/AST/Interp/Compiler.cpp @@ -335,6 +335,10 @@ bool Compiler::VisitCastExpr(const CastExpr *CE) { if (!PointeeType.isNull()) { if (std::optional T = classify(PointeeType)) Desc = P.createDescriptor(SubExpr, *T); + else + Desc = P.createDescriptor(SubExpr, PointeeType.getTypePtr(), + std::nullopt, true, false, + /*IsMutable=*/false, nullptr); } return this->emitNull(classifyPrim(CE->getType()), Desc, CE); } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 04f88ef..2eed0d3 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1504,6 +1504,12 @@ inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize()) return false; + + if (Ptr.isIntegralPointer()) { + S.Stk.push(Ptr.asIntPointer().atOffset(S.getCtx(), Off)); + return true; + } + S.Stk.push(Ptr.atField(Off)); return true; } @@ -1527,6 +1533,11 @@ inline bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) { if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize()) return false; + if (Ptr.isIntegralPointer()) { + S.Stk.push(Ptr.asIntPointer().atOffset(S.getCtx(), Off)); + return true; + } + S.Stk.push(Ptr.atField(Off)); return true; } diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index 2b1f8b4..ba9683a 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -597,3 +597,30 @@ std::optional Pointer::toRValue(const Context &Ctx, return std::nullopt; return Result; } + +IntPointer IntPointer::atOffset(const ASTContext &ASTCtx, + unsigned Offset) const { + if (!this->Desc) + return *this; + const Record *R = this->Desc->ElemRecord; + if (!R) + return *this; + + const Record::Field *F = nullptr; + for (auto &It : R->fields()) { + if (It.Offset == Offset) { + F = &It; + break; + } + } + if (!F) + return *this; + + const FieldDecl *FD = F->Decl; + const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent()); + unsigned FieldIndex = FD->getFieldIndex(); + uint64_t FieldOffset = + ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)) + .getQuantity(); + return IntPointer{this->Desc, FieldOffset}; +} diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h index 6f69834..b7b4f82 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -44,6 +44,8 @@ struct BlockPointer { struct IntPointer { const Descriptor *Desc; uint64_t Value; + + IntPointer atOffset(const ASTContext &ASTCtx, unsigned Offset) const; }; enum class Storage { Block, Int, Fn }; @@ -88,6 +90,9 @@ public: PointeeStorage.Int.Value = 0; PointeeStorage.Int.Desc = nullptr; } + Pointer(IntPointer &&IntPtr) : StorageKind(Storage::Int) { + PointeeStorage.Int = std::move(IntPtr); + } Pointer(Block *B); Pointer(Block *B, uint64_t BaseAndOffset); Pointer(const Pointer &P); @@ -161,9 +166,8 @@ public: /// Creates a pointer to a field. [[nodiscard]] Pointer atField(unsigned Off) const { + assert(isBlockPointer()); unsigned Field = Offset + Off; - if (isIntegralPointer()) - return Pointer(asIntPointer().Value + Field, asIntPointer().Desc); return Pointer(asBlockPointer().Pointee, Field, Field); } diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c index 9ec305d..13a5e08 100644 --- a/clang/test/AST/Interp/c.c +++ b/clang/test/AST/Interp/c.c @@ -101,8 +101,6 @@ int somefunc(int i) { // all-warning {{overflow in expression; result is 131'073 with type 'int'}} } -/// FIXME: The following test is incorrect in the new interpreter. -/// The null pointer returns 16 from its getIntegerRepresentation(). #pragma clang diagnostic ignored "-Wpointer-to-int-cast" struct ArrayStruct { char n[1]; @@ -111,10 +109,7 @@ char name2[(int)&((struct ArrayStruct*)0)->n]; // expected-warning {{folded to c // pedantic-expected-warning {{folded to constant array}} \ // ref-warning {{folded to constant array}} \ // pedantic-ref-warning {{folded to constant array}} -_Static_assert(sizeof(name2) == 0, ""); // expected-error {{failed}} \ - // expected-note {{evaluates to}} \ - // pedantic-expected-error {{failed}} \ - // pedantic-expected-note {{evaluates to}} +_Static_assert(sizeof(name2) == 0, ""); #ifdef __SIZEOF_INT128__ void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1]; // all-warning {{refers past the last possible element}} -- cgit v1.1