Timm =?utf-8?q?Bäder?= <[email protected]> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/221218 >From 54bcbf58c523183ca11fa6cb1ea60384f8786f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Thu, 3 Sep 2026 14:29:25 +0200 Subject: [PATCH 1/2] opaque decl --- clang/lib/AST/ByteCode/Compiler.cpp | 19 +- clang/lib/AST/ByteCode/Interp.cpp | 218 +++++++++++++++--- clang/lib/AST/ByteCode/Interp.h | 107 ++++++--- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 57 +++-- clang/lib/AST/ByteCode/InterpHelpers.h | 5 + clang/lib/AST/ByteCode/MemberPointer.h | 2 + clang/lib/AST/ByteCode/Opcodes.td | 9 +- clang/lib/AST/ByteCode/Pointer.cpp | 82 +++++-- clang/lib/AST/ByteCode/Pointer.h | 37 ++- clang/test/AST/ByteCode/records.cpp | 11 + clang/test/CodeGen/pr4349.c | 3 +- clang/test/SemaCXX/new-delete.cpp | 14 +- .../SemaTemplate/temp_arg_nontype_cxx1z.cpp | 1 + clang/unittests/AST/ByteCode/toAPValue.cpp | 2 - 14 files changed, 444 insertions(+), 123 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 03f9478a6e4e0..85abeb7e1bd04 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -7936,12 +7936,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); @@ -8730,11 +8736,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 a41cc1f5a564b..6831c6311c93c 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); @@ -192,6 +209,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()) { @@ -452,7 +501,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; @@ -889,7 +938,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)) @@ -956,7 +1005,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)) @@ -988,7 +1037,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()) { @@ -1035,6 +1090,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; } @@ -1255,6 +1312,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; @@ -1271,7 +1330,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; } @@ -1297,6 +1356,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; @@ -1421,7 +1498,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(); @@ -1803,7 +1880,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; @@ -1819,7 +1896,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; @@ -2048,9 +2125,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 { @@ -2082,14 +2172,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()) { @@ -2154,7 +2238,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()); @@ -2323,13 +2407,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); @@ -2603,7 +2687,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; @@ -2617,9 +2701,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. @@ -2765,6 +2849,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; @@ -2854,7 +2944,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()) { @@ -2868,7 +2958,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()) { @@ -2971,6 +3066,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()); + } } } @@ -2983,6 +3089,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()) { @@ -3042,6 +3159,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()) { @@ -3347,13 +3466,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)) { @@ -3365,10 +3487,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)) { @@ -3404,6 +3526,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 only needed // to keep an invalid sample producing the same diagnostics as the current // interpreter. @@ -1716,6 +1737,10 @@ bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (!CheckThis(S, OpPC)) return false; const Pointer &This = S.Current->getThis(); + + if (!This.isBlockPointer()) + return false; + const Pointer &Field = This.atField(I); if (!CheckLoad(S, OpPC, Field)) return false; @@ -1773,6 +1798,18 @@ bool InitGlobal(InterpState &S, uint32_t I) { NewPath[I] = Val.getPathEntry(I); } Val.takePath(NewPath); + } else if constexpr (std::is_same_v<T, Pointer>) { + auto &Val = P.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 if constexpr (needsAlloc<T>()) { auto &Val = P.deref<T>(); if (!Val.singleWord()) { @@ -2090,6 +2127,8 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (!CheckThis(S, OpPC)) return false; const Pointer &This = S.Current->getThis(); + if (!This.isBlockPointer()) + return false; S.Stk.push<Pointer>(This.atField(Off)); return true; } @@ -2153,46 +2192,36 @@ inline bool CheckNull(InterpState &S, CodePtr OpPC) { return true; } -inline bool VirtBaseHelper(InterpState &S, const RecordDecl *Decl, - const Pointer &Ptr) { - 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; -} +bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl, + const Pointer &Ptr); inline bool GetPtrVirtBasePop(InterpState &S, CodePtr OpPC, - const RecordDecl *D) { + const CXXRecordDecl *D) { assert(D); const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckNull(S, OpPC, Ptr, CSK_Base)) return false; - return VirtBaseHelper(S, D, Ptr); + return virtBaseHelper(S, D, Ptr); } -inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { +inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, + const CXXRecordDecl *D) { assert(D); const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckNull(S, OpPC, Ptr, CSK_Base)) return false; - return VirtBaseHelper(S, D, Ptr); + return virtBaseHelper(S, D, Ptr); } inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC, - const RecordDecl *D) { + const CXXRecordDecl *D) { assert(D); if (S.checkingPotentialConstantExpression()) return false; if (!CheckThis(S, OpPC)) return false; const Pointer &This = S.Current->getThis(); - return VirtBaseHelper(S, D, This); + return virtBaseHelper(S, D, This); } //===----------------------------------------------------------------------===// @@ -3037,6 +3066,22 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, uint32_t FPOI) { } } +inline bool AddrOf(InterpState &S, CodePtr OpPC) { + const Pointer Ptr = S.Stk.pop<Pointer>(); + + if (Ptr.isOpaquePointer()) { + const OpaquePointer &OP = Ptr.asOpaquePointer(); + QualType T = QualType(OP.FieldType.getPointer(), 0); + T = S.getASTContext().getPointerType(T); + + S.Stk.push<Pointer>(OP.withFieldType(T.getTypePtr(), OP.isOnePastEnd())); + } else { + S.Stk.push<Pointer>(Ptr); + } + + return true; +} + bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, unsigned BitWidth); bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth); @@ -3068,6 +3113,8 @@ bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { Kind = IntegralKind::BlockAddress; } S.Stk.push<T>(Kind, PtrVal, /*Offset=*/0); + } else if (Ptr.isOpaquePointer()) { + S.Stk.push<T>(IntegralKind::Address, Ptr.asOpaquePointer().Base, 0); } else if (Ptr.isFunctionPointer()) { const void *FuncDecl = Ptr.asFunctionPointer().Func->getDecl(); S.Stk.push<T>(IntegralKind::FunctionAddress, FuncDecl, /*Offset=*/0); @@ -3496,15 +3543,17 @@ inline bool ExpandPtr(InterpState &S) { return true; } -bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - APSInt &&Index, bool AllowReplace = true); - // 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()) + if (Ptr.isOpaquePointer()) { + if (S.inConstantContext() && !Offset.isZero() && + !CheckArray(S, OpPC, Ptr)) { + return false; + } return arrayElemPtrOpaque(S, OpPC, Ptr, Offset.toAPSInt()); + } if (Offset.isZero()) { if (const Descriptor *Desc = Ptr.getFieldDesc(); @@ -4141,6 +4190,10 @@ inline bool BitCast(InterpState &S, CodePtr OpPC) { Pointer FromPtr = S.Stk.pop<Pointer>(); Pointer &ToPtr = S.Stk.peek<Pointer>(); + // FIXME: Could allow reading from string pointers? + if (!FromPtr.isBlockPointer() || !ToPtr.isBlockPointer()) + return false; + const Descriptor *D = FromPtr.getFieldDesc(); if (D->isPrimitiveArray() && FromPtr.isArrayRoot()) FromPtr = FromPtr.atIndex(0); diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index b78ce614e290d..94a0e1e154a5c 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -165,6 +165,9 @@ static QualType getElemType(const Pointer &P) { ->getElementType(); } + if (P.isOpaquePointer() || P.isIntegralPointer()) + return P.getType(); + const Descriptor *Desc = P.getFieldDesc(); QualType T = Desc->getType(); if (Desc->isPrimitive()) @@ -409,7 +412,7 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, if (!StrPtr.isBlockPointer()) return false; - if (!CheckDummy(S, OpPC, StrPtr.block(), AK_Read)) + if (!CheckDummy(S, OpPC, StrPtr, AK_Read)) return false; if (!StrPtr.getFieldDesc()->isPrimitiveArray()) @@ -1318,13 +1321,13 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC, } assert(FirstArgT == PT_Ptr); const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!Ptr.isBlockPointer()) { + if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer()) { S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_compute) << Alignment; return false; } - const ValueDecl *PtrDecl = Ptr.getDeclDesc()->asValueDecl(); + const VarDecl *PtrDecl = Ptr.getRootVarDecl(); // We need a pointer for a declaration here. if (!PtrDecl) { if (BuiltinOp == Builtin::BI__builtin_is_aligned) @@ -1336,10 +1339,19 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC, return false; } - // For one-past-end pointers, we can't call getIndex() since it asserts. - // Use getNumElems() instead which gives the correct index for past-end. - unsigned PtrOffset = - Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex(); + unsigned PtrOffset; + if (Ptr.isBlockPointer()) { + // For one-past-end pointers, we can't call getIndex() since it asserts. + // Use getNumElems() instead which gives the correct index for past-end. + PtrOffset = Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex(); + } else { + if (std::optional<size_t> PtrOff = + Ptr.computeLayoutOffset(S.getASTContext())) + PtrOffset = *PtrOff; + else + return false; + } + CharUnits BaseAlignment = S.getASTContext().getDeclAlign(PtrDecl); CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset)); @@ -1388,8 +1400,18 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC, ? llvm::alignDown(PtrOffset, Alignment64) : llvm::alignTo(PtrOffset, Alignment64)); - S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity())); - return true; + if (Ptr.isBlockPointer()) { + S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity())); + return true; + } + + assert(Ptr.isOpaquePointer()); + + APSInt APOffset = + APSInt(APInt(64, NewOffset.getQuantity(), /*IsSigned=*/true), + /*IsUnsigned=*/false); + return arrayElemPtrOpaque(S, OpPC, Ptr, std::move(APOffset), + /*AllocReplace=*/true); } // Otherwise, we cannot constant-evaluate the result. @@ -1420,9 +1442,9 @@ static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC, CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue()); // If there is a base object, then it must have the correct alignment. - if (Ptr.isBlockPointer()) { + if (Ptr.isBlockPointer() || Ptr.isOpaquePointer()) { CharUnits BaseAlignment; - if (const auto *VD = Ptr.getDeclDesc()->asValueDecl()) + if (const auto *VD = Ptr.getRootVarDecl()) BaseAlignment = ASTCtx.getDeclAlign(VD); else if (const auto *E = Ptr.getRootExpr()) BaseAlignment = GetAlignOfExpr(ASTCtx, E, UETT_AlignOf); @@ -1443,7 +1465,7 @@ static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC, if (ExtraOffset) AVOffset -= CharUnits::fromQuantity(ExtraOffset->getZExtValue()); if (AVOffset.alignTo(Align) != AVOffset) { - if (Ptr.isBlockPointer()) + if (Ptr.isBlockPointer() || Ptr.isOpaquePointer()) S.CCEDiag(Call->getArg(0), diag::note_constexpr_baa_insufficient_alignment) << 1 << AVOffset.getQuantity() << Align.getQuantity(); @@ -2123,10 +2145,6 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, pushInteger(S, 0, Call->getType()); return true; } - - if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType()) - return false; - bool IsWide = (ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp); @@ -2144,6 +2162,9 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, return false; } + if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType()) + return false; + if (!CheckLoad(S, OpPC, PtrA, AK_Read) || !CheckLoad(S, OpPC, PtrB, AK_Read)) return false; @@ -2423,14 +2444,14 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, return false; if (!CheckMutable(S, OpPC, Ptr)) return false; - if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read)) + if (!CheckDummy(S, OpPC, Ptr, AK_Read)) return false; } // Check if we're currently running an initializer. if (S.initializingBlock(Ptr.block())) return Error(2); - if (S.EvaluatingDecl && Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl) + if (S.EvaluatingDecl && Ptr.getRootVarDecl() == S.EvaluatingDecl) return Error(2); pushInteger(S, Result, Call->getType()); diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h b/clang/lib/AST/ByteCode/InterpHelpers.h index 4c60670ac5a0c..f183efb5b19d1 100644 --- a/clang/lib/AST/ByteCode/InterpHelpers.h +++ b/clang/lib/AST/ByteCode/InterpHelpers.h @@ -41,6 +41,11 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, /// Checks if a pointer is a dummy pointer. bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK); +bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK); + +bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + APSInt &&Index, bool AllowReplace = true); /// Checks if a pointer is in range. template <typename T> diff --git a/clang/lib/AST/ByteCode/MemberPointer.h b/clang/lib/AST/ByteCode/MemberPointer.h index b23acf7befc67..39c7c24e1a304 100644 --- a/clang/lib/AST/ByteCode/MemberPointer.h +++ b/clang/lib/AST/ByteCode/MemberPointer.h @@ -101,6 +101,8 @@ class MemberPointer final { std::optional<Pointer> toPointer(const Context &Ctx) const; bool isBaseCastPossible() const { + if (!Base.isBlockPointer()) + return false; if (PtrOffset < 0) return true; return static_cast<uint64_t>(PtrOffset) <= Base.getByteOffset(); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 4be5495a7ed25..1831dc161f0a5 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -55,6 +55,7 @@ def ArgFixedPoint : ArgType { let Name = "FixedPoint"; let AsRef = true; } def ArgFunction : ArgType { let Name = "const Function *"; } def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; } def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; } +def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; } def ArgRecordField : ArgType { let Name = "const Record::Field *"; } def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; } def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; } @@ -328,6 +329,8 @@ class OffsetOpcode : Opcode { let Args = [ArgUint32]; } +def AddrOf : Opcode; + // [] -> [Pointer] def GetPtrLocal : OffsetOpcode { bit HasCustomEval = 1; @@ -379,11 +382,11 @@ def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, ArgBool, ArgTypePtr]; } // [Pointer] -> [Pointer] def GetPtrVirtBasePop : Opcode { // RecordDecl of base class. - let Args = [ArgRecordDecl]; + let Args = [ArgCXXRecordDecl]; } def GetPtrVirtBase : Opcode { // RecordDecl of base class. - let Args = [ArgRecordDecl]; + let Args = [ArgCXXRecordDecl]; } def IsBaseClass : SuccessOpcode; @@ -397,7 +400,7 @@ def GetPtrThisBase : Opcode { // [] -> [Pointer] def GetPtrThisVirtBase : Opcode { // RecordDecl of base class. - let Args = [ArgRecordDecl]; + let Args = [ArgCXXRecordDecl]; } // [] -> [Pointer] def This : Opcode; diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 81fa2beaedb24..8016680ef9c0a 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -242,9 +242,34 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { return APValue(APValue::LValueBase(Str.Base), CharUnits::fromQuantity(Offset * elemSize()), Path, /*OnePastTheEnd=*/false, /*IsNull=*/false); - case Storage::Opaque: - return APValue(APValue::LValueBase(Opaque.Base), CharUnits::Zero(), Path, - /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false); + case Storage::Opaque: { + if (!Opaque.Base->getType()->isPointerType()) { + for (const PointerPathEntry &Entry : Opaque.path()) { + switch (Entry.Kind) { + case PointerPathEntry::Field: + Path.push_back(APValue::LValuePathEntry({Entry.FD, false})); + break; + case PointerPathEntry::Base: + Path.push_back(APValue::LValuePathEntry( + {Entry.RD.getPointer(), Entry.RD.getInt()})); + break; + case PointerPathEntry::Array: + Path.push_back(APValue::LValuePathEntry::ArrayIndex(Entry.Index)); + break; + case PointerPathEntry::NegativeArray: + Path.push_back(APValue::LValuePathEntry::ArrayIndex(-Entry.Index)); + break; + } + } + } + size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(0); + auto Offset = CharUnits::fromQuantity(LayoutOffset + getByteOffset()); + auto Result = + APValue(Opaque.Base, Offset, Path, + /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false); + Result.setConstexprUnknown(Opaque.isConstexprUnknown()); + return Result; + } } assert(isBlockPointer()); @@ -446,7 +471,9 @@ Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { case Storage::String: return reinterpret_cast<uintptr_t>(Str.getLiteral()) + Offset; case Storage::Opaque: - return reinterpret_cast<uintptr_t>(asOpaquePointer().Base) + Offset; + if (auto O = Opaque.computeLayoutOffset(ASTCtx)) + return *O + Offset; + return std::nullopt; } auto getTypeSize = [&](QualType T) -> std::optional<size_t> { @@ -527,7 +554,9 @@ Pointer::computeLayoutOffset(const ASTContext &ASTCtx) const { case Storage::String: return Offset * Str.getLiteral()->getCharByteWidth(); case Storage::Opaque: - return Opaque.computeLayoutOffset(ASTCtx); + if (auto O = Opaque.computeLayoutOffset(ASTCtx)) + return *O + Offset; + return std::nullopt; } auto getTypeSize = [&](QualType T) -> std::optional<size_t> { @@ -876,17 +905,39 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { if (A.isZero() && B.isZero()) return true; - if (A.isIntegralPointer() && B.isIntegralPointer()) + // We allow comparisons between opaque pointers and block pointers, provided + // they have the same declaration as base. + if (A.StorageKind != B.StorageKind) { + if (A.isOpaquePointer() && B.isBlockPointer()) { + if (const VarDecl *BDecl = B.block()->getDescriptor()->asVarDecl()) + return BDecl == A.Opaque.Base->getMostRecentDecl(); + + return false; + } + if (B.isOpaquePointer() && A.isBlockPointer()) { + if (const VarDecl *ADecl = A.block()->getDescriptor()->asVarDecl()) + return ADecl == B.Opaque.Base->getMostRecentDecl(); + return false; + } + return false; + } + + switch (A.StorageKind) { + case Storage::Int: return true; - if (A.isFunctionPointer() && B.isFunctionPointer()) + case Storage::Block: + // See below. + break; + case Storage::Fn: return true; - if (A.isTypeidPointer() && B.isTypeidPointer()) + case Storage::Typeid: return A.asTypeidPointer().TypePtr == B.asTypeidPointer().TypePtr; - if (A.isStringPointer() && B.isStringPointer()) + case Storage::String: return A.Str.ID == B.Str.ID && A.Str.getLiteral() == B.Str.getLiteral(); - - if (A.StorageKind != B.StorageKind) - return false; + case Storage::Opaque: + return A.asOpaquePointer().Base->getMostRecentDecl() == + B.asOpaquePointer().Base->getMostRecentDecl(); + } return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee; } @@ -1292,7 +1343,12 @@ OpaquePointer::computeLayoutOffset(const ASTContext &ASTCtx) const { return std::nullopt; const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD); - Offset += Layout.getBaseClassOffset(Entry.RD.getPointer()).getQuantity(); + if (Entry.RD.getInt()) + Offset += + Layout.getVBaseClassOffset(Entry.RD.getPointer()).getQuantity(); + else + Offset += + Layout.getBaseClassOffset(Entry.RD.getPointer()).getQuantity(); CurType = ASTCtx.getCanonicalTagType(Entry.RD.getPointer()); } break; diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index fd09b4908f0b5..3bf6a363d6afd 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -494,7 +494,6 @@ struct OpaquePointer { bool isUnknownSizeArray() const; bool isRoot() const; }; -struct OpaqueTag {}; enum class Storage { Int, Block, Fn, Typeid, String, Opaque }; @@ -570,18 +569,33 @@ class Pointer { /// Equality operators are just for tests. bool operator==(const Pointer &P) const { - if (P.StorageKind != StorageKind) + if (StorageKind != P.StorageKind) return false; - if (isIntegralPointer()) + + switch (StorageKind) { + case Storage::Int: return P.Int.Value == Int.Value && P.Int.Ty == Int.Ty && P.Offset == Offset; - - if (isFunctionPointer()) + case Storage::Block: + return P.view() == view(); + case Storage::Fn: return P.Fn.Func == Fn.Func && P.Offset == Offset; - if (isStringPointer()) + case Storage::Typeid: + llvm_unreachable("typeid in operator==?"); + case Storage::String: return Str.Base == P.Str.Base && Offset == P.Offset; - - return P.view() == view(); + case Storage::Opaque: + if (!(P.Opaque.Base == Opaque.Base && + P.Opaque.PathLength == Opaque.PathLength)) + return false; + if (P.Offset != Offset) + return false; + if (Opaque.PathLength == 0) + return true; + return std::memcmp(P.Opaque.Path, Opaque.Path, + sizeof(PointerPathEntry) * Opaque.PathLength) == 0; + } + llvm_unreachable("Unhandled storage kind"); } bool operator!=(const Pointer &P) const { return !(P == *this); } @@ -922,6 +936,9 @@ class Pointer { return Fn.Func->getDecl()->isWeak(); } + + if (isOpaquePointer()) + return Opaque.Base->isWeak(); if (!isBlockPointer()) return false; @@ -940,6 +957,8 @@ class Pointer { /// Checks if the pointer points to a dummy value. bool isDummy() const { + if (isOpaquePointer()) + return true; if (!isBlockPointer()) return false; return view().isDummy(); @@ -951,6 +970,8 @@ class Pointer { return true; if (isStringPointer()) return true; + if (!isBlockPointer()) + return false; return view().isConst(); } bool isConstInMutable() const { diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp index 36b5cb62fe95f..90e7c1eb6ec64 100644 --- a/clang/test/AST/ByteCode/records.cpp +++ b/clang/test/AST/ByteCode/records.cpp @@ -2054,3 +2054,14 @@ namespace BaseInitViaDIE { constexpr SS ss {}; static_assert(ss.b == 42, ""); } + +namespace OPEOpaque { + struct S {char c[14];}; + extern S s; + static_assert((&s + 1) - &s == 1, ""); + + extern int a[12]; + static_assert ((&a + 12 - &a) == 12, ""); // both-error {{not an integral constant expression}} \ + // both-note {{cannot refer to element 12 of non-array object in a constant expression}} + +} diff --git a/clang/test/CodeGen/pr4349.c b/clang/test/CodeGen/pr4349.c index 3bec499e0b3f5..025a9b3903775 100644 --- a/clang/test/CodeGen/pr4349.c +++ b/clang/test/CodeGen/pr4349.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - -fexperimental-new-constant-interpreter | FileCheck %s // PR 4349 union reg diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 595d0325be12f..bd1eb23023d6f 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -721,19 +721,9 @@ int (*const_fold)[12] = new int[3][&const_fold + 12 - &const_fold]; #if __cplusplus >= 201402L // expected-error@-2 {{array size is not a constant expression}} // expected-note@-3 {{cannot refer to element 12 of non-array}} -#elif __cplusplus == 201103L -#if defined(NEW_INTERP) -// expected-error@-6 {{only the first dimension of an allocated array may have dynamic size}} -// expected-note@-7 {{cannot refer to element 12 of non-array}} -#endif #elif __cplusplus < 201103L -#if defined(NEW_INTERP) -// expected-error@-11 {{only the first dimension of an allocated array may have dynamic size}} -// expected-note@-12 {{cannot refer to element 12 of non-array}} -#else -// expected-error@-14 {{cannot allocate object of variably modified type}} -// expected-warning@-15 {{variable length arrays in C++ are a Clang extension}} -#endif +// expected-error@-5 {{cannot allocate object of variably modified type}} +// expected-warning@-6 {{variable length arrays in C++ are a Clang extension}} #endif #if __cplusplus >= 201103L diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 9c25e26f43c36..cede91cd41997 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s -fexperimental-new-constant-interpreter template<typename T, T val> struct A {}; // expected-note 3{{template parameter is declared here}} diff --git a/clang/unittests/AST/ByteCode/toAPValue.cpp b/clang/unittests/AST/ByteCode/toAPValue.cpp index 702a07a638915..a8e1d5e217597 100644 --- a/clang/unittests/AST/ByteCode/toAPValue.cpp +++ b/clang/unittests/AST/ByteCode/toAPValue.cpp @@ -102,8 +102,6 @@ TEST(ToAPValue, Pointers) { ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u); ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u); ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u); - ASSERT_TRUE( - GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType()); } } >From 8d1b796655ccda9b2207af53c4812a4b8c9228b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Fri, 4 Sep 2026 08:07:34 +0200 Subject: [PATCH 2/2] opaque Expr --- clang/lib/AST/ByteCode/Compiler.cpp | 19 +---- clang/lib/AST/ByteCode/DeclOrExpr.h | 7 ++ clang/lib/AST/ByteCode/Disasm.cpp | 6 +- clang/lib/AST/ByteCode/EvaluationResult.cpp | 4 +- clang/lib/AST/ByteCode/Interp.cpp | 78 +++---------------- clang/lib/AST/ByteCode/Interp.h | 33 ++++---- clang/lib/AST/ByteCode/InterpBlock.cpp | 11 +-- clang/lib/AST/ByteCode/InterpBlock.h | 14 +--- .../AST/ByteCode/InterpBuiltinObjectSize.cpp | 4 +- clang/lib/AST/ByteCode/InterpHelpers.h | 1 - clang/lib/AST/ByteCode/Opcodes.td | 3 +- clang/lib/AST/ByteCode/Pointer.cpp | 59 ++++++++------ clang/lib/AST/ByteCode/Pointer.h | 34 ++++---- clang/lib/AST/ByteCode/Program.cpp | 63 --------------- clang/lib/AST/ByteCode/Program.h | 6 -- 15 files changed, 96 insertions(+), 246 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 85abeb7e1bd04..d2feac9c03b30 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -8735,24 +8735,7 @@ bool Compiler<Emitter>::emitDestructionPop(const Descriptor *Desc, template <class Emitter> bool Compiler<Emitter>::emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU) { assert(!DiscardResult && "Should've been checked before"); - - 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; - if (E->getType()->isVoidType()) - return true; - - // Convert the dummy pointer to another pointer type if we have to. - if (PrimType PT = classifyPrim(E); PT != PT_Ptr) { - if (isPtrType(PT)) - return this->emitDecayPtr(PT_Ptr, PT, E); - return false; - } - return true; + return this->emitGetOpaquePtr(D, CU, E); } template <class Emitter> diff --git a/clang/lib/AST/ByteCode/DeclOrExpr.h b/clang/lib/AST/ByteCode/DeclOrExpr.h index e170b52c6e51d..b844c5644a233 100644 --- a/clang/lib/AST/ByteCode/DeclOrExpr.h +++ b/clang/lib/AST/ByteCode/DeclOrExpr.h @@ -28,6 +28,7 @@ struct DeclOrExpr { bool isExpr() const { return isa_and_nonnull<const Expr *>(V); } bool isDecl() const { return isa_and_nonnull<const Decl *>(V); } bool isValueDecl() const { return isa_and_nonnull<ValueDecl>(asDecl()); } + bool isVarDecl() const { return isa_and_nonnull<VarDecl>(asDecl()); } const Expr *asExpr() const { return V.dyn_cast<const Expr *>(); } const Decl *asDecl() const { return V.dyn_cast<const Decl *>(); } @@ -49,6 +50,12 @@ struct DeclOrExpr { return VD->getType(); return asExpr()->getType(); } + + SourceLocation getLocation() const { + if (const auto *VD = asValueDecl()) + return VD->getLocation(); + return asExpr()->getExprLoc(); + } }; static_assert(sizeof(DeclOrExpr) == sizeof(void *)); diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index 03187529541d8..c319591b1fee9 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -345,7 +345,6 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { // All the maps. Bytes += GlobalIndices.getMemorySize(); Bytes += Records.getMemorySize(); - Bytes += DummyVariables.getMemorySize(); // All Records. for (const Record *R : Records.values()) { @@ -372,8 +371,6 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { : TerminalColor{llvm::raw_ostream::RED, false}); OS << (GP.isInitialized() ? "initialized " : "uninitialized "); } - if (GP.block()->isDummy()) - OS << "dummy "; Desc->dump(OS); if (GP.isInitialized() && Desc->IsTemporary) { @@ -402,7 +399,7 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { } OS << "\n"; - if (GP.isInitialized() && Desc->isPrimitive() && !G->block()->isDummy()) { + if (GP.isInitialized() && Desc->isPrimitive()) { OS << " "; { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false}); @@ -634,7 +631,6 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { OS << " Extern: " << isExtern() << "\n"; OS << " Initialized: " << IsInitialized << "\n"; OS << " Weak: " << isWeak() << "\n"; - OS << " Dummy: " << isDummy() << '\n'; OS << " Dynamic: " << isDynamic() << "\n"; OS << " Metadata: " << MDSize << '\n'; } diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index bc939c9a5c8fc..5d232c5414e04 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -179,8 +179,8 @@ static void collectBlocks(PtrView Ptr, P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd(); }; - if (!Ptr.isLive() || Ptr.isZero() || Ptr.isDummy() || - Ptr.isUnknownSizeArray() || Ptr.isOnePastEnd()) + if (!Ptr.isLive() || Ptr.isZero() || Ptr.isUnknownSizeArray() || + Ptr.isOnePastEnd()) return; Blocks.insert(Ptr.Pointee); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 6831c6311c93c..9185f1d619a00 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -111,7 +111,7 @@ static void noteValueLocation(InterpState &S, const Pointer &Ptr) { } if (Ptr.isOpaquePointer()) - S.Note(Ptr.asOpaquePointer().Base->getLocation(), diag::note_declared_at); + S.Note(Ptr.asOpaquePointer().Base.getLocation(), diag::note_declared_at); } static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, @@ -209,13 +209,8 @@ 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, +static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) { - - if (!Ptr.isBlockPointer()) - return true; - - const Block *B = Ptr.block(); if (B->getDeclID()) { if (!(B->isStatic() && B->isTemporary())) return true; @@ -241,31 +236,11 @@ static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return true; } -static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B, +static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { - 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; + if (!Ptr.isBlockPointer()) + return true; + return CheckTemporary(S, OpPC, Ptr.block(), AK); } static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { @@ -340,8 +315,6 @@ void cleanupAfterFunctionCall(InterpState &S, const Function *Func) { } bool isConstexprUnknown(const Block *B) { - if (B->isDummy()) - return isa_and_nonnull<ParmVarDecl>(B->getDescriptor()->asValueDecl()); return B->getDescriptor()->IsConstexprUnknown; } @@ -868,8 +841,6 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) { if (!B->isAccessible()) { if (!CheckExtern(S, OpPC, Pointer(const_cast<Block *>(B)))) return false; - if (!CheckDummy(S, OpPC, B, AK_Read)) - return false; return CheckWeak(S, OpPC, B); } @@ -945,8 +916,6 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; if (!CheckExtern(S, OpPC, Ptr)) return false; - if (!CheckDummy(S, OpPC, Ptr.block(), AK)) - return false; return CheckWeak(S, OpPC, Ptr.block()); } @@ -1012,8 +981,6 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; if (!CheckExtern(S, OpPC, Ptr)) return false; - if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read)) - return false; return CheckWeak(S, OpPC, Ptr.block()); } @@ -1049,9 +1016,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, if (!Ptr.block()->isAccessible()) { if (!CheckLive(S, OpPC, Ptr, AK_Assign)) return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - return CheckDummy(S, OpPC, Ptr.block(), AK_Assign); + return CheckExtern(S, OpPC, Ptr); } if (!WillBeActivated && !CheckLifetime(S, OpPC, Ptr, AK_Assign)) return false; @@ -1373,25 +1338,6 @@ bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, 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; - - const ValueDecl *D = B->getDescriptor()->asValueDecl(); - 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) { - const SourceInfo &E = S.Current->getSource(OpPC); - S.FFDiag(E, diag::note_constexpr_modify_global); - } - return false; -} - static bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *CE, unsigned ArgSize) { auto Args = ArrayRef(CE->getArgs(), CE->getNumArgs()); @@ -2640,7 +2586,7 @@ static void setLifeStateRecurse(PtrView Ptr, Lifetime L) { /// Ends the lifetime of the peek'd pointer. bool EndLifetime(InterpState &S, CodePtr OpPC) { const auto &Ptr = S.Stk.peek<Pointer>(); - if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy)) + if (!CheckDummy(S, OpPC, Ptr, AK_Destroy)) return false; setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Ended); @@ -2658,7 +2604,7 @@ bool PseudoDtor(InterpState &S, CodePtr OpPC) { bool MarkDestroyed(InterpState &S, CodePtr OpPC) { const auto &Ptr = S.Stk.peek<Pointer>(); - if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy)) + if (!CheckDummy(S, OpPC, Ptr, AK_Destroy)) return false; setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Destroyed); @@ -2855,12 +2801,6 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, return Ptr.isRoot(); } - if (Ptr.isDummy()) { - if (!CheckIntegralAddressCast(S, OpPC, BitWidth)) - return false; - return Ptr.getIndex() == 0; - } - if (!Ptr.isZero()) { // Only allow based lvalue casts if they are lossless. if (!CheckIntegralAddressCast(S, OpPC, BitWidth)) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 343883872728b..a208f8d1eed0a 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2274,7 +2274,7 @@ inline bool LoadPopL(InterpState &S, CodePtr OpPC) { if (Ptr.isOpaquePointer()) { const OpaquePointer &OP = Ptr.asOpaquePointer(); - if (!Ptr.asOpaquePointer().Base->getType()->isPointerType()) + if (!Ptr.asOpaquePointer().Base.getType()->isPointerType()) return false; QualType T = Ptr.getType(); @@ -2747,9 +2747,9 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { return false; } -inline bool GetOpaquePtr(InterpState &S, const ValueDecl *VD, +inline bool GetOpaquePtr(InterpState &S, DeclOrExpr DOE, bool ConstexprUnknown) { - S.Stk.push<Pointer>(VD, ConstexprUnknown); + S.Stk.push<Pointer>(DOE, ConstexprUnknown); return true; } @@ -3072,8 +3072,8 @@ inline bool AddrOf(InterpState &S, CodePtr OpPC) { if (Ptr.isOpaquePointer()) { const OpaquePointer &OP = Ptr.asOpaquePointer(); QualType T = QualType(OP.FieldType.getPointer(), 0); - T = S.getASTContext().getPointerType(T); + T = S.getASTContext().getPointerType(T); S.Stk.push<Pointer>(OP.withFieldType(T.getTypePtr(), OP.isOnePastEnd())); } else { S.Stk.push<Pointer>(Ptr); @@ -3098,23 +3098,18 @@ bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation())); } else if constexpr (isIntegralOrPointer<T>()) { if (Ptr.isBlockPointer()) { - IntegralKind Kind = IntegralKind::Address; - const void *PtrVal; - if (Ptr.isDummy()) { - if (const Expr *E = Ptr.getRootExpr()) { - PtrVal = E; - if (isa<AddrLabelExpr>(E)) - Kind = IntegralKind::LabelAddress; - } else { - PtrVal = Ptr.getDeclDesc()->asDecl(); - } + S.Stk.push<T>(IntegralKind::BlockAddress, Ptr.block(), /*Offset=*/0); + } else if (Ptr.isOpaquePointer()) { + if (const Expr *BaseExpr = Ptr.asOpaquePointer().getBaseExpr()) { + IntegralKind Kind = IntegralKind::ExprAddress; + if (isa<AddrLabelExpr>(BaseExpr)) + Kind = IntegralKind::LabelAddress; + S.Stk.push<T>(Kind, BaseExpr, 0); } else { - PtrVal = Ptr.block(); - Kind = IntegralKind::BlockAddress; + S.Stk.push<T>(IntegralKind::Address, + Ptr.asOpaquePointer().Base.asVarDecl(), 0); } - S.Stk.push<T>(Kind, PtrVal, /*Offset=*/0); - } else if (Ptr.isOpaquePointer()) { - S.Stk.push<T>(IntegralKind::Address, Ptr.asOpaquePointer().Base, 0); + } else if (Ptr.isFunctionPointer()) { const void *FuncDecl = Ptr.asFunctionPointer().Func->getDecl(); S.Stk.push<T>(IntegralKind::FunctionAddress, FuncDecl, /*Offset=*/0); diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp index f43b477dff7f9..888a660719590 100644 --- a/clang/lib/AST/ByteCode/InterpBlock.cpp +++ b/clang/lib/AST/ByteCode/InterpBlock.cpp @@ -102,20 +102,11 @@ bool Block::hasPointer(const Pointer *P) const { void Block::movePointersTo(Block *B) { assert(B != this); - unsigned MDDiff = static_cast<int>(B->MDSize) - static_cast<int>(MDSize); while (Pointers) { Pointer *P = Pointers; - this->removePointer(P); P->BS.Pointee = B; - - // If the metadata size changed between the two blocks, move the pointer - // base/offset. Realistically, this should only happen when we move pointers - // from a dummy pointer to a global one. - P->BS.Base += MDDiff; - P->Offset += MDDiff; - B->addPointer(P); } assert(!this->hasPointers()); @@ -135,7 +126,7 @@ void Block::removePointers() { DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk) : Root(Root), B(~0u, Blk->Desc, Blk->MDSize, Blk->isExtern(), Blk->IsStatic, - Blk->isWeak(), Blk->isDummy(), + Blk->isWeak(), /*IsDead=*/true) { // Add the block to the chain of dead blocks. if (Root) diff --git a/clang/lib/AST/ByteCode/InterpBlock.h b/clang/lib/AST/ByteCode/InterpBlock.h index ffb4bf2e75654..4d6a2ecf81321 100644 --- a/clang/lib/AST/ByteCode/InterpBlock.h +++ b/clang/lib/AST/ByteCode/InterpBlock.h @@ -45,7 +45,6 @@ class Block final { static constexpr uint8_t ExternFlag = 1 << 0; static constexpr uint8_t DeadFlag = 1 << 1; static constexpr uint8_t WeakFlag = 1 << 2; - static constexpr uint8_t DummyFlag = 1 << 3; public: static constexpr uint8_t InlineDescMD = sizeof(InlineDescriptor); @@ -54,23 +53,20 @@ class Block final { /// Creates a new block. Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc, unsigned MDSize = 0, bool IsStatic = false, bool IsExtern = false, - bool IsWeak = false, bool IsDummy = false) + bool IsWeak = false) : Desc(Desc), DeclID(DeclID), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) { assert(Desc); AccessFlags |= (ExternFlag * IsExtern); AccessFlags |= (WeakFlag * IsWeak); - AccessFlags |= (DummyFlag * IsDummy); } Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize = 0, - bool IsStatic = false, bool IsExtern = false, bool IsWeak = false, - bool IsDummy = false) + bool IsStatic = false, bool IsExtern = false, bool IsWeak = false) : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) { assert(Desc); AccessFlags |= (ExternFlag * IsExtern); AccessFlags |= (WeakFlag * IsWeak); - AccessFlags |= (DummyFlag * IsDummy); } /// Returns the block's descriptor. @@ -85,7 +81,6 @@ class Block final { bool isTemporary() const { return Desc->IsTemporary; } bool isWeak() const { return AccessFlags & WeakFlag; } bool isDynamic() const { return (DynAllocId != std::nullopt); } - bool isDummy() const { return AccessFlags & DummyFlag; } bool isDead() const { return AccessFlags & DeadFlag; } /// Returns the size of the block, including metadata. unsigned getSize() const { return Desc->getAllocSize() + MDSize; } @@ -168,13 +163,12 @@ class Block final { friend class Program; Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize, bool IsExtern, - bool IsStatic, bool IsWeak, bool IsDummy, bool IsDead) + bool IsStatic, bool IsWeak, bool IsDead) : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) { assert(Desc); AccessFlags |= (ExternFlag * IsExtern); AccessFlags |= (DeadFlag * IsDead); AccessFlags |= (WeakFlag * IsWeak); - AccessFlags |= (DummyFlag * IsDummy); } /// To be called by DynamicAllocator. @@ -200,7 +194,7 @@ class Block final { const unsigned EvalID = ~0u; /// Allocation ID for this dynamic allocation, if it is one. UnsignedOrNone DynAllocId = std::nullopt; - /// AccessFlags containing IsExtern, IsDead, IsWeak, and IsDummy bits. + /// AccessFlags containing IsExtern, IsDead and IsWeak bits. uint8_t AccessFlags = 0; /// Size of the metadata. const uint8_t MDSize = 0; diff --git a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp index c1a69e03c0e73..1483dbbedeb29 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp @@ -352,7 +352,7 @@ computeOpaqueSize(const ASTContext &ASTCtx, const Pointer &Ptr, return TypeSize.getQuantity(); // Check if we need to add the flexible array member size. - const VarDecl *Base = dyn_cast<VarDecl>(OP.Base); + const VarDecl *Base = OP.getBaseDecl(); if (!Base) return TypeSize.getQuantity(); @@ -390,7 +390,7 @@ UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext &ASTCtx, if (Ptr.isOpaquePointer()) { bool UseClosestSurroundingVariable = (Kind == 1) || (Kind == 3); const OpaquePointer &OP = Ptr.asOpaquePointer(); - InvalidBase = OP.Base->getType()->isPointerType(); + InvalidBase = OP.Base.getType()->isPointerType(); bool DetermineForCompleteObject = pointsToCompleteObject(ASTCtx, Ptr); bool WritingOffTheEnd = isUserWritingOffTheEnd(ASTCtx, OP); diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h b/clang/lib/AST/ByteCode/InterpHelpers.h index f183efb5b19d1..703dec4d43d72 100644 --- a/clang/lib/AST/ByteCode/InterpHelpers.h +++ b/clang/lib/AST/ByteCode/InterpHelpers.h @@ -40,7 +40,6 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); /// Checks if a pointer is a dummy pointer. -bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK); bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 1831dc161f0a5..a3aaeff67e301 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -72,6 +72,7 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; } def ArgPrimType : ArgType { let Name = "PrimType"; } def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; } def ArgTypePtr : ArgType { let Name = "const Type *"; } +def ArgDeclOrExpr : ArgType { let Name = "DeclOrExpr"; } //===----------------------------------------------------------------------===// // Classes of types instructions operate on. @@ -618,7 +619,7 @@ def AddOffset : Opcode { } def GetOpaquePtr : SuccessOpcode { - let Args = [ArgValueDecl, ArgBool]; + let Args = [ArgDeclOrExpr, ArgBool]; } // [Pointer, Integral] -> [Pointer] diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 8016680ef9c0a..7d57294926184 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -243,7 +243,7 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { CharUnits::fromQuantity(Offset * elemSize()), Path, /*OnePastTheEnd=*/false, /*IsNull=*/false); case Storage::Opaque: { - if (!Opaque.Base->getType()->isPointerType()) { + if (!Opaque.Base.getType()->isPointerType()) { for (const PointerPathEntry &Entry : Opaque.path()) { switch (Entry.Kind) { case PointerPathEntry::Field: @@ -264,9 +264,15 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { } size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(0); auto Offset = CharUnits::fromQuantity(LayoutOffset + getByteOffset()); - auto Result = - APValue(Opaque.Base, Offset, Path, - /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false); + APValue Result; + if (const Expr *E = Opaque.Base.asExpr()) + Result = + APValue(E, Offset, Path, + /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false); + else + Result = + APValue(Opaque.Base.asValueDecl(), Offset, Path, + /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false); Result.setConstexprUnknown(Opaque.isConstexprUnknown()); return Result; } @@ -908,15 +914,17 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { // We allow comparisons between opaque pointers and block pointers, provided // they have the same declaration as base. if (A.StorageKind != B.StorageKind) { - if (A.isOpaquePointer() && B.isBlockPointer()) { + if (A.isOpaquePointer() && A.Opaque.Base.isVarDecl() && + B.isBlockPointer()) { if (const VarDecl *BDecl = B.block()->getDescriptor()->asVarDecl()) - return BDecl == A.Opaque.Base->getMostRecentDecl(); + return BDecl == A.Opaque.Base.asVarDecl()->getMostRecentDecl(); return false; } - if (B.isOpaquePointer() && A.isBlockPointer()) { + if (B.isOpaquePointer() && B.Opaque.Base.isVarDecl() && + A.isBlockPointer()) { if (const VarDecl *ADecl = A.block()->getDescriptor()->asVarDecl()) - return ADecl == B.Opaque.Base->getMostRecentDecl(); + return ADecl == B.Opaque.Base.asVarDecl()->getMostRecentDecl(); return false; } return false; @@ -926,8 +934,7 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { case Storage::Int: return true; case Storage::Block: - // See below. - break; + return A.BS.Pointee == B.BS.Pointee; case Storage::Fn: return true; case Storage::Typeid: @@ -935,11 +942,14 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { case Storage::String: return A.Str.ID == B.Str.ID && A.Str.getLiteral() == B.Str.getLiteral(); case Storage::Opaque: - return A.asOpaquePointer().Base->getMostRecentDecl() == - B.asOpaquePointer().Base->getMostRecentDecl(); + if (A.Opaque.Base.isExpr()) + return B.Opaque.Base.isExpr() && A.Opaque.Base == B.Opaque.Base; + if (A.Opaque.Base.isVarDecl()) + return B.Opaque.Base.isVarDecl() && + A.Opaque.Base.asVarDecl()->getMostRecentDecl() == + B.Opaque.Base.asVarDecl()->getMostRecentDecl(); + return false; } - - return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee; } bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) { @@ -989,23 +999,24 @@ bool Pointer::elemsOfSameArray(const Pointer &A, const Pointer &B) { return true; } +// FIXME: This should return true for string pointers. bool Pointer::pointsToLiteral() const { - if (isZero() || !isBlockPointer()) + if (isZero()) return false; - if (block()->isDynamic()) + if (isDynamic()) return false; - const Expr *E = block()->getDescriptor()->asExpr(); + const Expr *E = getRootExpr(); return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E); } bool Pointer::pointsToLabel() const { - if (isZero() || !isBlockPointer()) + if (isZero()) return false; - if (const Expr *E = BS.Pointee->getDescriptor()->asExpr()) - return isa<AddrLabelExpr>(E); + if (isOpaquePointer()) + return isa_and_nonnull<AddrLabelExpr>(Opaque.Base.asExpr()); return false; } @@ -1063,7 +1074,7 @@ static bool toRValue(const Context &Ctx, QualType Ty, PtrView Ptr, APValue &R) { Ty = AT->getValueType(); // Invalid pointers. - if (Ptr.isDummy() || !Ptr.isLive() || Ptr.isPastEnd()) + if (!Ptr.isLive() || Ptr.isPastEnd()) return false; // Primitives should never end up here. @@ -1261,7 +1272,7 @@ const VarDecl *Pointer::getRootVarDecl() const { if (isBlockPointer()) return getDeclDesc()->asVarDecl(); if (isOpaquePointer()) - return dyn_cast<VarDecl>(Opaque.Base); + return Opaque.getBaseDecl(); return nullptr; } @@ -1270,6 +1281,8 @@ const Expr *Pointer::getRootExpr() const { return getDeclDesc()->asExpr(); if (isStringPointer()) return Str.getLiteral(); + if (isOpaquePointer()) + return Opaque.getBaseExpr(); return nullptr; } @@ -1465,7 +1478,7 @@ bool OpaquePointer::isUnknownSizeArray() const { // base to see if this array is a flexible array member _and_ has actually // been initialized by data we know the size of. if (isa<IncompleteArrayType>(FieldType)) { - const VarDecl *Base = cast<VarDecl>(this->Base); + const VarDecl *Base = this->Base.asVarDecl(); if (!Base || !Base->getType()->isRecordType() || !Base->hasInit()) Result = true; else diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 3bf6a363d6afd..44548268bda5e 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -42,7 +42,6 @@ struct PtrView { bool isZero() const { return !Pointee; } bool isLive() const { return Pointee && !Pointee->isDead(); } - bool isDummy() const { return Pointee && Pointee->isDummy(); } bool isActive() const { return isRoot() || getInlineDesc()->IsActive; } bool isArrayRoot() const { return inArray() && Offset == Base; } bool isElementPastEnd() const { return Offset == PastEndMark; } @@ -430,13 +429,15 @@ struct PointerPathEntry { }; struct OpaquePointer { - const ValueDecl *Base = nullptr; + DeclOrExpr Base; // FieldType and IsOnePastEnd/IsConstexprUnknown bits. llvm::PointerIntPair<const Type *, 2, unsigned> FieldType = {}; const PointerPathEntry *Path = nullptr; unsigned PathLength = 0; ArrayRef<PointerPathEntry> path() const { return ArrayRef(Path, PathLength); } + const VarDecl *getBaseDecl() const { return Base.asVarDecl(); } + const Expr *getBaseExpr() const { return Base.asExpr(); } OpaquePointer withFieldType(const Type *FieldTy, @@ -467,14 +468,14 @@ struct OpaquePointer { } QualType getObjectType() const { - QualType T = Base->getType(); + QualType T = Base.getType(); if (T->isPointerOrReferenceType()) return T->getPointeeType(); return T; } QualType getFieldType() const { - if (FieldType.getPointer()->isPointerOrReferenceType()) + if (FieldType.getPointer()->isPointerOrReferenceType() && Base.isDecl()) return FieldType.getPointer()->getPointeeType(); return QualType(FieldType.getPointer(), 0); } @@ -549,11 +550,11 @@ class Pointer { : Offset(0), StorageKind(Storage::String), Str{Base, Id} {} Pointer(StringPointer Str, uint64_t Offset = 0) : Offset(Offset), StorageKind(Storage::String), Str(Str) {} - Pointer(const ValueDecl *Base, bool ConstexprUnknown = false) + + Pointer(DeclOrExpr DOE, bool ConstexprUnknown = false) : Offset(0), StorageKind(Storage::Opaque) { - Opaque.Base = Base; - Opaque.FieldType = {Base->getType().getTypePtr(), - ConstexprUnknown ? 2u : 0u}; + Opaque.Base = DOE; + Opaque.FieldType = {DOE.getType().getTypePtr(), ConstexprUnknown ? 2u : 0u}; Opaque.Path = nullptr; Opaque.PathLength = 0; } @@ -937,8 +938,11 @@ class Pointer { return Fn.Func->getDecl()->isWeak(); } - if (isOpaquePointer()) - return Opaque.Base->isWeak(); + if (isOpaquePointer()) { + if (const VarDecl *BaseDecl = Opaque.getBaseDecl()) + return BaseDecl->isWeak(); + return false; + } if (!isBlockPointer()) return false; @@ -959,9 +963,7 @@ class Pointer { bool isDummy() const { if (isOpaquePointer()) return true; - if (!isBlockPointer()) - return false; - return view().isDummy(); + return false; } /// Checks if an object or a subfield is mutable. @@ -1308,9 +1310,7 @@ class Pointer { bool pointsToLabel() const; /// Returns the AddrLabelExpr the Pointer points to, if any. const AddrLabelExpr *getPointedToLabel() const { - if (const Descriptor *Desc = getDeclDesc()) - return dyn_cast_if_present<AddrLabelExpr>(Desc->asExpr()); - return nullptr; + return dyn_cast_if_present<AddrLabelExpr>(getRootExpr()); } /// Prints the pointer. @@ -1397,7 +1397,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { } else if (P.isBlockPointer() && P.isArrayRoot()) OS << " arrayroot"; - if (P.isBlockPointer() && P.block() && P.block()->isDummy()) + if (P.isDummy()) OS << " dummy"; if (!P.isLive()) OS << " dead"; diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 137ca8c03fad5..b9a8476d41659 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -60,60 +60,6 @@ UnsignedOrNone Program::getOrCreateGlobal(const ValueDecl *VD, return std::nullopt; } -unsigned Program::getOrCreateDummy(DeclOrExpr D, bool IsConstexprUnknown) { - assert(D); - - if (const auto *VD = D.asVarDecl()) - D = VD->getFirstDecl(); - - // Dedup blocks since they are immutable and pointers cannot be compared. - if (auto It = DummyVariables.find(D.getOpaqueValue()); - It != DummyVariables.end()) - return It->second; - - QualType QT; - bool IsWeak = false; - if (const auto *E = D.asExpr()) { - QT = E->getType(); - } else { - const auto *VD = D.asValueDecl(); - IsWeak = VD->isWeak(); - QT = VD->getType(); - - if (QT->isReferenceType()) - QT = QT->getPointeeType(); - } - - assert(!QT.isNull()); - - Descriptor *Desc; - if (OptPrimType T = Ctx.classify(QT)) - Desc = createDescriptor(D, *T, /*SourceTy=*/nullptr, - /*IsConst=*/QT.isConstQualified()); - else - Desc = createDescriptor(D, QT.getTypePtr(), - /*IsConst=*/QT.isConstQualified()); - if (!Desc) - Desc = allocateDescriptor(D); - - Desc->IsConstexprUnknown = IsConstexprUnknown; - - assert(Desc); - - // Allocate a block for storage. - unsigned I = Globals.size(); - - auto *G = new (Allocator, Desc->getAllocSize()) - Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*MDSize=*/0u, - /*IsStatic=*/true, /*IsExtern=*/false, IsWeak, /*IsDummy=*/true); - G->block()->invokeCtor(); - assert(G->block()->isDummy()); - - Globals.push_back(G); - DummyVariables[D.getOpaqueValue()] = I; - return I; -} - UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init, bool IsConstexprUnknown) { bool IsStatic, IsExtern; @@ -142,15 +88,6 @@ UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init, for (const Decl *Redecl = VD->getPreviousDecl(); Redecl; Redecl = Redecl->getPreviousDecl()) { - // If this redecl was registered as a dummy variable, it is now a proper - // global variable and points to the block we just created. - if (auto DummyIt = DummyVariables.find(Redecl); - DummyIt != DummyVariables.end()) { - Global *Dummy = Globals[DummyIt->second]; - Dummy->block()->movePointersTo(NewGlobal->block()); - Globals[DummyIt->second] = NewGlobal; - DummyVariables.erase(DummyIt); - } // If the redeclaration hasn't been registered yet at all, we just set its // global index to Idx. If it has been registered yet, it might have // pointers pointing to it and we need to transfer those pointers to the new diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index 6c9a21728775d..ccd6720cce738 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -81,9 +81,6 @@ class Program final { UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD, const Expr *Init = nullptr); - /// Returns or creates a dummy value for unknown declarations. - unsigned getOrCreateDummy(DeclOrExpr D, bool IsConstexprUnknown = false); - /// Creates a global and returns its index. UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init, bool IsConstexprUnknown = false); @@ -211,9 +208,6 @@ class Program final { /// Mapping from decls to record metadata. llvm::DenseMap<const RecordDecl *, Record *> Records; - /// Dummy parameter to generate pointers from. - llvm::DenseMap<const void *, unsigned> DummyVariables; - /// Creates a new descriptor. template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) { return new (Allocator) Descriptor(std::forward<Ts>(Args)...); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
