Timm =?utf-8?q?Bäder?= <[email protected]> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>
llvmorg-github-actions[bot] wrote: <!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> This adds a new pointer type to represent something we don't have data for, but can still refer to. They are similar to LValue APValues: it's a base (currently always a `VarDecl`) and a "lvalue designator" path. For the first argument of `__builtin_object_size` and `__builtin_dynamic_object_size`, when we see an unknown variable, we emit an opaque pointer for the variable instead of a dummy pointer. Also if any load in that argument fails, we don't abort the evaluation but instead push an opaque pointer of the appropriate type on the stack. This fixes the review complaint from https://github.com/llvm/llvm-project/pull/196548, namely that we didn't have a way to just point to an opaque type if a load fails. Cases that still don't work: 1) The stuff in `test/Sema/builtin-object-size-cxx14.cpp` that use non-constexpr functions in the first argument of `__builtin_object_size` (everything in the `InvalidBase` namespace). This behavior matches GCC. 2) From `test/CodeGen/object-size.c`, the sample ```c void test26(void) { struct { int v[10]; } t[10]; // CHECK: store i32 312 gi = OBJECT_SIZE_BUILTIN(&t[1].v[12], 1); } results in 0, not 312. I don't know why it would result in 312. For modes 1 and 3, we should consider the closest surrounding variable, wich only has 10 elements, so at index 12, we have 0 bytes we can read from. GCC also returns 0. 3) From `test/CodeGen/object-size.c`, functions `test7` through `test17`, because they modify variables in the first argument of `__builtin_object_size`. 4) This line from `test/CodeGen/pass-object-size.c`: ```c gi = NoViableOverloadObjectSize1(&t[1]); ``` This might seem overkill for now, but I plan on using opaque pointers as replacement for the current "dummy pointer" mechanism as well. --- Patch is 66.58 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/213017.diff 22 Files Affected: - (modified) clang/lib/AST/ByteCode/Compiler.cpp (+12-3) - (modified) clang/lib/AST/ByteCode/Context.cpp (+7-9) - (modified) clang/lib/AST/ByteCode/Context.h (+1-1) - (modified) clang/lib/AST/ByteCode/Interp.cpp (+127) - (modified) clang/lib/AST/ByteCode/Interp.h (+79-36) - (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+20-212) - (added) clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp (+527) - (modified) clang/lib/AST/ByteCode/InterpHelpers.h (+2-1) - (modified) clang/lib/AST/ByteCode/InterpState.h (+4) - (modified) clang/lib/AST/ByteCode/Opcodes.td (+6) - (modified) clang/lib/AST/ByteCode/Pointer.cpp (+53) - (modified) clang/lib/AST/ByteCode/Pointer.h (+94-1) - (modified) clang/lib/AST/ByteCode/Program.cpp (+4-2) - (modified) clang/lib/AST/ByteCode/Record.h (+1) - (modified) clang/lib/AST/CMakeLists.txt (+1) - (modified) clang/lib/AST/ExprConstant.cpp (+4-1) - (added) clang/test/AST/ByteCode/builtin-object-size-codegen-cxx23.cpp (+15) - (modified) clang/test/AST/ByteCode/builtin-object-size-codegen.c (+64) - (modified) clang/test/AST/ByteCode/builtin-object-size-codegen.cpp (+125) - (modified) clang/test/AST/ByteCode/enable_if.c (+2-2) - (modified) clang/test/CodeGen/object-size.c (+3-89) - (modified) clang/test/CodeGen/pass-object-size.c (+2-3) ``````````diff diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 479b5251b9f57..1cb6768d6c67c 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -453,8 +453,12 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { case CK_LValueToRValue: { - if (ToLValue && E->getType()->isPointerType()) - return this->delegate(SubExpr); + if (ToLValue && E->getType()->isPointerType()) { + assert(!DiscardResult); + if (!this->visit(SubExpr)) + return false; + return this->emitLoadPopL(E); + } if (SubExpr->getType().isVolatileQualified()) return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, E); @@ -8706,8 +8710,13 @@ bool Compiler<Emitter>::emitDestructionPop(const Descriptor *Desc, template <class Emitter> bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E, bool CU) { assert(!DiscardResult && "Should've been checked before"); - unsigned DummyID = P.getOrCreateDummy(D, CU); + if (ToLValue) { + if (auto *VD = dyn_cast_if_present<ValueDecl>(D.dyn_cast<const Decl *>())) + return this->emitGetOpaquePtr(VD, E); + } + + unsigned DummyID = P.getOrCreateDummy(D, CU); if (!this->emitGetPtrGlobal(DummyID, E)) return false; if (E->getType()->isVoidType()) diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index b913d2a9f539c..6e3b93e2107b5 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -366,26 +366,24 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) { return Result; } -std::optional<uint64_t> -Context::tryEvaluateObjectSize(State &Parent, const Expr *E, unsigned Kind) { +std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent, + const Expr *E, + unsigned Kind, + bool IsDynamic) { assert(Stk.empty()); Compiler<EvalEmitter> C(*this, *P, Parent, Stk); std::optional<uint64_t> Result; - auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - const Descriptor *DeclDesc = Ptr.getDeclDesc(); - if (!DeclDesc) - return false; - - QualType T = DeclDesc->getType().getNonReferenceType(); + QualType T = Ptr.getType().getNonReferenceType(); if (T->isIncompleteType() || T->isFunctionType() || !T->isConstantSizeType()) return false; Pointer P = Ptr; - if (auto ObjectSize = evaluateBuiltinObjectSize(getASTContext(), Kind, P)) { + if (auto ObjectSize = + evaluateBuiltinObjectSize(getASTContext(), Kind, P, E, IsDynamic)) { Result = *ObjectSize; return true; } diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h index 47821a3e3a7f3..77566d35ad6e0 100644 --- a/clang/lib/AST/ByteCode/Context.h +++ b/clang/lib/AST/ByteCode/Context.h @@ -95,7 +95,7 @@ class Context final { /// bytes belonging to the same storage (stack, heap allocation, /// global variable) are considered. std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E, - unsigned Kind); + unsigned Kind, bool IsDynamic); std::optional<bool> evaluateWithSubstitution(State &Parent, const FunctionDecl *Callee, diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 23bdc5ea28c03..795f63690bfcf 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1612,6 +1612,29 @@ static bool getField(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } + if (Ptr.isOpaquePointer()) { + const OpaquePointer &OP = Ptr.asOpaquePointer(); + const Record *R = + S.getContext().getRecord(OP.getFieldType()->getAsRecordDecl()); + if (!R) + return false; + + const Record::Field *F = R->findField(Off); + if (!F) + return false; + + PointerPathEntry *NewPath = S.allocPointerPath(OP.PathLength + 1); + if (OP.Path) + std::memcpy(NewPath, Ptr.asOpaquePointer().Path, + sizeof(PointerPathEntry) * OP.PathLength); + + NewPath[OP.PathLength] = PointerPathEntry::field(F->Decl); + + S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, F->Decl->getType().getTypePtr(), + NewPath, OP.PathLength + 1); + return true; + } + if (!Ptr.isBlockPointer()) { // If we're trying to get the field of a TypeId pointer, try to produce a // proper diagnostic. @@ -1646,6 +1669,31 @@ static bool getBase(InterpState &S, CodePtr OpPC, const Pointer &Ptr, if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base)) return false; + if (Ptr.isOpaquePointer()) { + const OpaquePointer &OP = Ptr.asOpaquePointer(); + const Record *R = + S.getContext().getRecord(OP.getFieldType()->getAsRecordDecl()); + assert(R); + + const Record::Base *B = R->findBase(Off); + if (!B) + return false; + + unsigned NewPathLength = OP.PathLength + 1; + PointerPathEntry *NewPath = S.allocPointerPath(NewPathLength); + if (OP.Path) + std::memcpy(NewPath, OP.Path, + sizeof(PointerPathEntry) * (NewPathLength - 1)); + + NewPath[NewPathLength - 1] = + PointerPathEntry::base(cast<CXXRecordDecl>(B->Decl)); + S.Stk.push<Pointer>( + OpaqueTag{}, OP.Base, + S.getASTContext().getCanonicalTagType(B->Decl).getTypePtr(), NewPath, + NewPathLength); + return true; + } + if (!Ptr.isBlockPointer()) { if (!Ptr.isIntegralPointer()) return false; @@ -1906,6 +1954,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, S.Current = FrameBefore; return false; } + bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize) { @@ -3219,6 +3268,84 @@ bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth, return floatAPCast<true>(S, OpPC, F, BitWidth, FPOI); } +bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const OpaquePointer &OP, + uint64_t Offset) { + QualType ArrTy; + if (OP.PathLength > 0) { + if (OP.path().back().Kind == PointerPathEntry::Array) { + ArrTy = OP.getSurroundingArray(S.getASTContext()); + } else { + ArrTy = OP.getFieldType(); + } + } else { + ArrTy = OP.getObjectType(); + } + + QualType ElemType; + bool PastEnd = true; + if (ArrTy->isArrayType()) { + const auto *AT = ArrTy->getAsArrayTypeUnsafe(); + ElemType = AT->getElementType(); + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) + PastEnd = Offset >= CAT->getZExtSize(); + } else if (ArrTy->isRecordType()) { + ElemType = ArrTy; + } else { + ElemType = ArrTy; + } + if (ElemType.isNull()) + return false; + + unsigned NewPathLength = OP.PathLength + 1; + PointerPathEntry *NewPath = S.allocPointerPath(NewPathLength); + if (OP.Path) + std::memcpy(NewPath, OP.Path, + sizeof(PointerPathEntry) * (NewPathLength - 1)); + + NewPath[NewPathLength - 1] = PointerPathEntry::array(Offset); + S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, ElemType.getTypePtr(), NewPath, + NewPathLength, PastEnd); + return true; +} + +bool addSubOffsetOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + uint64_t Offset, bool Add) { + assert(Ptr.isOpaquePointer()); + const OpaquePointer &OP = Ptr.asOpaquePointer(); + + QualType ArrTy = OP.getFieldType(); + QualType ElemTy; + if (ArrTy->isArrayType()) + ElemTy = ArrTy->getAsArrayTypeUnsafe()->getElementType(); + else + ElemTy = ArrTy; + + if (Offset != 0) { + if (isa<IncompleteArrayType>(ArrTy)) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_unsized_array_indexed); + return false; + } + + S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) + << Offset << /*non-array*/ true << 0; + } + + unsigned ElemSize = + S.getASTContext().getTypeSizeInChars(ElemTy).getQuantity(); + unsigned NewOffset; + if (Add) + NewOffset = Ptr.getByteOffset() + (ElemSize * Offset); + else + NewOffset = Ptr.getByteOffset() - (ElemSize * Offset); + + bool PastEnd = NewOffset > 0; + + S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, OP.FieldType, OP.Path, + OP.PathLength, PastEnd, NewOffset); + return true; +} + // FIXME: Would be nice to generate this instead of hardcoding it here. constexpr bool OpReturns(Opcode Op) { return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet || diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 405f4a29ec982..bc22c896db655 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2252,6 +2252,37 @@ bool LoadPop(InterpState &S, CodePtr OpPC) { return true; } +/// Like LoadPop above, but if any of the checks fail, we +/// turn the pointer into an opaque pointer of appropriate type. +inline bool LoadPopL(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); + auto P = S.getEvalStatus().Diag; + S.getEvalStatus().Diag = nullptr; + + bool Failed = false; + if (!CheckLoad(S, OpPC, Ptr)) + Failed = true; + if (!Ptr.isBlockPointer()) + Failed = true; + if (!Ptr.canDeref(PT_Ptr)) + Failed = true; + S.getEvalStatus().Diag = P; + + if (Failed) { + if (Ptr.isOpaquePointer()) { + S.Stk.push<Pointer>(OpaqueTag{}, Ptr.asOpaquePointer().Base, + Ptr.getType()->getPointeeType().getTypePtr()); + } else { + S.Stk.push<Pointer>(OpaqueTag{}, Ptr.getDeclDesc()->asValueDecl(), + Ptr.getType()->getPointeeType().getTypePtr()); + } + + } else { + S.Stk.push<Pointer>(Ptr.deref<Pointer>()); + } + return true; +} + template <PrimType Name, class T = typename PrimConv<Name>::T> bool Store(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); @@ -2646,11 +2677,17 @@ std::optional<Pointer> OffsetHelper(InterpState &S, CodePtr OpPC, return Ptr.atIndex(static_cast<uint64_t>(Result)); } +bool addSubOffsetOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + uint64_t Offset, bool Add); template <PrimType Name, class T = typename PrimConv<Name>::T> bool AddOffset(InterpState &S, CodePtr OpPC) { const T &Offset = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>().expand(); + if (Ptr.isOpaquePointer()) + return addSubOffsetOpaque(S, OpPC, Ptr, static_cast<uint64_t>(Offset), + /*Add=*/true); + if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Add>( S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) { S.Stk.push<Pointer>(Result->narrow()); @@ -2664,6 +2701,10 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { const T &Offset = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>().expand(); + if (Ptr.isOpaquePointer()) + return addSubOffsetOpaque(S, OpPC, Ptr, static_cast<uint64_t>(Offset), + /*Add=*/false); + if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Sub>( S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) { S.Stk.push<Pointer>(Result->narrow()); @@ -2672,6 +2713,11 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { return false; } +inline bool GetOpaquePtr(InterpState &S, const ValueDecl *VD) { + S.Stk.push<Pointer>(OpaqueTag{}, VD); + return true; +} + template <ArithOp Op> static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { @@ -3431,18 +3477,16 @@ inline bool ExpandPtr(InterpState &S) { return true; } -// 1) Pops an integral value from the stack -// 2) Peeks a pointer -// 3) Pushes a new pointer that's a narrowed array -// element of the peeked pointer with the value -// from 1) added as offset. -// -// This leaves the original pointer on the stack and pushes a new one -// with the offset applied and narrowed. -template <PrimType Name, class T = typename PrimConv<Name>::T> -inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) { - const T &Offset = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.peek<Pointer>(); +bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const OpaquePointer &OP, + uint64_t Offset); + +// Implementation for ArrayElemPtr and ArrayElemPtrPop ops. +template <typename T> +inline bool arrayElemPtr(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + const T &Offset) { + if (Ptr.isOpaquePointer()) + return arrayElemPtrOpaque(S, OpPC, Ptr.asOpaquePointer(), + static_cast<int64_t>(Offset)); if (!Ptr.isZero() && !Offset.isZero()) { if (!CheckArray(S, OpPC, Ptr)) @@ -3466,38 +3510,31 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) { S.Stk.push<Pointer>(Result->narrow()); return true; } - return false; } +// 1) Pops an integral value from the stack +// 2) Peeks a pointer +// 3) Pushes a new pointer that's a narrowed array +// element of the peeked pointer with the value +// from 1) added as offset. +// +// This leaves the original pointer on the stack and pushes a new one +// with the offset applied and narrowed. template <PrimType Name, class T = typename PrimConv<Name>::T> -inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) { +inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) { const T &Offset = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - - if (!Ptr.isZero() && !Offset.isZero()) { - if (!CheckArray(S, OpPC, Ptr)) - return false; - } + const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (Offset.isZero()) { - if (const Descriptor *Desc = Ptr.getFieldDesc(); - Desc && Desc->isArray() && Ptr.getIndex() == 0) { - S.Stk.push<Pointer>(Ptr.atIndex(0).narrow()); - return true; - } - S.Stk.push<Pointer>(Ptr.narrow()); - return true; - } + return arrayElemPtr<T>(S, OpPC, Ptr, Offset); +} - assert(!Offset.isZero()); +template <PrimType Name, class T = typename PrimConv<Name>::T> +inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) { + const T &Offset = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (std::optional<Pointer> Result = - OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr)) { - S.Stk.push<Pointer>(Result->narrow()); - return true; - } - return false; + return arrayElemPtr<T>(S, OpPC, Ptr, Offset); } template <PrimType Name, class T = typename PrimConv<Name>::T> @@ -3569,6 +3606,12 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) { return false; } + if (Ptr.isOpaquePointer()) { + // llvm::errs()<< "ArrayDecay of Opaque pointer\n"; + S.Stk.push<Pointer>(Ptr); + return true; + } + if (Ptr.isRoot() || !Ptr.isUnknownSizeArray()) { S.Stk.push<Pointer>(Ptr.atIndex(0).narrow()); return true; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index a539fb26abc08..7ba47a99cbb6e 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -2297,218 +2297,9 @@ static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC, return true; } -static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx, - const Descriptor *Desc) { - if (Desc->isPrimitive() || Desc->isArray()) - return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity(); - - if (Desc->isRecord()) { - // Can't use Descriptor::getType() as that may return a pointer type. Look - // at the decl directly. - return ASTCtx - .getTypeSizeInChars( - ASTCtx.getCanonicalTagType(Desc->ElemRecord->getDecl())) - .getQuantity(); - } - - return std::nullopt; -} - -/// Compute the byte offset of \p Ptr in the full declaration. -static unsigned computePointerOffset(const ASTContext &ASTCtx, - const Pointer &Ptr) { - unsigned Result = 0; - - Pointer P = Ptr; - while (P.isField() || P.isArrayElement()) { - P = P.expand(); - const Descriptor *D = P.getFieldDesc(); - - if (P.isArrayElement()) { - unsigned ElemSize = - ASTCtx.getTypeSizeInChars(D->getElemQualType()).getQuantity(); - if (P.isOnePastEnd()) - Result += ElemSize * P.getNumElems(); - else - Result += ElemSize * P.getIndex(); - P = P.expand().getArray(); - } else if (P.isBaseClass()) { - const auto *RD = cast<CXXRecordDecl>(D->asDecl()); - bool IsVirtual = Ptr.isVirtualBaseClass(); - P = P.getBase(); - const Record *BaseRecord = P.getRecord(); - - const ASTRecordLayout &Layout = - ASTCtx.getASTRecordLayout(cast<CXXRecordDecl>(BaseRecord->getDecl())); - if (IsVirtual) - Result += Layout.getVBaseClassOffset(RD).getQuantity(); - else - Result += Layout.getBaseClassOffset(RD).getQuantity(); - } else if (P.isField()) { - const FieldDecl *FD = P.getField(); - const ASTRecordLayout &Layout = - ASTCtx.getASTRecordLayout(FD->getParent()); - unsigned FieldIndex = FD->getFieldIndex(); - uint64_t FieldOffset = - ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)) - .getQuantity(); - Result += FieldOffset; - P = P.getBase(); - } else - llvm_unreachable("Unhandled descriptor type"); - } - - return Result; -} - -/// Does Ptr point to the last subobject? -static bool pointsToLastObject(const Pointer &Ptr) { - Pointer P = Ptr; - while (!P.isRoot()) { - - if (P.isArrayElement()) { - P = P.expand().getArray(); - continue; - } - if (P.isBaseClass()) { - if (P.getRecord()->getNumFields() > 0) - return false; - P = P.getBase(); - continue; - } - - Pointer Base = P.getBase(); - if (const Record *R = Base.getRecord()) { - assert(P.getField()); - if (P.getField()->getFieldIndex() != R->getNumFields() - 1) - return false; - } - P = Base; - } - - return true; -} - -/// Does Ptr point to the last object AND to a flexible array member? -static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const Pointer &Ptr, - bool InvalidBase) { - auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) { - using FAMKind = LangOptions::StrictFlexArraysLevelKind; - FAMKind StrictFlexArraysLevel = - Ctx.getLangOpts().getStrictFlexArraysLevel(); - - if (StrictFlexArraysLevel == FAMKind::Default) - return true; - - unsigned NumElems = FieldDesc->getNumElems(); - if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly) - return true; - - if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete) - return true; - return false; - }; - - const Descriptor *FieldDesc = Ptr.getFieldDesc(); - if (!FieldDesc->isArray()) - return false; - - return InvalidBase && pointsToLastObject(Ptr) && - isFlexibleArrayMember(FieldDesc); -} - -UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext &ASTCtx, - unsigned Kind, Pointer &Ptr) { - if (Ptr.isZero() || !Ptr.isBlockPointer()) - return std::nullopt; - - if (Ptr.isDummy() && Ptr.getType()->isPointerType()) - return std::nullopt; - - bool InvalidBase = false; - - if (Ptr.isDummy()) { - if (const VarDecl *VD = Ptr.getDeclDesc()->asVarDecl(); - VD && VD->getType()->isPointerType()) - InvalidBase = true; - } - - // According to the GCC documentation, we want the size of the subobject - // denoted by the pointer. But that's not quite right -- what we actually - // want is the size of the immediately-enclosing array, if there is one. - if (Ptr.isArrayElement()) - Ptr = Ptr.expand(); - - bool DetermineForCompleteObject = Ptr.getFieldDesc() == Ptr.getDeclDesc(); - const Descriptor *DeclDesc = Ptr.getDeclDesc... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/213017 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
