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 uses the recently introduced opaque pointers for what we used to use dummy pointers for, if the base of the pointer is a declaration. This of course means we see opaque pointers in a lot more places. However, we don't need to actually allocate anything for their data anymore, resulting in a lot fewer allocations for such pointers. --- Patch is 45.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/222897.diff 14 Files Affected: - (modified) clang/lib/AST/ByteCode/Compiler.cpp (+12-7) - (modified) clang/lib/AST/ByteCode/Interp.cpp (+186-32) - (modified) clang/lib/AST/ByteCode/Interp.h (+80-27) - (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+39-18) - (modified) clang/lib/AST/ByteCode/InterpHelpers.h (+5) - (modified) clang/lib/AST/ByteCode/MemberPointer.h (+2) - (modified) clang/lib/AST/ByteCode/Opcodes.td (+6-3) - (modified) clang/lib/AST/ByteCode/Pointer.cpp (+112-13) - (modified) clang/lib/AST/ByteCode/Pointer.h (+8-17) - (modified) clang/test/AST/ByteCode/records.cpp (+11) - (modified) clang/test/CodeGen/pr4349.c (+2-1) - (modified) clang/test/SemaCXX/new-delete.cpp (+2-12) - (modified) clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp (+1) - (modified) clang/unittests/AST/ByteCode/toAPValue.cpp (-2) ``````````diff diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index daa5307c92298..473494c694a98 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -7935,12 +7935,18 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { // check), so that e.g. '&*(int *)0' is not rejected. if (!Ctx.getLangOpts().CPlusPlus) { const Expr *Sub = SubExpr->IgnoreParens(); + if (const auto *Deref = dyn_cast<UnaryOperator>(Sub); - Deref && Deref->getOpcode() == UO_Deref) - return this->delegate(Deref->getSubExpr()); + Deref && Deref->getOpcode() == UO_Deref) { + if (DiscardResult) + return this->discard(Deref->getSubExpr()); + return this->visit(Deref->getSubExpr()) && this->emitAddrOf(E); + } } // We should already have a pointer when we get here. - return this->delegate(SubExpr); + if (DiscardResult) + return this->discard(SubExpr); + return this->delegate(SubExpr) && this->emitAddrOf(E); case UO_Deref: // *x if (DiscardResult) return this->discard(SubExpr); @@ -8729,11 +8735,10 @@ template <class Emitter> bool Compiler<Emitter>::emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU) { assert(!DiscardResult && "Should've been checked before"); - if (ToLValue) { - if (const auto *VD = D.asValueDecl()) - return this->emitGetOpaquePtr(VD, CU, E); - } + if (const auto *VD = D.asValueDecl()) + return this->emitGetOpaquePtr(VD, CU, E); + assert(D.asExpr()); unsigned DummyID = P.getOrCreateDummy(D, CU); if (!this->emitGetPtrGlobal(DummyID, E)) return false; diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index e205b025bbe01..160cc745c89b6 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -97,6 +97,23 @@ static void noteValueLocation(InterpState &S, const Block *B) { S.Note(Desc->getLocation(), diag::note_declared_at); } +static void noteValueLocation(InterpState &S, const Pointer &Ptr) { + if (Ptr.isBlockPointer()) { + const Block *B = Ptr.block(); + const Descriptor *Desc = B->getDescriptor(); + if (B->isDynamic()) + S.Note(Desc->getLocation(), diag::note_constexpr_dynamic_alloc_here); + else if (B->isTemporary()) + S.Note(Desc->getLocation(), diag::note_constexpr_temporary_here); + else + S.Note(Desc->getLocation(), diag::note_declared_at); + return; + } + + if (Ptr.isOpaquePointer()) + S.Note(Ptr.asOpaquePointer().Base->getLocation(), diag::note_declared_at); +} + static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, const ValueDecl *VD, AccessKinds AK = AK_Read); @@ -200,6 +217,38 @@ static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, S.Note(VD->getLocation(), diag::note_declared_at); } +static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + + if (!Ptr.isBlockPointer()) + return true; + + const Block *B = Ptr.block(); + if (B->getDeclID()) { + if (!(B->isStatic() && B->isTemporary())) + return true; + + const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>( + B->getDescriptor()->asExpr()); + if (!MTE) + return true; + + // FIXME(perf): Since we do this check on every Load from a static + // temporary, it might make sense to cache the value of the + // isUsableInConstantExpressions call. + if (S.checkingConstantDestruction() || + (B->getEvalID() != S.EvalID && + !MTE->isUsableInConstantExpressions(S.getASTContext()))) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; + noteValueLocation(S, B); + return false; + } + } + + return true; +} + static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) { if (B->getDeclID()) { @@ -460,7 +509,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, } else if (!S.checkingPotentialConstantExpression()) { S.FFDiag(Src, diag::note_constexpr_access_uninit) << AK << /*uninitialized=*/false << S.Current->getRange(OpPC); - noteValueLocation(S, Ptr.block()); + noteValueLocation(S, Ptr); } return false; @@ -897,7 +946,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, } // Block and string pointers are the only ones we can actually read from. if (!Ptr.isReadablePointerType()) - return false; + return CheckDummy(S, OpPC, Ptr, AK); if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) { if (!CheckLive(S, OpPC, Ptr, AK)) @@ -964,7 +1013,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(!Ptr.isZero()); if (!Ptr.isReadablePointerType()) - return false; + return CheckDummy(S, OpPC, Ptr, AK_Read); if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) { if (!CheckLive(S, OpPC, Ptr, AK_Read)) @@ -996,7 +1045,13 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, bool WillBeActivated) { - if (!Ptr.isBlockPointer() || Ptr.isZero()) + if (Ptr.isZero()) + return false; + + if (Ptr.isOpaquePointer()) + return CheckDummy(S, OpPC, Ptr, AK_Assign); + + if (!Ptr.isBlockPointer()) return false; if (!Ptr.block()->isAccessible()) { @@ -1043,6 +1098,8 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; if (!CheckRange(S, OpPC, Ptr, AK_Assign)) return false; + if (!Ptr.isBlockPointer()) + return false; return true; } @@ -1263,6 +1320,8 @@ bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source, const Pointer &Ptr) { + if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer()) + return false; // Regular new type(...) call. if (isa_and_nonnull<CXXNewExpr>(Source)) return true; @@ -1279,7 +1338,7 @@ bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source, const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc) << Ptr.toDiagnosticString(S.getASTContext()); - noteValueLocation(S, Ptr.block()); + noteValueLocation(S, Ptr); return false; } @@ -1305,6 +1364,24 @@ bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR, return CheckDeclRef(S, OpPC, DR); } +bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (!Ptr.isDummy()) + return true; + + const VarDecl *D = Ptr.getRootVarDecl(); + if (!D) + return false; + + if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement) + return diagnoseUnknownDecl(S, OpPC, D, AK); + + if (AK == AK_Destroy || S.getLangOpts().CPlusPlus14) + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_modify_global); + return false; +} + +// FIXME: Remove this once all dummy pointers are opaque pointers. bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) { if (!B->isDummy()) return true; @@ -1429,7 +1506,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, return true; if (!Ptr.isBlockPointer()) - return false; + return CheckDeleteSource(S, OpPC, nullptr, Ptr); // Remove base casts. QualType InitialType = Ptr.getType(); @@ -1811,7 +1888,7 @@ static bool diagnoseOutOfLifetimeDestroy(InterpState &S, CodePtr OpPC, bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckLive(S, OpPC, Ptr, AK_Destroy)) return false; - if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Destroy)) + if (!CheckTemporary(S, OpPC, Ptr, AK_Destroy)) return false; if (!CheckRange(S, OpPC, Ptr, AK_Destroy)) return false; @@ -1827,7 +1904,7 @@ bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return true; // Can't call a dtor on a global variable. - if (Ptr.block()->isStatic()) { + if (Ptr.isOpaquePointer() || Ptr.block()->isStatic()) { const SourceInfo &E = S.Current->getSource(OpPC); S.FFDiag(E, diag::note_constexpr_modify_global); return false; @@ -2056,9 +2133,22 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func, return true; } -static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr, +static bool getDynamicDecl(InterpState &S, CodePtr OpPC, const Pointer &Ptr, const CXXRecordDecl *&DynamicDecl) { + auto diagUnknownDynamicType = [&](const Pointer &P) -> bool { + APValue V = P.toAPValue(S.getASTContext()); + QualType TT = S.getASTContext().getLValueReferenceType(P.getType()); + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_polymorphic_unknown_dynamic_type) + << AK_MemberCall << V.getAsString(S.getASTContext(), TT); + return false; + }; + + if (!Ptr.isBlockPointer()) + return diagUnknownDynamicType(Ptr); + + PtrView TypePtr = Ptr.view(); if (S.InitializingPtrs.empty()) { TypePtr = TypePtr.stripBaseCasts(); } else { @@ -2090,14 +2180,8 @@ static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr, QualType DynamicType = TypePtr.getType(); if (TypePtr.Pointee->isStatic() || TypePtr.isConst()) { if (const VarDecl *VD = Pointer(TypePtr).getRootVarDecl(); - VD && !VD->isConstexpr()) { - const Expr *E = S.Current->getExpr(OpPC); - APValue V = Pointer(TypePtr).toAPValue(S.getASTContext()); - QualType TT = S.getASTContext().getLValueReferenceType(DynamicType); - S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type) - << AK_MemberCall << V.getAsString(S.getASTContext(), TT); - return false; - } + VD && !VD->isConstexpr()) + return diagUnknownDynamicType(Pointer(TypePtr)); } if (DynamicType->isPointerType() || DynamicType->isReferenceType()) { @@ -2162,7 +2246,7 @@ bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestTypePtr, const auto &Ptr = S.Stk.pop<Pointer>(); QualType TargetType = QualType(DestTypePtr, 0); - if (Ptr.isConstexprUnknown()) { + if (Ptr.isConstexprUnknown() || Ptr.isOpaquePointer()) { QualType T = Ptr.getType(); const Expr *E = S.Current->getExpr(OpPC); APValue V = Ptr.toAPValue(S.getASTContext()); @@ -2331,13 +2415,13 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); - if (!ThisPtr.isBlockPointer()) + if (!ThisPtr.isBlockPointer() && !ThisPtr.isOpaquePointer()) return false; const FunctionDecl *Callee = Func->getDecl(); const CXXRecordDecl *DynamicDecl = nullptr; - if (!getDynamicDecl(S, OpPC, ThisPtr.view(), DynamicDecl)) + if (!getDynamicDecl(S, OpPC, ThisPtr, DynamicDecl)) return false; assert(DynamicDecl); @@ -2611,7 +2695,7 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, } if (!Ptr.isBlockPointer()) - return false; + return CheckDummy(S, OpPC, Ptr, AK_Construct); if (!CheckRange(S, OpPC, Ptr, AK_Construct)) return false; @@ -2625,9 +2709,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, return false; if (!CheckLive(S, OpPC, Ptr, AK_Construct)) return false; - return CheckDummy(S, OpPC, Ptr.block(), AK_Construct); + return CheckDummy(S, OpPC, Ptr, AK_Construct); } - if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Construct)) + if (!CheckTemporary(S, OpPC, Ptr, AK_Construct)) return false; // CheckLifetime for this and all base pointers. @@ -2773,6 +2857,12 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, if (Ptr.isIntegralPointer()) return true; + if (Ptr.isOpaquePointer()) { + if (!CheckIntegralAddressCast(S, OpPC, BitWidth)) + return false; + return Ptr.isRoot(); + } + if (Ptr.isDummy()) { if (!CheckIntegralAddressCast(S, OpPC, BitWidth)) return false; @@ -2862,7 +2952,7 @@ bool GetTypeid(InterpState &S, const Type *TypePtr, const Type *TypeInfoType) { bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) { const auto &P = S.Stk.pop<Pointer>(); - if (!P.isBlockPointer()) + if (!P.isBlockPointer() && !P.isOpaquePointer()) return false; if (P.isConstexprUnknown()) { @@ -2876,7 +2966,12 @@ bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) { } // Pick the most-derived type. - CanQualType T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified(); + CanQualType T; + if (P.isBlockPointer()) + T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified(); + else + T = P.getType()->getCanonicalTypeUnqualified(); + // ... unless we're currently constructing this object. // FIXME: We have a similar check to this in more places. if (S.Current->getFunction()) { @@ -2979,6 +3074,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView Ptr, PrimType T) { auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength]; std::copy_n(Val.path(), PathLength, NewPath); Val.takePath(NewPath); + } else if (T == PT_Ptr) { + auto &Val = Ptr.deref<Pointer>(); + if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) { + const OpaquePointer &OP = Val.asOpaquePointer(); + auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength]; + std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry)); + Val = Pointer(OP.withPath(NewPath, OP.PathLength, + OP.getFieldType().getTypePtr(), + OP.isOnePastEnd()), + Val.getByteOffset()); + } } } @@ -2991,6 +3097,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView Ptr) { auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength]; std::copy_n(Val.path(), PathLength, NewPath); Val.takePath(NewPath); + } else if constexpr (std::is_same_v<T, Pointer>) { + auto &Val = Ptr.deref<Pointer>(); + if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) { + const OpaquePointer &OP = Val.asOpaquePointer(); + auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength]; + std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry)); + Val = Pointer(OP.withPath(NewPath, OP.PathLength, + OP.getFieldType().getTypePtr(), + OP.isOnePastEnd()), + Val.getByteOffset()); + } } else { auto &Val = Ptr.deref<T>(); if (!Val.singleWord()) { @@ -3050,6 +3167,8 @@ static void finishGlobalRecurse(InterpState &S, PtrView Ptr) { bool FinishInitGlobal(InterpState &S) { const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (!Ptr.isBlockPointer()) + return true; finishGlobalRecurse(S, Ptr.view()); if (Ptr.canBeInitialized()) { @@ -3355,13 +3474,16 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC, return Ptr; const OpaquePointer &OP = Ptr.asOpaquePointer(); - QualType ArrTy = OP.getSurroundingArray(); - QualType ElemTy = ArrTy; + QualType ArrTy = OP.getSurroundingArray().getCanonicalType(); + QualType ElemTy = OP.getFieldType(); unsigned NumElems = 1; - if (const ArrayType *AT = ArrTy->getAsArrayTypeUnsafe()) { - ElemTy = AT->getElementType(); - if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) + + if (OP.isArrayElement()) { + if (const ConstantArrayType *CAT = + S.getASTContext().getAsConstantArrayType(ArrTy)) NumElems = CAT->getZExtSize(); + } else { + ArrTy = ElemTy; } if (isa<IncompleteArrayType>(ArrTy)) { @@ -3373,10 +3495,10 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC, if (Offset > NumElems) { if (Op == ArithOp::Add) S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) - << Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems; + << Offset << /*non-array*/ !OP.isArrayElement() << NumElems; else S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) - << -Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems; + << -Offset << /*non-array*/ !OP.isArrayElement() << NumElems; } if (!validType(ElemTy) || !validType(ArrTy)) { @@ -3412,6 +3534,38 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC, return Pointer(OP.withPastEnd(true), NewOffset); } +bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl, + const Pointer &Ptr) { + if (Ptr.isOpaquePointer()) { + const OpaquePointer &OP = Ptr.asOpaquePointer(); + if (!OP.getFieldType()->isRecordType()) { + S.Stk.push<Pointer>(Ptr); + return true; + } + + PointerPathEntry *NewPath = + S.extendPointerPath(OP.PathLength + 1, OP.Path, + PointerPathEntry::base(Decl, /*IsVirtual=*/true)); + + S.Stk.push<Pointer>( + OP.withPath(NewPath, OP.PathLength + 1, + S.getASTContext().getCanonicalTagType(Decl).getTypePtr()), + Ptr.getByteOffset()); + return true; + } + + if (!Ptr.isBlockPointer()) + return false; + if (!Ptr.getFieldDesc()->isRecord()) + return false; + Pointer Base = Ptr.stripBaseCasts(); + const Record::Base *VirtBase = Base.getRecord()->findVirtualBase(Decl); + if (!VirtBase) + return false; + S.Stk.push<Pointer>(Base.atField(VirtBase->Offset)); + return true; +} + // FIXME: Would be nice to generate this instead of hardcoding it here. [[maybe_unused]] static 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 61118d77b7ac2..343883872728b 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1494,15 +1494,30 @@ bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) { const T &LHS = S.Stk.pop<T>(); const Pointer &P = S.Stk.peek<Pointer>(); - ComparisonCategoryResult CmpResult = LHS.compare(RHS); + ComparisonCategoryResult CmpResult; if constexpr (std::is_same_v<T, Pointer>) { - if (CmpResult == ComparisonCategoryResult::Unordered) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified) + if (!Pointer::hasSameBase(LHS, RHS)) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_pointer_comparison_unspecified) << LHS.toDiagnosticString(S.getASTContext()) << RHS.toDiagnosticString(S.getASTContext()); return false; } + std::optional<size_t> LHSOffset = + LHS.computeLayoutOffset(S.getASTContext()); + std::optional<size_t> RHSOffset = + RHS.computeLayoutOffset(S.getASTContext()); + if (!LHSOffset || !RHSOffset) + return false; + + if (LHSOffset < RHSOffset) + CmpResult = ComparisonCategoryResult::Less; + else if (LHSOffset > RHSOffset) + CmpResult = ComparisonCategoryResult::Greater; + else + CmpResult = ComparisonCategoryResult::Equal; + } else { + CmpResult = LHS.compare(RHS); } assert(CmpInfo); @@ -1673,6 +1688,9 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { if (!CheckRange(S, OpPC, Obj, CSK_Field)) return false; + if (!Obj.isBlockPointer()) + return false; + // FIXME(postswitch): The isUnknownSizeArray() check here is only needed // to keep an invalid sample producing the same diagnostics as the current // interpreter. @@ -1696,6 +1714,9 @@ bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { if (!CheckRange(S, OpPC, Obj, CSK_Field)) return false; + if (!Obj.isBlockPointer()) + return false; + // FIXME(postswitch): The isUnknownSizeArray() check here is... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/222897 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
