diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index cbf4f1397eb1f..7add83f896244 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2232,20 +2232,20 @@ class CXXMethodDecl : public FunctionDecl { overridden_method_range overridden_methods() const; - /// Returns the parent of this method declaration, which + /// Return the parent of this method declaration, which /// is the class in which this method is defined. const CXXRecordDecl *getParent() const { return cast(FunctionDecl::getParent()); } - /// Returns the parent of this method declaration, which + /// Return the parent of this method declaration, which /// is the class in which this method is defined. CXXRecordDecl *getParent() { return const_cast( cast(FunctionDecl::getParent())); } - /// Returns the type of the \c this pointer. + /// Return the type of the \c this pointer. /// /// Should only be called for instance (i.e., non-static) methods. Note /// that for the call operator of a lambda closure type, this returns the @@ -2253,9 +2253,17 @@ class CXXMethodDecl : public FunctionDecl { /// 'this' type. QualType getThisType() const; + /// Return the type of the object pointed by \c this. + /// + /// See getThisType() for usage restriction. + QualType getThisObjectType() const; + static QualType getThisType(const FunctionProtoType *FPT, const CXXRecordDecl *Decl); + static QualType getThisObjectType(const FunctionProtoType *FPT, + const CXXRecordDecl *Decl); + Qualifiers getMethodQualifiers() const { return getType()->getAs()->getMethodQuals(); } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 28ed6cdfde14f..faddbc6d96758 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -185,15 +185,20 @@ class CXXMemberCallExpr final : public CallExpr { static CXXMemberCallExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, EmptyShell Empty); - /// Retrieves the implicit object argument for the member call. + /// Retrieve the implicit object argument for the member call. /// /// For example, in "x.f(5)", this returns the sub-expression "x". Expr *getImplicitObjectArgument() const; - /// Retrieves the declaration of the called method. + /// Retrieve the type of the object argument. + /// + /// Note that this always returns a non-pointer type. + QualType getObjectType() const; + + /// Retrieve the declaration of the called method. CXXMethodDecl *getMethodDecl() const; - /// Retrieves the CXXRecordDecl for the underlying type of + /// Retrieve the CXXRecordDecl for the underlying type of /// the implicit object argument. /// /// Note that this is may not be the same declaration as that of the class diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 857ac19e6b14c..59710a55498f2 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2253,12 +2253,23 @@ CXXMethodDecl::overridden_methods() const { return getASTContext().overridden_methods(this); } +static QualType getThisObjectType(ASTContext &C, const FunctionProtoType *FPT, + const CXXRecordDecl *Decl) { + QualType ClassTy = C.getTypeDeclType(Decl); + return C.getQualifiedType(ClassTy, FPT->getMethodQuals()); +} + QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT, const CXXRecordDecl *Decl) { ASTContext &C = Decl->getASTContext(); - QualType ClassTy = C.getTypeDeclType(Decl); - ClassTy = C.getQualifiedType(ClassTy, FPT->getMethodQuals()); - return C.getPointerType(ClassTy); + QualType ObjectTy = ::getThisObjectType(C, FPT, Decl); + return C.getPointerType(ObjectTy); +} + +QualType CXXMethodDecl::getThisObjectType(const FunctionProtoType *FPT, + const CXXRecordDecl *Decl) { + ASTContext &C = Decl->getASTContext(); + return ::getThisObjectType(C, FPT, Decl); } QualType CXXMethodDecl::getThisType() const { @@ -2273,6 +2284,14 @@ QualType CXXMethodDecl::getThisType() const { getParent()); } +QualType CXXMethodDecl::getThisObjectType() const { + // Ditto getThisType. + assert(isInstance() && "No 'this' for static methods!"); + + return CXXMethodDecl::getThisObjectType(getType()->getAs(), + getParent()); +} + bool CXXMethodDecl::hasInlineBody() const { // If this function is a template instantiation, look at the template from // which it was instantiated. diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index b30f785ba8f54..c5f86a4cc12b4 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -651,6 +651,13 @@ Expr *CXXMemberCallExpr::getImplicitObjectArgument() const { return nullptr; } +QualType CXXMemberCallExpr::getObjectType() const { + QualType Ty = getImplicitObjectArgument()->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + return Ty; +} + CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const { if (const auto *MemExpr = dyn_cast(getCallee()->IgnoreParens())) return cast(MemExpr->getMemberDecl()); diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index 511bcd00d4277..3a9c3b3474394 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -378,7 +378,7 @@ class CGCXXABI { virtual void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, bool Delegating, - Address This) = 0; + Address This, QualType ThisTy) = 0; /// Emits the VTable definitions required for the given record type. virtual void emitVTableDefinitions(CodeGenVTables &CGVT, @@ -421,11 +421,15 @@ class CGCXXABI { llvm::Type *Ty, SourceLocation Loc) = 0; + using DeleteOrMemberCallExpr = + llvm::PointerUnion; + /// Emit the ABI-specific virtual destructor call. - virtual llvm::Value * - EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, - CXXDtorType DtorType, Address This, - const CXXMemberCallExpr *CE) = 0; + virtual llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF, + const CXXDestructorDecl *Dtor, + CXXDtorType DtorType, + Address This, + DeleteOrMemberCallExpr E) = 0; virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 890bf2ba7724f..9c18d69fb6c21 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3505,7 +3505,7 @@ struct DestroyUnpassedArg final : EHScopeStack::Cleanup { const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor(); assert(!Dtor->isTrivial()); CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*for vbase*/ false, - /*Delegating=*/false, Addr); + /*Delegating=*/false, Addr, Ty); } else { CGF.callCStructDestructor(CGF.MakeAddrLValue(Addr, Ty)); } diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 9a9dd88810ed3..c8bb63c5c4b1f 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -491,12 +491,15 @@ namespace { cast(CGF.CurCodeDecl)->getParent(); const CXXDestructorDecl *D = BaseClass->getDestructor(); + // We are already inside a destructor, so presumably the object being + // destroyed should have the expected type. + QualType ThisTy = D->getThisObjectType(); Address Addr = CGF.GetAddressOfDirectBaseInCompleteClass(CGF.LoadCXXThisAddress(), DerivedClass, BaseClass, BaseIsVirtual); CGF.EmitCXXDestructorCall(D, Dtor_Base, BaseIsVirtual, - /*Delegating=*/false, Addr); + /*Delegating=*/false, Addr, ThisTy); } }; @@ -1440,9 +1443,11 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { if (DtorType == Dtor_Deleting) { RunCleanupsScope DtorEpilogue(*this); EnterDtorCleanups(Dtor, Dtor_Deleting); - if (HaveInsertPoint()) + if (HaveInsertPoint()) { + QualType ThisTy = Dtor->getThisObjectType(); EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, - /*Delegating=*/false, LoadCXXThisAddress()); + /*Delegating=*/false, LoadCXXThisAddress(), ThisTy); + } return; } @@ -1473,8 +1478,9 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { EnterDtorCleanups(Dtor, Dtor_Complete); if (!isTryBody) { + QualType ThisTy = Dtor->getThisObjectType(); EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false, - /*Delegating=*/false, LoadCXXThisAddress()); + /*Delegating=*/false, LoadCXXThisAddress(), ThisTy); break; } @@ -2013,7 +2019,7 @@ void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF, const CXXDestructorDecl *dtor = record->getDestructor(); assert(!dtor->isTrivial()); CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*for vbase*/ false, - /*Delegating=*/false, addr); + /*Delegating=*/false, addr, type); } void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, @@ -2363,8 +2369,11 @@ namespace { : Dtor(D), Addr(Addr), Type(Type) {} void Emit(CodeGenFunction &CGF, Flags flags) override { + // We are calling the destructor from within the constructor. + // Therefore, "this" should have the expected type. + QualType ThisTy = Dtor->getThisObjectType(); CGF.EmitCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false, - /*Delegating=*/true, Addr); + /*Delegating=*/true, Addr, ThisTy); } }; } // end anonymous namespace @@ -2402,31 +2411,32 @@ CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, - bool Delegating, - Address This) { + bool Delegating, Address This, + QualType ThisTy) { CGM.getCXXABI().EmitDestructorCall(*this, DD, Type, ForVirtualBase, - Delegating, This); + Delegating, This, ThisTy); } namespace { struct CallLocalDtor final : EHScopeStack::Cleanup { const CXXDestructorDecl *Dtor; Address Addr; + QualType Ty; - CallLocalDtor(const CXXDestructorDecl *D, Address Addr) - : Dtor(D), Addr(Addr) {} + CallLocalDtor(const CXXDestructorDecl *D, Address Addr, QualType Ty) + : Dtor(D), Addr(Addr), Ty(Ty) {} void Emit(CodeGenFunction &CGF, Flags flags) override { CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, - /*Delegating=*/false, Addr); + /*Delegating=*/false, Addr, Ty); } }; } // end anonymous namespace void CodeGenFunction::PushDestructorCleanup(const CXXDestructorDecl *D, - Address Addr) { - EHStack.pushCleanup(NormalAndEHCleanup, D, Addr); + QualType T, Address Addr) { + EHStack.pushCleanup(NormalAndEHCleanup, D, Addr, T); } void CodeGenFunction::PushDestructorCleanup(QualType T, Address Addr) { @@ -2436,7 +2446,7 @@ void CodeGenFunction::PushDestructorCleanup(QualType T, Address Addr) { const CXXDestructorDecl *D = ClassDecl->getDestructor(); assert(D && D->isUsed() && "destructor not marked as used!"); - PushDestructorCleanup(D, Addr); + PushDestructorCleanup(D, T, Addr); } void CodeGenFunction::InitializeVTablePointer(const VPtr &Vptr) { diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 19a9e75cc5ac9..6ad43cefc4d29 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -480,11 +480,12 @@ namespace { template struct DestroyNRVOVariable : EHScopeStack::Cleanup { - DestroyNRVOVariable(Address addr, llvm::Value *NRVOFlag) - : NRVOFlag(NRVOFlag), Loc(addr) {} + DestroyNRVOVariable(Address addr, QualType type, llvm::Value *NRVOFlag) + : NRVOFlag(NRVOFlag), Loc(addr), Ty(type) {} llvm::Value *NRVOFlag; Address Loc; + QualType Ty; void Emit(CodeGenFunction &CGF, Flags flags) override { // Along the exceptions path we always execute the dtor. @@ -511,26 +512,24 @@ namespace { struct DestroyNRVOVariableCXX final : DestroyNRVOVariable { - DestroyNRVOVariableCXX(Address addr, const CXXDestructorDecl *Dtor, - llvm::Value *NRVOFlag) - : DestroyNRVOVariable(addr, NRVOFlag), - Dtor(Dtor) {} + DestroyNRVOVariableCXX(Address addr, QualType type, + const CXXDestructorDecl *Dtor, llvm::Value *NRVOFlag) + : DestroyNRVOVariable(addr, type, NRVOFlag), + Dtor(Dtor) {} const CXXDestructorDecl *Dtor; void emitDestructorCall(CodeGenFunction &CGF) { CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, - /*Delegating=*/false, Loc); + /*Delegating=*/false, Loc, Ty); } }; struct DestroyNRVOVariableC final : DestroyNRVOVariable { DestroyNRVOVariableC(Address addr, llvm::Value *NRVOFlag, QualType Ty) - : DestroyNRVOVariable(addr, NRVOFlag), Ty(Ty) {} - - QualType Ty; + : DestroyNRVOVariable(addr, Ty, NRVOFlag) {} void emitDestructorCall(CodeGenFunction &CGF) { CGF.destroyNonTrivialCStruct(CGF, Loc, Ty); @@ -1940,7 +1939,7 @@ void CodeGenFunction::emitAutoVarTypeCleanup( if (emission.NRVOFlag) { assert(!type->isArrayType()); CXXDestructorDecl *dtor = type->getAsCXXRecordDecl()->getDestructor(); - EHStack.pushCleanup(cleanupKind, addr, dtor, + EHStack.pushCleanup(cleanupKind, addr, type, dtor, emission.NRVOFlag); return; } diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 8ad229fc0c362..5476d13b7c461 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -10,12 +10,13 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenFunction.h" #include "CGCUDARuntime.h" #include "CGCXXABI.h" #include "CGDebugInfo.h" #include "CGObjCRuntime.h" +#include "CodeGenFunction.h" #include "ConstantEmitter.h" +#include "TargetInfo.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "llvm/IR/Intrinsics.h" @@ -90,12 +91,26 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( } RValue CodeGenFunction::EmitCXXDestructorCall( - GlobalDecl Dtor, const CGCallee &Callee, llvm::Value *This, + GlobalDecl Dtor, const CGCallee &Callee, llvm::Value *This, QualType ThisTy, llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *CE) { + const CXXMethodDecl *DtorDecl = cast(Dtor.getDecl()); + + assert(!ThisTy.isNull()); + assert(ThisTy->getAsCXXRecordDecl() == DtorDecl->getParent() && + "Pointer/Object mixup"); + + LangAS SrcAS = ThisTy.getAddressSpace(); + LangAS DstAS = DtorDecl->getMethodQualifiers().getAddressSpace(); + if (SrcAS != DstAS) { + QualType DstTy = DtorDecl->getThisType(); + llvm::Type *NewType = CGM.getTypes().ConvertType(DstTy); + This = getTargetHooks().performAddrSpaceCast(*this, This, SrcAS, DstAS, + NewType); + } + CallArgList Args; - commonEmitCXXMemberOrOperatorCall(*this, cast(Dtor.getDecl()), - This, ImplicitParam, ImplicitParamTy, CE, - Args, nullptr); + commonEmitCXXMemberOrOperatorCall(*this, DtorDecl, This, ImplicitParam, + ImplicitParamTy, CE, Args, nullptr); return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee, ReturnValueSlot(), Args); } @@ -345,7 +360,9 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( Callee = CGCallee::forDirect(CGM.GetAddrOfFunction(GD, Ty), GD); } - EmitCXXDestructorCall(GD, Callee, This.getPointer(), + QualType ThisTy = + IsArrow ? Base->getType()->getPointeeType() : Base->getType(); + EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, /*ImplicitParam=*/nullptr, /*ImplicitParamTy=*/QualType(), nullptr); } @@ -1883,7 +1900,7 @@ static void EmitObjectDelete(CodeGenFunction &CGF, CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, /*Delegating=*/false, - Ptr); + Ptr, ElementType); else if (auto Lifetime = ElementType.getObjCLifetime()) { switch (Lifetime) { case Qualifiers::OCL_None: diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 06ef2dff7e9f5..c3060d1fb3514 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -675,7 +675,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// PushDestructorCleanup - Push a cleanup to call the /// complete-object variant of the given destructor on the object at /// the given address. - void PushDestructorCleanup(const CXXDestructorDecl *Dtor, Address Addr); + void PushDestructorCleanup(const CXXDestructorDecl *Dtor, QualType T, + Address Addr); /// PopCleanupBlock - Will pop the cleanup entry on the stack and /// process all branch fixups. @@ -2554,8 +2555,8 @@ class CodeGenFunction : public CodeGenTypeCache { static Destroyer destroyCXXObject; void EmitCXXDestructorCall(const CXXDestructorDecl *D, CXXDtorType Type, - bool ForVirtualBase, bool Delegating, - Address This); + bool ForVirtualBase, bool Delegating, Address This, + QualType ThisTy); void EmitNewArrayInitializer(const CXXNewExpr *E, QualType elementType, llvm::Type *ElementTy, Address NewPtr, @@ -3677,9 +3678,9 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *E, CallArgList *RtlArgs); - RValue EmitCXXDestructorCall(GlobalDecl Dtor, - const CGCallee &Callee, - llvm::Value *This, llvm::Value *ImplicitParam, + RValue EmitCXXDestructorCall(GlobalDecl Dtor, const CGCallee &Callee, + llvm::Value *This, QualType ThisTy, + llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *E); RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E, ReturnValueSlot ReturnValue); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 7216e1daefd4c..826d98151d9b9 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -224,7 +224,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, - bool Delegating, Address This) override; + bool Delegating, Address This, + QualType ThisTy) override; void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) override; @@ -261,9 +262,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, - CXXDtorType DtorType, - Address This, - const CXXMemberCallExpr *CE) override; + CXXDtorType DtorType, Address This, + DeleteOrMemberCallExpr E) override; void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override; @@ -1127,7 +1127,7 @@ void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, // FIXME: Provide a source location here even though there's no // CXXMemberCallExpr for dtor call. CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting; - EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr); + EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE); if (UseGlobalDelete) CGF.PopCleanupBlock(); @@ -1538,7 +1538,8 @@ CGCXXABI::AddedStructorArgs ItaniumCXXABI::addImplicitConstructorArgs( void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, - bool Delegating, Address This) { + bool Delegating, Address This, + QualType ThisTy) { GlobalDecl GD(DD, Type); llvm::Value *VTT = CGF.GetVTTParameter(GD, ForVirtualBase, Delegating); QualType VTTTy = getContext().getPointerType(getContext().VoidPtrTy); @@ -1550,7 +1551,8 @@ void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF, else Callee = CGCallee::forDirect(CGM.getAddrOfCXXStructor(GD), GD); - CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), VTT, VTTTy, nullptr); + CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, VTT, VTTTy, + nullptr); } void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, @@ -1738,7 +1740,10 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall( CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType, - Address This, const CXXMemberCallExpr *CE) { + Address This, DeleteOrMemberCallExpr E) { + auto *CE = E.dyn_cast(); + auto *D = E.dyn_cast(); + assert((CE != nullptr) ^ (D != nullptr)); assert(CE == nullptr || CE->arg_begin() == CE->arg_end()); assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete); @@ -1748,8 +1753,15 @@ llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall( llvm::FunctionType *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); CGCallee Callee = CGCallee::forVirtual(CE, GD, This, Ty); - CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), nullptr, QualType(), - nullptr); + QualType ThisTy; + if (CE) { + ThisTy = CE->getObjectType(); + } else { + ThisTy = D->getDestroyedType(); + } + + CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, nullptr, + QualType(), nullptr); return nullptr; } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index a91a949d024f8..ca06ad3f042bb 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -258,7 +258,8 @@ class MicrosoftCXXABI : public CGCXXABI { void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, - bool Delegating, Address This) override; + bool Delegating, Address This, + QualType ThisTy) override; void emitVTableTypeMetadata(const VPtrInfo &Info, const CXXRecordDecl *RD, llvm::GlobalVariable *VTable); @@ -296,9 +297,8 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, - CXXDtorType DtorType, - Address This, - const CXXMemberCallExpr *CE) override; + CXXDtorType DtorType, Address This, + DeleteOrMemberCallExpr E) override; void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD, CallArgList &CallArgs) override { @@ -844,8 +844,7 @@ void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, // CXXMemberCallExpr for dtor call. bool UseGlobalDelete = DE->isGlobalDelete(); CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting; - llvm::Value *MDThis = - EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr); + llvm::Value *MDThis = EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE); if (UseGlobalDelete) CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType); } @@ -1569,7 +1568,8 @@ CGCXXABI::AddedStructorArgs MicrosoftCXXABI::addImplicitConstructorArgs( void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, - bool Delegating, Address This) { + bool Delegating, Address This, + QualType ThisTy) { // Use the base destructor variant in place of the complete destructor variant // if the class has no virtual bases. This effectively implements some of the // -mconstructor-aliases optimization, but as part of the MS C++ ABI. @@ -1591,7 +1591,7 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, BaseDtorEndBB = EmitDtorCompleteObjectHandler(CGF); } - CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), + CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, /*ImplicitParam=*/nullptr, /*ImplicitParamTy=*/QualType(), nullptr); if (BaseDtorEndBB) { @@ -1900,7 +1900,10 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall( CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType, - Address This, const CXXMemberCallExpr *CE) { + Address This, DeleteOrMemberCallExpr E) { + auto *CE = E.dyn_cast(); + auto *D = E.dyn_cast(); + assert((CE != nullptr) ^ (D != nullptr)); assert(CE == nullptr || CE->arg_begin() == CE->arg_end()); assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete); @@ -1917,8 +1920,15 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall( llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()), DtorType == Dtor_Deleting); + QualType ThisTy; + if (CE) { + ThisTy = CE->getObjectType(); + } else { + ThisTy = D->getDestroyedType(); + } + This = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true); - RValue RV = CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), + RValue RV = CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, ImplicitParam, Context.IntTy, CE); return RV.getScalarVal(); } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index dd77fc55721fb..9a6385f283196 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8190,6 +8190,27 @@ void Sema::ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *MethodD) { CheckCXXDefaultArguments(Method); } +// Emit the given diagnostic for each non-address-space qualifier. +// Common part of CheckConstructorDeclarator and CheckDestructorDeclarator. +static void checkMethodTypeQualifiers(Sema &S, Declarator &D, unsigned DiagID) { + const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); + if (FTI.hasMethodTypeQualifiers() && !D.isInvalidType()) { + bool DiagOccured = false; + FTI.MethodQualifiers->forEachQualifier( + [DiagID, &S, &DiagOccured](DeclSpec::TQ, StringRef QualName, + SourceLocation SL) { + // This diagnostic should be emitted on any qualifier except an addr + // space qualifier. However, forEachQualifier currently doesn't visit + // addr space qualifiers, so there's no way to write this condition + // right now; we just diagnose on everything. + S.Diag(SL, DiagID) << QualName << SourceRange(SL); + DiagOccured = true; + }); + if (DiagOccured) + D.setInvalidType(); + } +} + /// CheckConstructorDeclarator - Called by ActOnDeclarator to check /// the well-formedness of the constructor declarator @p D with type @p /// R. If there are any errors in the declarator, this routine will @@ -8230,25 +8251,11 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R, D.setInvalidType(); } - DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); - if (FTI.hasMethodTypeQualifiers()) { - bool DiagOccured = false; - FTI.MethodQualifiers->forEachQualifier( - [&](DeclSpec::TQ TypeQual, StringRef QualName, SourceLocation SL) { - // This diagnostic should be emitted on any qualifier except an addr - // space qualifier. However, forEachQualifier currently doesn't visit - // addr space qualifiers, so there's no way to write this condition - // right now; we just diagnose on everything. - Diag(SL, diag::err_invalid_qualified_constructor) - << QualName << SourceRange(SL); - DiagOccured = true; - }); - if (DiagOccured) - D.setInvalidType(); - } + checkMethodTypeQualifiers(*this, D, diag::err_invalid_qualified_constructor); // C++0x [class.ctor]p4: // A constructor shall not be declared with a ref-qualifier. + DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); if (FTI.hasRefQualifier()) { Diag(FTI.getRefQualifierLoc(), diag::err_ref_qualifier_constructor) << FTI.RefQualifierIsLValueRef @@ -8423,18 +8430,11 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, } } - DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); - if (FTI.hasMethodTypeQualifiers() && !D.isInvalidType()) { - FTI.MethodQualifiers->forEachQualifier( - [&](DeclSpec::TQ TypeQual, StringRef QualName, SourceLocation SL) { - Diag(SL, diag::err_invalid_qualified_destructor) - << QualName << SourceRange(SL); - }); - D.setInvalidType(); - } + checkMethodTypeQualifiers(*this, D, diag::err_invalid_qualified_destructor); // C++0x [class.dtor]p2: // A destructor shall not be declared with a ref-qualifier. + DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); if (FTI.hasRefQualifier()) { Diag(FTI.getRefQualifierLoc(), diag::err_ref_qualifier_destructor) << FTI.RefQualifierIsLValueRef diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d8c4ea48ebce7..f632a4d3bd1a7 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5093,12 +5093,10 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType, QualType ClassType = S.Context.getTypeDeclType(ActingContext); // [class.dtor]p2: A destructor can be invoked for a const, volatile or // const volatile object. - Qualifiers Quals; + Qualifiers Quals = Method->getMethodQualifiers(); if (isa(Method)) { Quals.addConst(); Quals.addVolatile(); - } else { - Quals = Method->getMethodQualifiers(); } QualType ImplicitParamType = S.Context.getQualifiedType(ClassType, Quals); diff --git a/clang/test/CodeGenCXX/PR42665.cpp b/clang/test/CodeGenCXX/PR42665.cpp new file mode 100644 index 0000000000000..4a460cfa10788 --- /dev/null +++ b/clang/test/CodeGenCXX/PR42665.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -std=c++17 -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple %ms_abi_triple -emit-llvm -std=c++17 -O0 %s -o - | FileCheck %s + +// Minimal reproducer for PR42665. + +struct Foo { + Foo() = default; + virtual ~Foo() = default; +}; + +template +struct Pair { + Foo first; + Deleter second; +}; + +template +Pair(Foo, Deleter) -> Pair; + +template +void deleter(T& t) { t.~T(); } + +auto make_pair() { + return Pair{ Foo(), deleter }; +} + +void foobar() { + auto p = make_pair(); + auto& f = p.first; + auto& d = p.second; + d(f); // Invoke virtual destructor of Foo through d. +} // p's destructor is invoked. + +// Regexes are used to handle both kind of mangling. +// +// CHECK-LABEL: define linkonce_odr{{( dso_local)?}} void @{{.*deleter.*Foo.*}}(%struct.Foo* dereferenceable({{[0-9]+}}) +// CHECK-SAME: [[T:%.*]]) +// CHECK: [[T_ADDR:%.*]] = alloca %struct.Foo* +// CHECK: store %struct.Foo* [[T]], %struct.Foo** [[T_ADDR]] +// CHECK: [[R0:%.*]] = load %struct.Foo*, %struct.Foo** [[T_ADDR]] +// CHECK: [[R1:%.*]] = bitcast %struct.Foo* [[R0]] to [[TYPE:.*struct\.Foo.*]]*** +// CHECK: [[VTABLE:%.*]] = load [[TYPE]]**, [[TYPE]]*** [[R1]] +// CHECK: [[VFUN:%.*]] = getelementptr inbounds [[TYPE]]*, [[TYPE]]** [[VTABLE]], i64 0 +// CHECK: [[DTOR:%.*]] = load [[TYPE]]*, [[TYPE]]** [[VFUN]] +// CHECK: call {{(void|i8\*)}} [[DTOR]](%struct.Foo* [[R0]] +// +// CHECK-LABEL: define{{( dso_local)?}} void @{{.*foobar.*}}() +// CHECK: [[P:%.*]] = alloca %struct.Pair +// CHECK: [[F:%.*]] = alloca %struct.Foo* +// CHECK: [[D:%.*]] = alloca [[TYPE:void \(%struct.Foo\*\)]]** +// CHECK: call void @{{.*make_pair.*}}(%struct.Pair* sret [[P]]) +// CHECK: [[FIRST:%.*]] = getelementptr inbounds %struct.Pair, %struct.Pair* [[P]], i32 0, i32 0 +// CHECK: store %struct.Foo* [[FIRST]], %struct.Foo** [[F]] +// CHECK: [[SECOND:%.*]] = getelementptr inbounds %struct.Pair, %struct.Pair* [[P]], i32 0, i32 1 +// CHECK: store void (%struct.Foo*)** [[SECOND]], [[TYPE]]*** [[D]] +// CHECK: [[R0:%.*]] = load [[TYPE]]**, [[TYPE]]*** [[D]] +// CHECK: [[R1:%.*]] = load [[TYPE]]*, [[TYPE]]** [[R0]] +// CHECK: [[R2:%.*]] = load %struct.Foo*, %struct.Foo** [[F]] +// CHECK: call void [[R1]](%struct.Foo* dereferenceable({{[0-9]+}}) [[R2]]) +// CHECK: call void @{{.*Pair.*Foo.*}}(%struct.Pair* [[P]]) + diff --git a/clang/test/CodeGenOpenCLCXX/addrspace-ctor.cl b/clang/test/CodeGenOpenCLCXX/addrspace-ctor.cl deleted file mode 100644 index 42c2e6e9077a8..0000000000000 --- a/clang/test/CodeGenOpenCLCXX/addrspace-ctor.cl +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang_cc1 %s -triple spir-unknown-unknown -cl-std=c++ -emit-llvm -O0 -o - | FileCheck %s - -struct MyType { - MyType(int i) : i(i) {} - MyType(int i) __constant : i(i) {} - int i; -}; - -//CHECK: call void @_ZNU3AS26MyTypeC1Ei(%struct.MyType addrspace(2)* @const1, i32 1) -__constant MyType const1 = 1; -//CHECK: call void @_ZNU3AS26MyTypeC1Ei(%struct.MyType addrspace(2)* @const2, i32 2) -__constant MyType const2(2); -//CHECK: call void @_ZNU3AS46MyTypeC1Ei(%struct.MyType addrspace(4)* addrspacecast (%struct.MyType addrspace(1)* @glob to %struct.MyType addrspace(4)*), i32 1) -MyType glob(1); diff --git a/clang/test/CodeGenOpenCLCXX/addrspace-with-class.cl b/clang/test/CodeGenOpenCLCXX/addrspace-with-class.cl new file mode 100644 index 0000000000000..21ba1ca251d86 --- /dev/null +++ b/clang/test/CodeGenOpenCLCXX/addrspace-with-class.cl @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 %s -triple spir-unknown-unknown -cl-std=c++ -emit-llvm -O0 -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple spir-unknown-unknown -cl-std=c++ -emit-llvm -O0 -o - | FileCheck %s --check-prefix=CHECK-DEFINITIONS + +// This test ensures the proper address spaces and address space cast are used +// for constructors, member functions and destructors. +// See also atexit.cl and global_init.cl for other specific tests. + +// CHECK: %struct.MyType = type { i32 } +struct MyType { + MyType(int i) : i(i) {} + MyType(int i) __constant : i(i) {} + ~MyType() {} + ~MyType() __constant {} + int bar() { return i + 2; } + int bar() __constant { return i + 1; } + int i; +}; + +// CHECK: @const1 = addrspace(2) global %struct.MyType zeroinitializer +__constant MyType const1 = 1; +// CHECK: @const2 = addrspace(2) global %struct.MyType zeroinitializer +__constant MyType const2(2); +// CHECK: @glob = addrspace(1) global %struct.MyType zeroinitializer +MyType glob(1); + +// CHECK: call void @_ZNU3AS26MyTypeC1Ei(%struct.MyType addrspace(2)* @const1, i32 1) +// CHECK: call void @_ZNU3AS26MyTypeC1Ei(%struct.MyType addrspace(2)* @const2, i32 2) +// CHECK: call void @_ZNU3AS46MyTypeC1Ei(%struct.MyType addrspace(4)* addrspacecast (%struct.MyType addrspace(1)* @glob to %struct.MyType addrspace(4)*), i32 1) + +// CHECK-LABEL: define spir_kernel void @fooGlobal() +kernel void fooGlobal() { + // CHECK: call i32 @_ZNU3AS46MyType3barEv(%struct.MyType addrspace(4)* addrspacecast (%struct.MyType addrspace(1)* @glob to %struct.MyType addrspace(4)*)) + glob.bar(); + // CHECK: call i32 @_ZNU3AS26MyType3barEv(%struct.MyType addrspace(2)* @const1) + const1.bar(); + // CHECK: call void @_ZNU3AS26MyTypeD1Ev(%struct.MyType addrspace(2)* @const1) + const1.~MyType(); +} + +// CHECK-LABEL: define spir_kernel void @fooLocal() +kernel void fooLocal() { + // CHECK: [[VAR:%.*]] = alloca %struct.MyType + // CHECK: [[REG:%.*]] = addrspacecast %struct.MyType* [[VAR]] to %struct.MyType addrspace(4)* + // CHECK: call void @_ZNU3AS46MyTypeC1Ei(%struct.MyType addrspace(4)* [[REG]], i32 3) + MyType myLocal(3); + // CHECK: [[REG:%.*]] = addrspacecast %struct.MyType* [[VAR]] to %struct.MyType addrspace(4)* + // CHECK: call i32 @_ZNU3AS46MyType3barEv(%struct.MyType addrspace(4)* [[REG]]) + myLocal.bar(); + // CHECK: [[REG:%.*]] = addrspacecast %struct.MyType* [[VAR]] to %struct.MyType addrspace(4)* + // CHECK: call void @_ZNU3AS46MyTypeD1Ev(%struct.MyType addrspace(4)* [[REG]]) +} + +// Ensure all members are defined for all the required address spaces. +// CHECK-DEFINITIONS-DAG: define linkonce_odr void @_ZNU3AS26MyTypeC1Ei(%struct.MyType addrspace(2)* %this, i32 %i) +// CHECK-DEFINITIONS-DAG: define linkonce_odr void @_ZNU3AS46MyTypeC1Ei(%struct.MyType addrspace(4)* %this, i32 %i) +// CHECK-DEFINITIONS-DAG: define linkonce_odr void @_ZNU3AS26MyTypeD1Ev(%struct.MyType addrspace(2)* %this) +// CHECK-DEFINITIONS-DAG: define linkonce_odr void @_ZNU3AS46MyTypeD1Ev(%struct.MyType addrspace(4)* %this) +// CHECK-DEFINITIONS-DAG: define linkonce_odr i32 @_ZNU3AS26MyType3barEv(%struct.MyType addrspace(2)* %this) +// CHECK-DEFINITIONS-DAG: define linkonce_odr i32 @_ZNU3AS46MyType3barEv(%struct.MyType addrspace(4)* %this)