Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -1186,12 +1186,14 @@ Clang provides support for :doc:`automated reference counting ` in Objective-C, which eliminates the need -for manual ``retain``/``release``/``autorelease`` message sends. There are two +for manual ``retain``/``release``/``autorelease`` message sends. There are three feature macros associated with automatic reference counting: ``__has_feature(objc_arc)`` indicates the availability of automated reference counting in general, while ``__has_feature(objc_arc_weak)`` indicates that automated reference counting also includes support for ``__weak`` pointers to -Objective-C objects. +Objective-C objects. ``__has_feature(objc_arc_fields)`` indicates that C structs +are allowed to have fields that are pointers to Objective-C objects managed by +automatic reference counting. .. _objc-fixed-enum: Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -3506,6 +3506,9 @@ /// when needed. mutable bool LoadedFieldsFromExternalStorage : 1; + /// Basic properties of non-trivial C structs. + bool HasStrongObjCPointer : 1; + protected: RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, @@ -3565,6 +3568,31 @@ LoadedFieldsFromExternalStorage = val; } + /// Functions to query basic properties of non-trivial C structs. + bool isNonTrivialToPrimitiveDefaultInitialize() const { + return HasStrongObjCPointer; + } + + bool isNonTrivialToPrimitiveCopy() const { + return HasStrongObjCPointer; + } + + bool isNonTrivialToPrimitiveDestructiveMove() const { + return HasStrongObjCPointer; + } + + bool isNonTrivialToPrimitiveDestroy() const { + return HasStrongObjCPointer; + } + + bool hasStrongObjCPointer() const { + return HasStrongObjCPointer; + } + + void setHasStrongObjCPointer(bool val) { + HasStrongObjCPointer = val; + } + /// \brief Determines whether this declaration represents the /// injected class name. /// Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1084,11 +1084,55 @@ // true when Type is objc's weak and weak is enabled but ARC isn't. bool isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const; + /// Functions to query basic properties of non-trivial C struct types. + + /// Returns true if this type is a C struct that is non-trivial to + /// default-initialize or an array that contains such a struct. + bool hasNonTrivialToDefaultInitializeStruct() const; + + /// Returns true if this type is a C struct that is non-trivial to copy or an + /// array that contains such a struct. + bool hasNonTrivialToCopyStruct() const; + + /// Returns true if this type is a C struct that is non-trivial to + /// destructively move or an array that contains such a struct. + bool hasNonTrivialToDestructiveMoveStruct() const; + + /// Returns true if this type is a C struct that is non-trivial to destroy or + /// an array that contains such a struct. + bool hasNonTrivialToDestroyStruct() const; + + /// Returns true if this type is a C struct that has a __strong field or an + /// array that contains such a struct. + bool hasStructWithObjCStrongField() const; + + /// Returns true if this is a non-trivial type that would cause a struct + /// transitively containing this type to be non-trivial to default initialize. + bool nonTrivialToDefaultInitialize() const; + + /// Returns true if this is a non-trivial type that would cause a struct + /// transitively containing this type to be non-trivial to copy. + bool nonTrivialToCopy() const; + + /// Returns true if this is a non-trivial type that would cause a struct + /// transitively containing this type to be non-trivial to destructively + /// move. + bool nonTrivialToDestructiveMove() const; + + /// Returns true if this is a non-trivial type that would cause a struct + /// transitively containing this type to be non-trivial to destroy. + bool nonTrivialToDestroy() const; + + /// Returns true if this is an objc __strong pointer or a type containing a + /// struct that has an objc __strong pointer field. + bool hasObjCStrongPointer() const; + enum DestructionKind { DK_none, DK_cxx_destructor, DK_objc_strong_lifetime, - DK_objc_weak_lifetime + DK_objc_weak_lifetime, + DK_c_struct_strong_field }; /// Returns a nonzero value if objects of this type require Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5109,12 +5109,17 @@ "jump bypasses initialization of __strong variable">; def note_protected_by_objc_weak_init : Note< "jump bypasses initialization of __weak variable">; +def note_protected_by_non_trivial_c_struct_init : Note< + "jump bypasses initialization of variable of non-trivial C struct type">; def note_enters_block_captures_cxx_obj : Note< "jump enters lifetime of block which captures a destructible C++ object">; def note_enters_block_captures_strong : Note< "jump enters lifetime of block which strongly captures a variable">; def note_enters_block_captures_weak : Note< "jump enters lifetime of block which weakly captures a variable">; +def note_enters_block_captures_non_trivial_c_struct : Note< + "jump enters lifetime of block which captures a C struct that is non-trivial " + "to destroy">; def note_exits_cleanup : Note< "jump exits scope of variable with __attribute__((cleanup))">; @@ -5155,6 +5160,9 @@ "jump exits lifetime of block which strongly captures a variable">; def note_exits_block_captures_weak : Note< "jump exits lifetime of block which weakly captures a variable">; +def note_exits_block_captures_non_trivial_c_struct : Note< + "jump exits lifetime of block which captures a C struct that is non-trivial " + "to destroy">; def err_func_returning_qualified_void : ExtWarn< "function cannot return qualified void type %0">, @@ -7173,6 +7181,10 @@ "cannot pass object with interface type %1 by value to variadic " "%select{function|block|method|constructor}2; expected type from format " "string was %3">; +def err_cannot_pass_non_trivial_c_struct_to_vararg : Error< + "cannot pass non-trivial C object of type %0 by value to variadic " + "%select{function|block|method|constructor}1">; + def err_cannot_pass_objc_interface_to_vararg : Error< "cannot pass object with interface type %0 by value through variadic " Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -5776,6 +5776,12 @@ return true; } + // The block needs copy/destroy helpers if Ty is a C struct type that is + // non-trivial to destructly move or destroy. + if (Ty.hasNonTrivialToDestructiveMoveStruct() || + Ty.hasNonTrivialToDestroyStruct()) + return true; + if (!Ty->isObjCRetainableType()) return false; Qualifiers qs = Ty.getQualifiers(); Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3892,7 +3892,7 @@ : TagDecl(DK, TK, C, DC, IdLoc, Id, PrevDecl, StartLoc), HasFlexibleArrayMember(false), AnonymousStructOrUnion(false), HasObjectMember(false), HasVolatileMember(false), - LoadedFieldsFromExternalStorage(false) { + LoadedFieldsFromExternalStorage(false), HasStrongObjCPointer(false) { assert(classof(static_cast(this)) && "Invalid Kind!"); } Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2207,6 +2207,65 @@ getObjCLifetime() != Qualifiers::OCL_Weak; } +bool QualType::hasNonTrivialToDefaultInitializeStruct() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->isNonTrivialToPrimitiveDefaultInitialize(); + return false; +} + +bool QualType::hasNonTrivialToCopyStruct() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->isNonTrivialToPrimitiveCopy(); + return false; +} + +bool QualType::hasNonTrivialToDestructiveMoveStruct() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->isNonTrivialToPrimitiveDestructiveMove(); + return false; +} + +bool QualType::hasNonTrivialToDestroyStruct() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->isNonTrivialToPrimitiveDestroy(); + return false; +} + +bool QualType::hasStructWithObjCStrongField() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->hasStrongObjCPointer(); + return false; +} + +bool QualType::nonTrivialToDefaultInitialize() const { + return hasObjCStrongPointer(); +} + +bool QualType::nonTrivialToCopy() const { + return hasObjCStrongPointer(); +} + +bool QualType::nonTrivialToDestructiveMove() const { + return hasObjCStrongPointer(); +} + +bool QualType::nonTrivialToDestroy() const { + return hasObjCStrongPointer(); +} + +bool QualType::hasObjCStrongPointer() const { + if (hasStructWithObjCStrongField()) + return true; + + Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime(); + return Lifetime == Qualifiers::OCL_Strong; +} + bool Type::isLiteralType(const ASTContext &Ctx) const { if (isDependentType()) return false; @@ -3895,6 +3954,11 @@ return DK_objc_weak_lifetime; } + // See if this is a C struct that is non-trivial to destroy or an array that + // contains such a struct. + if (type.hasStructWithObjCStrongField()) + return DK_c_struct_strong_field; + /// Currently, the only destruction kind we recognize is C++ objects /// with non-trivial destructors. const CXXRecordDecl *record = Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -477,6 +477,12 @@ info.NeedsCopyDispose = true; info.HasCXXObject = true; + // So do C structs that require non-trivial copy construction or + // destruction. + } else if (variable->getType().hasNonTrivialToCopyStruct() || + variable->getType().hasNonTrivialToDestroyStruct()) { + info.NeedsCopyDispose = true; + // And so do types with destructors. } else if (CGM.getLangOpts().CPlusPlus) { if (const CXXRecordDecl *record = @@ -1513,6 +1519,7 @@ CXXRecord, // Copy or destroy ARCWeak, ARCStrong, + NonTrivialCStruct, BlockObject, // Assign or release None }; @@ -1548,6 +1555,9 @@ Flags |= BLOCK_FIELD_IS_WEAK; return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); } + if (T.hasNonTrivialToCopyStruct()) + return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, + BlockFieldFlags()); if (!T->isObjCRetainableType()) // For all other types, the memcpy is fine. return std::make_pair(BlockCaptureEntityKind::None, Flags); @@ -1677,6 +1687,13 @@ EmitSynthesizedCXXCopyCtor(dstField, srcField, CI.getCopyExpr()); } else if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCWeak) { EmitARCCopyWeak(dstField, srcField); + // If this is a C struct that requires non-trivial copy construction, emit a + // call to its copy constructor. + } else if (CopiedCapture.Kind == + BlockCaptureEntityKind::NonTrivialCStruct) { + QualType varType = CI.getVariable()->getType(); + callCStructCopyConstructor(dstField, srcField, varType, + varType.isVolatileQualified(), true); } else { llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src"); if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCStrong) { @@ -1749,6 +1766,10 @@ return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags()); } + if (T.hasNonTrivialToDestroyStruct()) + return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, + BlockFieldFlags()); + // Other types don't need to be destroy explicitly. if (!T->isObjCRetainableType()) return std::make_pair(BlockCaptureEntityKind::None, Flags); @@ -1853,6 +1874,13 @@ } else if (DestroyedCapture.Kind == BlockCaptureEntityKind::ARCStrong) { EmitARCDestroyStrong(srcField, ARCImpreciseLifetime); + // If this is a C struct that requires non-trivial destruction, emit a call + // to its destructor. + } else if (DestroyedCapture.Kind == + BlockCaptureEntityKind::NonTrivialCStruct) { + QualType varType = CI.getVariable()->getType(); + pushDestroy(varType.isDestructedType(), srcField, varType); + // Otherwise we call _Block_object_dispose. It wouldn't be too // hard to just emit this as a cleanup if we wanted to make sure // that things were done in reverse. @@ -2020,6 +2048,36 @@ id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); } }; + +/// Emits the copy/dispose helpers for a __block variable that is a non-trivial +/// C struct. +class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers { + QualType VarType; + +public: + NonTrivialCStructByrefHelpers(CharUnits alignment, QualType type) + : BlockByrefHelpers(alignment), VarType(type) {} + + void emitCopy(CodeGenFunction &CGF, Address destField, + Address srcField) override { + CGF.callCStructMoveConstructor(destField, srcField, VarType, + VarType.isVolatileQualified(), true); + } + + bool needsDispose() const override { + return VarType.hasNonTrivialToDestroyStruct(); + } + + void emitDispose(CodeGenFunction &CGF, Address field) override { + EHScopeStack::stable_iterator cleanupDepth = CGF.EHStack.stable_begin(); + CGF.pushDestroy(VarType.isDestructedType(), field, VarType); + CGF.PopCleanupBlocks(cleanupDepth); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const override { + id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); + } +}; } // end anonymous namespace static llvm::Constant * @@ -2205,6 +2263,13 @@ CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr)); } + // If type is a non-trivial C struct type that is non-trivial to + // destructly move or destroy, build the copy and dispose helpers. + if (type.hasNonTrivialToDestructiveMoveStruct() || + type.hasNonTrivialToDestroyStruct()) + return ::buildByrefHelpers( + CGM, byrefInfo, NonTrivialCStructByrefHelpers(valueAlignment, type)); + // Otherwise, if we don't have a retainable type, there's nothing to do. // that the runtime does extra copies. if (!type->isObjCRetainableType()) return nullptr; Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3532,6 +3532,16 @@ cast(E)->getCastKind() == CK_LValueToRValue) { LValue L = EmitLValue(cast(E)->getSubExpr()); assert(L.isSimple()); + + // If type is a C struct that is non-trivial to copy, emit a call to its + // copy constructor to build the argument in the temporary slot. + if (type.hasNonTrivialToCopyStruct()) { + AggValueSlot Slot = CreateAggTemp(E->getType(), "temp.lvalue"); + EmitAggExpr(E, Slot); + args.add(Slot.asRValue(), type); + return; + } + if (L.getAlignment() >= getContext().getTypeAlignInChars(type)) { args.add(L.asAggregateRValue(), type, /*NeedsCopy*/true); } else { Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -1232,6 +1232,15 @@ if (emission.IsByRef) emitByrefStructureInit(emission); + // Initialize the variable here if it doesn't have a initializer and it is a + // C struct that is non-trivial to initialize or an array containing such a + // struct. + if (!Init && type.hasNonTrivialToDefaultInitializeStruct()) { + defaultInitNonTrivialCStructVar(emission.getAllocatedAddress(), type, + type.isVolatileQualified()); + return; + } + if (isTrivialInitializer(Init)) return; @@ -1406,6 +1415,11 @@ case QualType::DK_objc_weak_lifetime: break; + + case QualType::DK_c_struct_strong_field: + destroyer = CodeGenFunction::destroyNonTrivialCStruct; + cleanupKind = getARCCleanupKind(); + break; } // If we haven't chosen a more specific destroyer, use the default. @@ -1467,6 +1481,8 @@ return destroyARCStrongPrecise; case QualType::DK_objc_weak_lifetime: return destroyARCWeak; + case QualType::DK_c_struct_strong_field: + return destroyNonTrivialCStruct; } llvm_unreachable("Unknown DestructionKind"); } @@ -1897,6 +1913,14 @@ setAddrOfLocalVar(&D, DeclPtr); + // If this arg is a type containing a C struct that is non-trivial to + // destruct, the callee is responsible for destructing the argument. + if (Ty.hasNonTrivialToDestroyStruct()) { + AutoVarEmission Emission(D); + Emission.Addr = GetAddrOfLocalVar(&D); + emitAutoVarTypeCleanup(Emission, Ty.isDestructedType()); + } + // Emit debug info for param declaration. if (CGDebugInfo *DI = getDebugInfo()) { if (CGM.getCodeGenOpts().getDebugInfo() >= Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -79,6 +79,7 @@ case QualType::DK_objc_strong_lifetime: case QualType::DK_objc_weak_lifetime: + case QualType::DK_c_struct_strong_field: // We don't care about releasing objects during process teardown. assert(!D.getTLSKind() && "should have rejected this"); return; Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -77,7 +77,9 @@ void EmitAggLoadOfLValue(const Expr *E); /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. - void EmitFinalDestCopy(QualType type, const LValue &src); + /// SrcIsRValue is true if source comes from an RValue. + void EmitFinalDestCopy(QualType type, const LValue &src, + bool SrcIsRValue = false); void EmitFinalDestCopy(QualType type, RValue src); void EmitCopy(QualType type, const AggValueSlot &dest, const AggValueSlot &src); @@ -245,6 +247,12 @@ /// directly into the return value slot. Otherwise, a final move /// will be performed. void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) { + // Push destructor if the result is ignored and the type is a C struct that + // is non-trivial to destroy. + QualType Ty = E->getType(); + if (Dest.isIgnored() && Ty.hasNonTrivialToDestroyStruct()) + CGF.pushDestroy(Ty.isDestructedType(), src.getAggregateAddress(), Ty); + if (shouldUseDestForReturnSlot()) { // Logically, Dest.getAddr() should equal Src.getAggregateAddr(). // The possibility of undef rvalues complicates that a lot, @@ -261,11 +269,12 @@ void AggExprEmitter::EmitFinalDestCopy(QualType type, RValue src) { assert(src.isAggregate() && "value must be aggregate value!"); LValue srcLV = CGF.MakeAddrLValue(src.getAggregateAddress(), type); - EmitFinalDestCopy(type, srcLV); + EmitFinalDestCopy(type, srcLV, true); } /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. -void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src) { +void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src, + bool SrcIsRValue) { // If Dest is ignored, then we're evaluating an aggregate expression // in a context that doesn't care about the result. Note that loads // from volatile l-values force the existence of a non-ignored @@ -273,10 +282,41 @@ if (Dest.isIgnored()) return; + // Copy non-trivial C structs here. + + // For simplicity, both the destination and the source are treated as being + // volatile if either is volatile. + bool IsVolatile = Dest.isVolatile() || src.isVolatile(); + + if (SrcIsRValue) { + if (type.hasNonTrivialToDestructiveMoveStruct()) { + if (Dest.isPotentiallyAliased()) + CGF.callCStructMoveAssignmentOperator(Dest.getAddress(), + src.getAddress(), type, + IsVolatile, true); + else + CGF.callCStructMoveConstructor(Dest.getAddress(), src.getAddress(), + type, IsVolatile, true); + return; + } + } else { + if (type.hasNonTrivialToCopyStruct()) { + if (Dest.isPotentiallyAliased()) + CGF.callCStructCopyAssignmentOperator(Dest.getAddress(), + src.getAddress(), type, + IsVolatile, true); + else + CGF.callCStructCopyConstructor(Dest.getAddress(), src.getAddress(), + type, IsVolatile, true); + return; + } + } + AggValueSlot srcAgg = AggValueSlot::forLValue(src, AggValueSlot::IsDestructed, needsGC(type), AggValueSlot::IsAliased); EmitCopy(type, Dest, srcAgg); + } /// Perform a copy from the source into the destination. Index: lib/CodeGen/CGNonTrivialStruct.cpp =================================================================== --- /dev/null +++ lib/CodeGen/CGNonTrivialStruct.cpp @@ -0,0 +1,579 @@ +//===--- CGNonTrivialStruct.cpp - Emit Special Functions for C Structs ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to generate various special functions for C +// structs. +// +//===----------------------------------------------------------------------===// + +#include "CodeGenFunction.h" +#include "CodeGenModule.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace clang; +using namespace CodeGen; + +enum FieldKind { + FK_Strong, // objc strong pointer. + FK_Struct, // non-trivial C struct. + FK_Array // array that has non-trivial elements. +}; + +/// Return an address with the specified offset from the passed address. +static Address getAddrWithOffset(Address Addr, unsigned Offset, + CodeGenFunction &CGF) { + if (!Addr.isValid() || Offset == 0) + return Addr; + Addr = CGF.Builder.CreateBitCast(Addr, CGF.CGM.Int8PtrTy); + Addr = CGF.Builder.CreateConstInBoundsGEP(Addr, Offset, CharUnits::One()); + return CGF.Builder.CreateBitCast(Addr, CGF.CGM.Int8PtrPtrTy); +} + +static const CGFunctionInfo &getFunctionInfo(CodeGenModule &CGM, bool IsBinary, + FunctionArgList &Args) { + ASTContext &Ctx = CGM.getContext(); + llvm::SmallVector Params; + QualType ParamTy = Ctx.getPointerType(Ctx.VoidPtrTy); + Params.push_back(ImplicitParamDecl::Create(Ctx, nullptr, SourceLocation(), + &Ctx.Idents.get("dst"), ParamTy, + ImplicitParamDecl::Other)); + + if (IsBinary) + Params.push_back(ImplicitParamDecl::Create(Ctx, nullptr, SourceLocation(), + &Ctx.Idents.get("src"), ParamTy, + ImplicitParamDecl::Other)); + + for (auto &P : Params) + Args.push_back(P); + + return CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args); +} + +// Get the kind of a C struct field. Returns an empty Optional if the field +// type is trivial. +template +static llvm::Optional getFieldKind(QualType QT, ASTContext &Ctx) { + if (!T::isInterestingType(QT)) + return Optional(); + + if (QT->getAs()) + return FK_Struct; + if (Ctx.getAsArrayType(QT)) + return FK_Array; + if (QT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong) + return FK_Strong; + + return Optional(); +} + +static std::string getVolatileOffsetStr(bool IsVolatile, CharUnits Offset) { + std::string S; + if (IsVolatile) + S = "v"; + S += llvm::to_string(Offset.getQuantity()); + return S; +} + +namespace { +// This is a helper class that encodes the information about the fields of a +// non-trivial C struct into the name of its special functions. The string has +// the following structure: +// +// ::= + +// ::= | | +// ::= | +// ::= "_AB" '_' '_' +// "_AE" +// ::= +// ::= "_s" ["_b"] ["_v"] + +template class FieldInfoToString { +public: + FieldInfoToString(QualType QT, bool IsVolatile, ASTContext &C) : Ctx(C) { + visitStruct(CharUnits::Zero(), QT, IsVolatile); + } + + void visitField(CharUnits Offset, QualType QT, bool IsVolatile) { + switch (*getFieldKind(QT, Ctx)) { + case FK_Struct: + visitStruct(Offset, QT, IsVolatile); + break; + case FK_Array: + visitArray(Offset, QT, IsVolatile); + break; + case FK_Strong: + Str += "_s"; + if (QT->isBlockPointerType()) + Str += "b"; + Str += getVolatileOffsetStr(IsVolatile, Offset); + break; + } + } + + void visitStruct(CharUnits Offset, QualType QT, bool IsVolatile) { + const RecordDecl *RD = QT->castAs()->getDecl(); + const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD); + + // Iterate over the fields of the struct. + size_t FieldNo = 0; + for (const FieldDecl *FD : RD->fields()) { + QualType FT = FD->getType(); + unsigned FOffsetVal = RL.getFieldOffset(FieldNo++) / Ctx.getCharWidth(); + if (getFieldKind(FT, Ctx)) { + CharUnits FOffset = Offset + CharUnits::fromQuantity(FOffsetVal); + visitField(FOffset, FT, FT.isVolatileQualified() || IsVolatile); + } + } + } + + void visitArray(CharUnits Offset, QualType QT, bool IsVolatile) { + const auto *AT = Ctx.getAsConstantArrayType(QT); + unsigned NumElts = Ctx.getConstantArrayElementCount(AT); + QualType EltTy = Ctx.getBaseElementType(AT); + unsigned EltSize = Ctx.getTypeSizeInChars(EltTy).getQuantity(); + Str += "_AB" + llvm::to_string(Offset.getQuantity()) + "_" + + llvm::to_string(EltSize) + "_" + llvm::to_string(NumElts); + visitField(Offset, EltTy, IsVolatile); + Str += "_AE"; + } + + operator std::string() const { return Str; } + +private: + ASTContext &Ctx; + std::string Str; +}; +} // namespace + +// This function creates the mangled name of a special function of a non-trivial +// C struct. Since there is no ODR in C, the function is mangled based on the +// struct contents and not the name. The mangled name has the following +// structure: +// +// ::= '_' +// ::= "__destructor_" | "__default_constructor_" | +// "__copy_constructor_" | "__move_constructor_" | +// "__copy_assignment_" | "__move_assignment_" +// ::= ["_" ] + +template +static std::string +getSpecialFuncName(QualType QT, bool IsVolatile, CharUnits DstAlignment, + CharUnits SrcAlignment, bool IsBinary, CodeGenModule &CGM) { + StringRef Prefix = T::getFuncPrefix(); + std::string S = FieldInfoToString(QT, IsVolatile, CGM.getContext()); + std::string A = llvm::to_string(DstAlignment.getQuantity()); + if (IsBinary) + A += "_" + llvm::to_string(SrcAlignment.getQuantity()); + return std::string(Prefix) + A + S; +} + +// Helper function to create a null constant. +static llvm::Constant *getNullForVariable(Address Addr) { + llvm::Type *Ty = Addr.getElementType(); + return llvm::ConstantPointerNull::get(cast(Ty)); +} + +namespace { + +template class IRGen { +public: + IRGen() + : CGF(nullptr), DstStart(Address::invalid()), + SrcStart(Address::invalid()) {} + + IRGen(CodeGenFunction &F) + : CGF(&F), DstStart(Address::invalid()), SrcStart(Address::invalid()) {} + + void setFunction(CodeGenFunction &F, FunctionArgList &Args, + CharUnits DstAlignment, CharUnits SrcAlignment) { + CGF = &F; + llvm::Value *D = CGF->Builder.CreateLoad(CGF->GetAddrOfLocalVar(Args[0])); + DstStart = Address(D, DstAlignment); + if (isBinary()) { + assert(CGF->CurFn->arg_size() == 2 && + "Binary functions expected to have two arguments"); + llvm::Value *S = CGF->Builder.CreateLoad(CGF->GetAddrOfLocalVar(Args[1])); + SrcStart = Address(S, SrcAlignment); + } + } + + void visitMostOuterStruct(QualType QT, bool IsVolatile) { + visitStruct(DstStart, SrcStart, QT, IsVolatile, false); + } + + void visitField(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile) { + switch (*getFieldKind(QT, CGF->getContext())) { + case FK_Struct: + visitStruct(DstAddr, SrcAddr, QT, IsVolatile); + break; + case FK_Array: + visitArray(DstAddr, SrcAddr, QT, IsVolatile); + break; + case FK_Strong: + if (IsVolatile) + QT = QT.withVolatile(); + T::emitIRStrong(DstAddr, SrcAddr, QT, *CGF); + break; + } + } + + void visitStruct(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, bool IsField = true) { + // Just call the special function if this is a struct field contained in + // another struct. + if (IsField) { + T::callSpecialFunction(DstAddr, SrcAddr, QT, IsVolatile, *CGF); + return; + } + + const RecordDecl *RD = QT->castAs()->getDecl(); + ASTContext &Ctx = CGF->getContext(); + const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD); + + // Iterate over the fields of the struct. + size_t FieldNo = 0; + for (const FieldDecl *FD : RD->fields()) { + unsigned FOffset = RL.getFieldOffset(FieldNo++) / Ctx.getCharWidth(); + Address FieldDstAddr = getAddrWithOffset(DstAddr, FOffset, *CGF); + Address FieldSrcAddr = getAddrWithOffset(SrcAddr, FOffset, *CGF); + QualType FT = FD->getType(); + if (getFieldKind(FT, CGF->getContext())) + visitField(FieldDstAddr, FieldSrcAddr, FT, + FT.isVolatileQualified() || IsVolatile); + } + } + + void visitArray(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile) { + // Compute the end address. + ASTContext &Ctx = CGF->getContext(); + QualType BaseEltQT; + Address TmpAddr = DstAddr; + llvm::Value *NumElts = + CGF->emitArrayLength(Ctx.getAsArrayType(QT), BaseEltQT, TmpAddr); + unsigned BaseEltSize = Ctx.getTypeSizeInChars(BaseEltQT).getQuantity(); + llvm::Value *BaseEltSizeVal = + llvm::ConstantInt::get(NumElts->getType(), BaseEltSize); + llvm::Value *SizeInBytes = + CGF->Builder.CreateNUWMul(BaseEltSizeVal, NumElts); + Address BC = CGF->Builder.CreateBitCast(DstAddr, CGF->CGM.Int8PtrTy); + llvm::Value *DstArrayEnd = + CGF->Builder.CreateInBoundsGEP(BC.getPointer(), SizeInBytes); + DstArrayEnd = CGF->Builder.CreateBitCast(DstArrayEnd, CGF->CGM.Int8PtrPtrTy, + "dstarray.end"); + llvm::BasicBlock *PreheaderBB = CGF->Builder.GetInsertBlock(); + + // Create the header block and insert the phi instructions. + llvm::BasicBlock *HeaderBB = CGF->createBasicBlock("loop.header"); + CGF->EmitBlock(HeaderBB); + llvm::Type *Ty = DstAddr.getPointer()->getType(); + llvm::PHINode *DstPHI = CGF->Builder.CreatePHI(Ty, 2, "dstaddr.cur"); + DstPHI->addIncoming(DstAddr.getPointer(), PreheaderBB); + llvm::PHINode *SrcPHI = nullptr; + if (SrcAddr.isValid()) { + SrcPHI = CGF->Builder.CreatePHI(Ty, 2, "srcaddr.cur"); + SrcPHI->addIncoming(SrcAddr.getPointer(), PreheaderBB); + } + + // Create the exit and loop body blocks. + llvm::BasicBlock *ExitBB = CGF->createBasicBlock("loop.exit"); + llvm::BasicBlock *LoopBB = CGF->createBasicBlock("loop.body"); + + // Emit the comparison and conditional branch instruction that jumps to + // either the exit or the loop body. + llvm::Value *Done = CGF->Builder.CreateICmpEQ(DstPHI, DstArrayEnd, "done"); + CGF->Builder.CreateCondBr(Done, ExitBB, LoopBB); + + // Visit the element of the array in the loop body. + CGF->EmitBlock(LoopBB); + QualType EltQT = Ctx.getAsArrayType(QT)->getElementType(); + CharUnits EltSize = Ctx.getTypeSizeInChars(EltQT); + Address Dst = + Address(DstPHI, DstAddr.getAlignment().alignmentAtOffset(EltSize)); + Address Src = Address::invalid(); + if (SrcAddr.isValid()) + Src = Address(SrcPHI, SrcAddr.getAlignment().alignmentAtOffset(EltSize)); + visitField(Dst, Src, EltQT, IsVolatile); + + // Instrs to update the destination and source addresses. + Address NextDst = getAddrWithOffset(Dst, EltSize.getQuantity(), *CGF); + Address NextSrc = getAddrWithOffset(Src, EltSize.getQuantity(), *CGF); + + // Update phi instructions. + LoopBB = CGF->Builder.GetInsertBlock(); + DstPHI->addIncoming(NextDst.getPointer(), LoopBB); + if (NextSrc.isValid()) + SrcPHI->addIncoming(NextSrc.getPointer(), LoopBB); + + // Insert an unconditional branch to the header block. + CGF->Builder.CreateBr(HeaderBB); + CGF->EmitBlock(ExitBB); + } + + static bool isBinary() { return T::isBinary(); } + + static const char *getFuncPrefix() { return T::getFuncPrefix(); } + + static bool isInterestingType(QualType QT) { + return T::isInterestingType(QT); + } + +private: + CodeGenFunction *CGF; + Address DstStart, SrcStart; +}; + +template struct SpecialFunc { + // Emit IR for objc __strong pointers. + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) {} + + // This function returns true if the subclass is used to generate special + // functions that need two arguments (source and destination arguments for + // copy/move constructors/assignment operators). + static bool isBinary() { return IsBinary; } +}; + +// Various subclasses of SpecialFunc that are used to customize +// the IR IRGen generates. +struct Destructor : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + CGF.destroyARCStrongImprecise(CGF, DstAddr, QT); + } + static const char *getFuncPrefix() { return "__destructor_"; } + + static bool isInterestingType(QualType T) { return T.nonTrivialToDestroy(); } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructDestructor(DstAddr, QT, IsVolatile); + } +}; + +struct DefaultConstructor : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + CGF.EmitNullInitialization(DstAddr, QT); + } + static const char *getFuncPrefix() { return "__default_constructor_"; } + + static bool isInterestingType(QualType T) { + return T.nonTrivialToDefaultInitialize(); + } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructDefaultConstructor(DstAddr, QT, IsVolatile); + } +}; + +struct CopyConstructor : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + llvm::Value *SrcVal = CGF.EmitLoadOfScalar( + SrcAddr, QT.isVolatileQualified(), QT, SourceLocation()); + llvm::Value *Val = CGF.EmitARCRetain(QT, SrcVal); + CGF.EmitStoreOfScalar(Val, CGF.MakeAddrLValue(DstAddr, QT), true); + } + static const char *getFuncPrefix() { return "__copy_constructor_"; } + + static bool isInterestingType(QualType T) { return T.nonTrivialToCopy(); } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructCopyConstructor(DstAddr, SrcAddr, QT, IsVolatile, false); + } +}; + +struct MoveConstructor : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + LValue SrcLV = CGF.MakeAddrLValue(SrcAddr, QT); + llvm::Value *SrcVal = + CGF.EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal(); + CGF.EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV); + CGF.EmitStoreOfScalar(SrcVal, CGF.MakeAddrLValue(DstAddr, QT), + /* isInitialization */ true); + } + static const char *getFuncPrefix() { return "__move_constructor_"; } + + static bool isInterestingType(QualType T) { + return T.nonTrivialToDestructiveMove(); + } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructMoveConstructor(DstAddr, SrcAddr, QT, IsVolatile, false); + } +}; + +struct CopyAssignment : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + llvm::Value *SrcVal = CGF.EmitLoadOfScalar( + SrcAddr, QT.isVolatileQualified(), QT, SourceLocation()); + CGF.EmitARCStoreStrong(CGF.MakeAddrLValue(DstAddr, QT), SrcVal, false); + } + static const char *getFuncPrefix() { return "__copy_assignment_"; } + + static bool isInterestingType(QualType T) { return T.nonTrivialToCopy(); } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructCopyAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile, + false); + } +}; + +struct MoveAssignment : SpecialFunc { + static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT, + CodeGenFunction &CGF) { + LValue SrcLV = CGF.MakeAddrLValue(SrcAddr, QT); + llvm::Value *SrcVal = + CGF.EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal(); + CGF.EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV); + LValue DstLV = CGF.MakeAddrLValue(DstAddr, QT); + llvm::Value *DstVal = + CGF.EmitLoadOfLValue(DstLV, SourceLocation()).getScalarVal(); + CGF.EmitStoreOfScalar(SrcVal, DstLV); + CGF.EmitARCRelease(DstVal, ARCImpreciseLifetime); + } + static const char *getFuncPrefix() { return "__move_assignment_"; } + + static bool isInterestingType(QualType T) { + return T.nonTrivialToDestructiveMove(); + } + static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF) { + CGF.callCStructMoveAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile, + false); + } +}; +} // namespace + +void CodeGenFunction::destroyNonTrivialCStruct(CodeGenFunction &CGF, + Address Addr, QualType Type) { + CGF.callCStructDestructor(Addr, Type, Type.isVolatileQualified()); +} + +// A template function to create the special function for a non-trivial C +// struct. Template parameter 'T' is an instantiation of IRGen. +template +static llvm::Function * +getSpecialFunction(QualType QT, bool IsVolatile, T &Gen, CharUnits DstAlignment, + CharUnits SrcAlignment, CodeGenModule &CGM) { + bool IsBinary = T::isBinary(); + std::string FuncName = getSpecialFuncName(QT, IsVolatile, DstAlignment, + SrcAlignment, IsBinary, CGM); + + // If the special function already exists in the module, return it here. + if (llvm::Function *F = CGM.getModule().getFunction(FuncName)) + return F; + + // Create the special function. + ASTContext &Ctx = CGM.getContext(); + CodeGenFunction CGF(CGM); + FunctionArgList Args; + const CGFunctionInfo &FI = getFunctionInfo(CGM, IsBinary, Args); + llvm::FunctionType *FuncTy = CGM.getTypes().GetFunctionType(FI); + llvm::Function *F = + llvm::Function::Create(FuncTy, llvm::GlobalValue::LinkOnceODRLinkage, + FuncName, &CGM.getModule()); + F->setVisibility(llvm::GlobalValue::HiddenVisibility); + CGF.CGM.SetLLVMFunctionAttributes(nullptr, FI, F); + CGF.CGM.SetLLVMFunctionAttributesForDefinition(nullptr, F); + IdentifierInfo *II = &Ctx.Idents.get(FuncName); + FunctionDecl *FD = FunctionDecl::Create( + Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, + Ctx.VoidTy, nullptr, SC_PrivateExtern, false, false); + CGF.StartFunction(FD, Ctx.VoidTy, F, FI, Args); + Gen.setFunction(CGF, Args, DstAlignment, SrcAlignment); + Gen.visitMostOuterStruct(QT, IsVolatile); + CGF.FinishFunction(); + return F; +} + +// Default-initialize a non-trivial struct or an array. +void CodeGenFunction::defaultInitNonTrivialCStructVar(Address DstPtr, + QualType QT, + bool IsVolatile) { + if (getContext().getAsArrayType(QT)) { + IRGen Gen(*this); + DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy); + Gen.visitArray(DstPtr, Address::invalid(), QT, IsVolatile); + } else { + callCStructDefaultConstructor(DstPtr, QT, IsVolatile); + } +} + +template +static void callUnaryFunction(Address DstPtr, QualType QT, bool IsVolatile, + CodeGenFunction &CGF) { + IRGen Gen; + llvm::Function *Fn = getSpecialFunction( + QT, IsVolatile, Gen, DstPtr.getAlignment(), CharUnits::Zero(), CGF.CGM); + DstPtr = CGF.Builder.CreateBitCast(DstPtr, CGF.CGM.Int8PtrPtrTy); + CGF.EmitNounwindRuntimeCall(Fn, DstPtr.getPointer()); +} + +template +static void callBinaryFunction(Address DstPtr, Address SrcPtr, QualType QT, + bool IsVolatile, CodeGenFunction &CGF, + bool EmitMemCopy) { + IRGen Gen; + llvm::Function *Fn = + getSpecialFunction(QT, IsVolatile, Gen, DstPtr.getAlignment(), + SrcPtr.getAlignment(), CGF.CGM); + if (EmitMemCopy) + CGF.EmitAggregateCopy(DstPtr, SrcPtr, QT, IsVolatile); + DstPtr = CGF.Builder.CreateBitCast(DstPtr, CGF.CGM.Int8PtrPtrTy); + SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, CGF.CGM.Int8PtrPtrTy); + CGF.EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()}); +} + +// Functions to emit calls to the special functions of a non-trivial C struct. +void CodeGenFunction::callCStructDefaultConstructor(Address DstPtr, QualType QT, + bool IsVolatile) { + callUnaryFunction(DstPtr, QT, IsVolatile, *this); +} + +void CodeGenFunction::callCStructDestructor(Address DstPtr, QualType QT, + bool IsVolatile) { + callUnaryFunction(DstPtr, QT, IsVolatile, *this); +} + +void CodeGenFunction::callCStructCopyConstructor(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy) { + callBinaryFunction(DstPtr, SrcPtr, QT, IsVolatile, *this, + EmitMemCopy); +} + +void CodeGenFunction::callCStructCopyAssignmentOperator(Address DstPtr, + Address SrcPtr, + QualType QT, + bool IsVolatile, + bool EmitMemCopy) { + callBinaryFunction(DstPtr, SrcPtr, QT, IsVolatile, *this, + EmitMemCopy); +} + +void CodeGenFunction::callCStructMoveConstructor(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy) { + callBinaryFunction(DstPtr, SrcPtr, QT, IsVolatile, *this, + EmitMemCopy); +} + +void CodeGenFunction::callCStructMoveAssignmentOperator(Address DstPtr, + Address SrcPtr, + QualType QT, + bool IsVolatile, + bool EmitMemCopy) { + callBinaryFunction(DstPtr, SrcPtr, QT, IsVolatile, *this, + EmitMemCopy); +} Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -56,6 +56,7 @@ CGExprScalar.cpp CGGPUBuiltin.cpp CGLoopInfo.cpp + CGNonTrivialStruct.cpp CGObjC.cpp CGObjCGNU.cpp CGObjCMac.cpp Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1534,6 +1534,7 @@ case QualType::DK_objc_weak_lifetime: return getLangOpts().Exceptions; case QualType::DK_objc_strong_lifetime: + case QualType::DK_c_struct_strong_field: return getLangOpts().Exceptions && CGM.getCodeGenOpts().ObjCAutoRefCountExceptions; } @@ -3330,6 +3331,26 @@ CXXDtorType Type, const CXXRecordDecl *RD); + // These functions emit calls to the special functions of non-trivial C + // structs. + void defaultInitNonTrivialCStructVar(Address DstPtr, QualType QT, + bool IsVolatile); + void callCStructDefaultConstructor(Address DstPtr, QualType QT, + bool IsVolatile); + void callCStructCopyConstructor(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy); + void callCStructMoveConstructor(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy); + void callCStructCopyAssignmentOperator(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy); + void callCStructMoveAssignmentOperator(Address DstPtr, Address SrcPtr, + QualType QT, bool IsVolatile, + bool EmitMemCopy); + void callCStructDestructor(Address DstPtr, QualType QT, bool IsVolatile); + RValue EmitCXXMemberOrOperatorCall(const CXXMethodDecl *Method, const CGCallee &Callee, @@ -3501,6 +3522,7 @@ static Destroyer destroyARCStrongPrecise; static Destroyer destroyARCWeak; static Destroyer emitARCIntrinsicUse; + static Destroyer destroyNonTrivialCStruct; void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr); llvm::Value *EmitObjCAutoreleasePoolPush(); Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1146,6 +1146,7 @@ // Objective-C features .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? .Case("objc_arc", LangOpts.ObjCAutoRefCount) + .Case("objc_arc_fields", true) .Case("objc_arc_weak", LangOpts.ObjCWeak) .Case("objc_default_synthesize_properties", LangOpts.ObjC2) .Case("objc_fixed_enum", LangOpts.ObjC2) Index: lib/Sema/JumpDiagnostics.cpp =================================================================== --- lib/Sema/JumpDiagnostics.cpp +++ lib/Sema/JumpDiagnostics.cpp @@ -154,6 +154,10 @@ return ScopePair(diag::note_protected_by_objc_weak_init, diag::note_exits_objc_weak); + case QualType::DK_c_struct_strong_field: + return ScopePair(diag::note_protected_by_non_trivial_c_struct_init, + diag::note_exits_dtor); + case QualType::DK_cxx_destructor: OutDiag = diag::note_exits_dtor; break; @@ -254,6 +258,10 @@ Diags = ScopePair(diag::note_enters_block_captures_weak, diag::note_exits_block_captures_weak); break; + case QualType::DK_c_struct_strong_field: + Diags = ScopePair(diag::note_enters_block_captures_non_trivial_c_struct, + diag::note_exits_block_captures_non_trivial_c_struct); + break; case QualType::DK_none: llvm_unreachable("non-lifetime captured variable"); } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10941,6 +10941,9 @@ } } + if (var->getType().hasNonTrivialToDestroyStruct()) + getCurFunction()->setHasBranchProtectedScope(); + // Warn about externally-visible variables being defined without a // prior declaration. We only want to do this for global // declarations, but we also specifically need to avoid doing it for @@ -14823,6 +14826,7 @@ // Get the type for the field. const Type *FDTy = FD->getType().getTypePtr(); + Qualifiers QS = FD->getType().getQualifiers(); if (!FD->isAnonymousStructOrUnion()) { // Remember all fields written by the user. @@ -14964,7 +14968,9 @@ FD->setType(T); } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && Record && !ObjCFieldLifetimeErrReported && - (!getLangOpts().CPlusPlus || Record->isUnion())) { + ((!getLangOpts().CPlusPlus && + QS.getObjCLifetime() == Qualifiers::OCL_Weak) || + Record->isUnion())) { // It's an error in ARC or Weak if a field has lifetime. // We don't want to report this in a system header, though, // so we just make the field unavailable. @@ -15000,6 +15006,13 @@ Record->setHasObjectMember(true); } } + + if (Record && !getLangOpts().CPlusPlus) { + QualType FT = FD->getType(); + if (FT.hasObjCStrongPointer()) + Record->setHasStrongObjCPointer(true); + } + if (Record && FD->getType().isVolatileQualified()) Record->setHasVolatileMember(true); // Keep track of the number of named members. Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -776,6 +776,9 @@ return VAK_Valid; } + if (Ty.nonTrivialToDestroy()) + return VAK_Invalid; + if (Ty.isCXX98PODType(Context)) return VAK_Valid; @@ -837,7 +840,10 @@ break; case VAK_Invalid: - if (Ty->isObjCObjectType()) + if (Ty.hasNonTrivialToDestroyStruct()) + Diag(E->getLocStart(), + diag::err_cannot_pass_non_trivial_c_struct_to_vararg) << Ty << CT; + else if (Ty->isObjCObjectType()) DiagRuntimeBehavior( E->getLocStart(), nullptr, PDiag(diag::err_cannot_pass_objc_interface_to_vararg) Index: test/ARCMT/checking.m =================================================================== --- test/ARCMT/checking.m +++ test/ARCMT/checking.m @@ -116,7 +116,7 @@ } struct S { - A* a; // expected-error {{ARC forbids Objective-C objects in struct}} + A* a; }; @interface B Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- /dev/null +++ test/CodeGenObjC/strong-in-c-struct.m @@ -0,0 +1,515 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - %s | FileCheck %s + +typedef void (^BlockTy)(void); + +typedef struct { + int f0; + id f1; +} Strong; + +typedef struct { + Strong f0; + id f1; +} StrongOuter; + +typedef struct { + int f0; + volatile id f1; +} StrongVolatile; + +typedef struct { + BlockTy f0; +} StrongBlock; + +typedef struct { + int i; + id f0[2][2]; +} IDArray; + +typedef struct { + double d; + Strong f0[2][2]; +} StructArray; + +Strong getStrong(void); +StrongOuter getStrongOuter(void); +void calleeStrong(Strong); +void func(Strong *); + + +// CHECK: define void @test_constructor_destructor_StrongOuter() +// CHECK: %[[T:.*]] = alloca %[[STRUCT_StrongOuter:.*]], align 8 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_StrongOuter]]* %[[T]] to i8* +// CHECK: call void @__default_constructor_8_s8_s16(i8** %[[V0]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__default_constructor_8_s8_s16(i8** %[[DST:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: call void @__default_constructor_8_s8(i8** %[[V0]]) +// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 16 +// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// CHECK: %[[V4:.*]] = bitcast i8** %[[V3]] to i8* +// CHECK: call void @llvm.memset.p0i8.i64(i8* %[[V4]], i8 0, i64 8, i32 8, i1 false) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__default_constructor_8_s8(i8** %[[DST:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// CHECK: %[[V4:.*]] = bitcast i8** %[[V3]] to i8* +// CHECK: call void @llvm.memset.p0i8.i64(i8* %[[V4]], i8 0, i64 8, i32 8, i1 false) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__destructor_8_s8_s16(i8** %[[DST:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: call void @__destructor_8_s8(i8** %[[V0]]) +// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 16 +// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// CHECK: call void @objc_storeStrong(i8** %[[V3]], i8* null) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__destructor_8_s8(i8** %[[DST:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// CHECK: call void @objc_storeStrong(i8** %[[V3]], i8* null) +// CHECK: ret void + +void test_constructor_destructor_StrongOuter(void) { + StrongOuter t; +} + +// CHECK: define void @test_copy_constructor_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8 +// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGOUTER]], align 8 +// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[S]], %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8* +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 24, i32 8, i1 false) +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8** +// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8** +// CHECK: call void @__copy_constructor_8_8_s8_s16(i8** %[[V3]], i8** %[[V4]]) +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8** +// CHECK: call void @__destructor_8_s8_s16(i8** %[[V5]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_s8_s16(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: call void @__copy_constructor_8_8_s8(i8** %[[V0]], i8** %[[V1]]) +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: %[[V9:.*]] = call i8* @objc_retain(i8* %[[V8]]) +// CHECK: store i8* %[[V9]], i8** %[[V4]], align 8 +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_s8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 8 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: %[[V9:.*]] = call i8* @objc_retain(i8* %[[V8]]) +// CHECK: store i8* %[[V9]], i8** %[[V4]], align 8 +// CHECK: ret void + +void test_copy_constructor_StrongOuter(StrongOuter *s) { + StrongOuter t = *s; +} + +// CHECK: define void @test_copy_assignment_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[D:.*]], %[[STRUCT_STRONGOUTER]]* %[[S:.*]]) +// CHECK: %[[D_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8 +// CHECK: %[[S_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8 +// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[D]], %[[STRUCT_STRONGOUTER]]** %[[D_ADDR]], align 8 +// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[S]], %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[D_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8 +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8* +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V1]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V2]], i8* %[[V3]], i64 24, i32 8, i1 false) +// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8** +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V1]] to i8** +// CHECK: call void @__copy_assignment_8_8_s8_s16(i8** %[[V4]], i8** %[[V5]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_s8_s16(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: call void @__copy_assignment_8_8_s8(i8** %[[V0]], i8** %[[V1]]) +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* %[[V8]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_s8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 8 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* %[[V8]]) +// CHECK: ret void + +void test_copy_assignment_StrongOuter(StrongOuter *d, StrongOuter *s) { + *d = *s; +} + +// CHECK: define internal void @__Block_byref_object_copy_(i8*, i8*) +// CHECK: call void @__move_constructor_8_8_s8_s16( + +// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_s8_s16(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: call void @__move_constructor_8_8_s8(i8** %[[V0]], i8** %[[V1]]) +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: store i8* null, i8** %[[V7]], align 8 +// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8 +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_s8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 8 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: store i8* null, i8** %[[V7]], align 8 +// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8 +// CHECK: ret void + +// CHECK: define internal void @__Block_byref_object_dispose_(i8*) +// CHECK: call void @__destructor_8_s8_s16( +// CHECK: ret void + +void test_move_constructor_StrongOuter(void) { + __block StrongOuter t; + BlockTy b = ^{ (void)t; }; +} + +// CHECK: define void @test_move_assignment_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[P:.*]]) +// CHECK: %[[P_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8 +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGOUTER]], align 8 +// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[P]], %[[STRUCT_STRONGOUTER]]** %[[P_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[P_ADDR]], align 8 +// CHECK: call void @getStrongOuter(%[[STRUCT_STRONGOUTER]]* sret %[[TMP]]) +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8* +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[TMP]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 24, i32 8, i1 false) +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8** +// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[TMP]] to i8** +// CHECK: call void @__move_assignment_8_8_s8_s16(i8** %[[V3]], i8** %[[V4]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_s8_s16(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: call void @__move_assignment_8_8_s8(i8** %[[V0]], i8** %[[V1]]) +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: store i8* null, i8** %[[V7]], align 8 +// CHECK: %[[V9:.*]] = load i8*, i8** %[[V4]], align 8 +// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8 +// CHECK: call void @objc_release(i8* %[[V9]]) +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_s8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 8 +// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8** +// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8 +// CHECK: store i8* null, i8** %[[V7]], align 8 +// CHECK: %[[V9:.*]] = load i8*, i8** %[[V4]], align 8 +// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8 +// CHECK: call void @objc_release(i8* %[[V9]]) +// CHECK: ret void + +void test_move_assignment_StrongOuter(StrongOuter *p) { + *p = getStrongOuter(); +} + +// CHECK: define void @test_parameter_Strong([2 x i64] %[[A_COERCE:.*]]) +// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]* +// CHECK: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) +// CHECK: ret void + +void test_parameter_Strong(Strong a) { +} + +// CHECK: define void @test_argument_Strong([2 x i64] %[[A_COERCE:.*]]) +// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8 +// CHECK-NEXT: %[[TEMP_LVALUE:.*]] = alloca %[[STRUCT_STRONG]], align 8 +// CHECK-NEXT: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]* +// CHECK-NEXT: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8 +// CHECK-NEXT: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to i8* +// CHECK-NEXT: %[[V2:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8* +// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 16, i32 8, i1 false) +// CHECK-NEXT: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to i8** +// CHECK-NEXT: %[[V4:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8** +// CHECK-NEXT: call void @__copy_constructor_8_8_s8(i8** %[[V3]], i8** %[[V4]]) +// CHECK-NEXT: %[[V5:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to [2 x i64]* +// CHECK-NEXT: %[[V6:.*]] = load [2 x i64], [2 x i64]* %[[V5]], align 8 +// CHECK-NEXT: call void @calleeStrong([2 x i64] %[[V6]]) +// CHECK-NEXT: %[[V7:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8** +// CHECK-NEXT: call void @__destructor_8_s8(i8** %[[V7]]) +// CHECK-NEXT: ret void + +void test_argument_Strong(Strong a) { + calleeStrong(a); +} + +// CHECK: define [2 x i64] @test_return_Strong([2 x i64] %[[A_COERCE:.*]]) +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8 +// CHECK-NEXT: %[[A:.*]] = alloca %[[STRUCT_STRONG]], align 8 +// CHECK-NEXT: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]* +// CHECK-NEXT: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8 +// CHECK-NEXT: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8* +// CHECK-NEXT: %[[V2:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8* +// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 16, i32 8, i1 false) +// CHECK-NEXT: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8** +// CHECK-NEXT: %[[V4:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8** +// CHECK-NEXT: call void @__copy_constructor_8_8_s8(i8** %[[V3]], i8** %[[V4]]) +// CHECK-NEXT: %[[V5:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8** +// CHECK-NEXT: call void @__destructor_8_s8(i8** %[[V5]]) +// CHECK-NEXT: %[[V6:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to [2 x i64]* +// CHECK-NEXT: %[[V7:.*]] = load [2 x i64], [2 x i64]* %[[V6]], align 8 +// CHECK-NEXT: ret [2 x i64] %[[V7]] + +Strong test_return_Strong(Strong a) { + return a; +} + +// CHECK: define void @test_destructor_ignored_result() +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8 +// CHECK: %[[CALL:.*]] = call [2 x i64] @getStrong() +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[COERCE]] to [2 x i64]* +// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V0]], align 8 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) +// CHECK: ret void + +void test_destructor_ignored_result(void) { + getStrong(); +} + +// CHECK: define void @test_copy_constructor_StrongBlock( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( +// CHECK: call void @__copy_constructor_8_8_sb0( +// CHECK: call void @__destructor_8_sb0( +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_sb0(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load i8*, i8** %[[V1]], align 8 +// CHECK: %[[V3:.*]] = call i8* @objc_retainBlock(i8* %[[V2]]) +// CHECK: store i8* %[[V3]], i8** %[[V0]], align 8 +// CHECK: ret void + +void test_copy_constructor_StrongBlock(StrongBlock *s) { + StrongBlock t = *s; +} + +// CHECK: define void @test_copy_assignment_StrongBlock(%[[STRUCT_STRONGBLOCK:.*]]* %[[D:.*]], %[[STRUCT_STRONGBLOCK]]* %[[S:.*]]) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( +// CHECK: call void @__copy_assignment_8_8_sb0( + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_sb0(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load i8*, i8** %[[V1]], align 8 +// CHECK: %[[V3:.*]] = call i8* @objc_retainBlock(i8* %[[V2]]) +// CHECK: %[[V4:.*]] = load i8*, i8** %[[V0]], align 8 +// CHECK: store i8* %[[V3]], i8** %[[V0]], align 8 +// CHECK: call void @objc_release(i8* %[[V4]]) +// CHECK: ret void + +void test_copy_assignment_StrongBlock(StrongBlock *d, StrongBlock *s) { + *d = *s; +} + +// CHECK: define void @test_copy_constructor_StrongVolatile0( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( +// CHECK: call void @__copy_constructor_8_8_sv8( +// CHECK: call void @__destructor_8_sv8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_sv8( +// CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8 +// CHECK: %[[V9:.*]] = call i8* @objc_retain(i8* %[[V8]]) +// CHECK: store volatile i8* %[[V9]], i8** %{{.*}}, align 8 + +void test_copy_constructor_StrongVolatile0(StrongVolatile *s) { + StrongVolatile t = *s; +} + +// CHECK: define void @test_copy_constructor_StrongVolatile1( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( +// CHECK: call void @__copy_constructor_8_8_sv8( + +void test_copy_constructor_StrongVolatile1(Strong *s) { + volatile Strong t = *s; +} + +// CHECK: define void @test_block_capture_Strong() +// CHECK: call void @__default_constructor_8_s8( +// CHECK: call void @__copy_constructor_8_8_s8( +// CHECK: call void @__destructor_8_s8( +// CHECK: call void @__destructor_8_s8( +// CHECK: ret void + +// CHECK: define internal void @__copy_helper_block_.1(i8*, i8*) +// CHECK: call void @__copy_constructor_8_8_s8( +// CHECK: ret void + +// CHECK: define internal void @__destroy_helper_block_.2( +// CHECK: call void @__destructor_8_s8( +// CHECK: ret void + +void test_block_capture_Strong(void) { + Strong t; + BlockTy b = ^(){ (void)t; }; +} + +// CHECK: define void @test_variable_length_array(i32 %[[N:.*]]) +// CHECK: %[[N_ADDR:.*]] = alloca i32, align 4 +// CHECK: store i32 %[[N]], i32* %[[N_ADDR]], align 4 +// CHECK: %[[V0:.*]] = load i32, i32* %[[N_ADDR]], align 4 +// CHECK: %[[V1:.*]] = zext i32 %[[V0]] to i64 +// CHECK: %[[VLA:.*]] = alloca %[[STRUCT_STRONG:.*]], i64 %[[V1]], align 8 +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[VLA]] to i8** +// CHECK: %[[V4:.*]] = mul nuw i64 16, %[[V1]] +// CHECK: %[[V5:.*]] = bitcast i8** %[[V3]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 %[[V4]] +// CHECK: %[[DSTARRAY_END:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: br label + +// CHECK: %[[DSTADDR_CUR:.*]] = phi i8** [ %[[V3]], {{.*}} ], [ %[[V7:.*]], {{.*}} ] +// CHECK: %[[DONE:.*]] = icmp eq i8** %[[DSTADDR_CUR]], %[[DSTARRAY_END]] +// CHECK: br i1 %[[DONE]], label + +// CHECK: call void @__default_constructor_8_s8(i8** %[[DSTADDR_CUR]]) +// CHECK: %[[V8:.*]] = bitcast i8** %[[DSTADDR_CUR]] to i8* +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 16 +// CHECK: %[[V7]] = bitcast i8* %[[V9]] to i8** +// CHECK: br label + +// CHECK: call void @func(%[[STRUCT_STRONG]]* %[[VLA]]) +// CHECK: %[[V10:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[VLA]], i64 %[[V1]] +// CHECK: %[[ARRAYDESTROY_ISEMPTY:.*]] = icmp eq %[[STRUCT_STRONG]]* %[[VLA]], %[[V10]] +// CHECK: br i1 %[[ARRAYDESTROY_ISEMPTY]], label + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi %[[STRUCT_STRONG]]* [ %[[V10]], {{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], {{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V11:.*]] = bitcast %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENT]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V11]]) #5 +// CHECK: %[[ARRAYDESTROY_DONE:.*]] = icmp eq %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENT]], %[[VLA]] +// CHECK: br i1 %[[ARRAYDESTROY_DONE]], label + +// CHECK: ret void + +void test_variable_length_array(int n) { + Strong a[n]; + func(a); +} + +// CHECK: define linkonce_odr hidden void @__default_constructor_8_AB8_8_4_s8_AE( +void test_constructor_destructor_IDArray(void) { + IDArray t; +} + +// CHECK: define linkonce_odr hidden void @__default_constructor_8_AB8_16_4_s16_AE( +void test_constructor_destructor_StructArray(void) { + StructArray t; +} Index: test/Lexer/has_feature_objc_arc.m =================================================================== --- test/Lexer/has_feature_objc_arc.m +++ test/Lexer/has_feature_objc_arc.m @@ -13,8 +13,16 @@ void no_objc_arc_weak_feature(); #endif +#if __has_feature(objc_arc_fields) +void has_objc_arc_fields(); +#else +void no_objc_arc_fields(); +#endif + // CHECK-ARC: void has_objc_arc_feature(); // CHECK-ARC: void has_objc_arc_weak_feature(); +// CHECK-ARC: void has_objc_arc_fields(); // CHECK-ARCLITE: void has_objc_arc_feature(); // CHECK-ARCLITE: void no_objc_arc_weak_feature(); +// CHECK-ARCLITE: void has_objc_arc_fields(); Index: test/SemaObjC/arc-decls.m =================================================================== --- test/SemaObjC/arc-decls.m +++ test/SemaObjC/arc-decls.m @@ -3,7 +3,7 @@ // rdar://8843524 struct A { - id x; // expected-error {{ARC forbids Objective-C objects in struct}} + id x; }; union u { @@ -13,7 +13,7 @@ @interface I { struct A a; struct B { - id y[10][20]; // expected-error {{ARC forbids Objective-C objects in struct}} + id y[10][20]; id z; } b; @@ -23,7 +23,7 @@ // rdar://10260525 struct r10260525 { - id (^block) (); // expected-error {{ARC forbids blocks in struct}} + id (^block) (); }; struct S { Index: test/SemaObjC/arc-system-header.m =================================================================== --- test/SemaObjC/arc-system-header.m +++ test/SemaObjC/arc-system-header.m @@ -23,8 +23,7 @@ } void test5(struct Test5 *p) { - p->field = 0; // expected-error {{'field' is unavailable in ARC}} - // expected-note@arc-system-header.h:25 {{field has non-trivial ownership qualification}} + p->field = 0; } id test6() { @@ -49,8 +48,7 @@ extern void doSomething(Test9 arg); void test9() { - Test9 foo2 = {0, 0}; // expected-error {{'field' is unavailable in ARC}} - // expected-note@arc-system-header.h:56 {{field has non-trivial ownership qualification}} + Test9 foo2 = {0, 0}; doSomething(foo2); } #endif Index: test/SemaObjC/strong-in-c-struct.m =================================================================== --- /dev/null +++ test/SemaObjC/strong-in-c-struct.m @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fsyntax-only -verify %s + +typedef struct { + id a; +} Strong; + +void callee_variadic(const char *, ...); + +void test_variadic(void) { + Strong t; + callee_variadic("s", t); // expected-error {{cannot pass non-trivial C object of type 'Strong' by value to variadic function}} +} + +void test_jump0(int cond) { + switch (cond) { + case 0: + ; + Strong x; // expected-note {{jump bypasses initialization of variable of non-trivial C struct type}} + break; + case 1: // expected-error {{cannot jump from switch statement to this case label}} + x.a = 0; + break; + } +} + +void test_jump1(void) { + static void *ips[] = { &&L0 }; +L0: // expected-note {{possible target of indirect goto}} + ; + Strong x; // expected-note {{jump exits scope of variable with non-trivial destructor}} + goto *ips; // expected-error {{cannot jump}} +} + +typedef void (^BlockTy)(void); +void func(BlockTy); +void func2(Strong); + +void test_block_scope0(int cond) { + Strong x; // expected-note {{jump enters lifetime of block which captures a C struct that is non-trivial to destroy}} + switch (cond) { + case 0: + func(^{ func2(x); }); + break; + default: // expected-error {{cannot jump from switch statement to this case label}} + break; + } +} + +void test_block_scope1(void) { + static void *ips[] = { &&L0 }; +L0: // expected-note {{possible target of indirect goto}} + ; + Strong x; // expected-note {{jump exits scope of variable with non-trivial destructor}} expected-note {{jump exits lifetime of block which captures a C struct that is non-trivial to destroy}} + func(^{ func2(x); }); + goto *ips; // expected-error {{cannot jump}} +}