https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/220917
>From b07934b4674104fc5b62ab3d2dc9f50bd58e5cfa 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] 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 | 53 +++-- 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, 443 insertions(+), 120 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..de09d019387cc 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()) + 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(); @@ -2124,7 +2146,8 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, return true; } - if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType()) + if ((!PtrA.isReadablePointerType() && !PtrA.isOpaquePointer()) || + (!PtrB.isReadablePointerType() && !PtrB.isOpaquePointer())) return false; bool IsWide = @@ -2423,14 +2446,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()); } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
