From 090510608da404a0e1c80fa29eb5fc18f2d91755 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sat, 7 Sep 2019 00:34:43 +0000 Subject: [PATCH] [Sema] Diagnose default-initialization, destruction, and copying of non-trivial C union types This recommits r365985, which was reverted because it broke a few projects using unions containing non-trivial ObjC pointer fields in system headers. We now have a patch to fix the problem (see https://reviews.llvm.org/D65256). Original commit message: This patch diagnoses uses of non-trivial C unions and structs/unions containing non-trivial C unions in the following contexts, which require default-initialization, destruction, or copying of the union objects, instead of disallowing fields of non-trivial types in C unions, which is what we currently do: - function parameters. - function returns. - assignments. - compound literals. - block captures except capturing of `__block` variables by non-escaping blocks. - local and global variable definitions. - lvalue-to-rvalue conversions of volatile types. See the discussion in https://reviews.llvm.org/D62988 for more background. rdar://problem/50679094 Differential Revision: https://reviews.llvm.org/D63753 llvm-svn: 371275 --- clang/include/clang/AST/Decl.h | 24 ++ clang/include/clang/AST/DeclBase.h | 9 +- clang/include/clang/AST/Type.h | 45 +++- clang/include/clang/Basic/DiagnosticSemaKinds.td | 19 +- clang/include/clang/Sema/Sema.h | 42 +++ clang/lib/AST/Decl.cpp | 3 + clang/lib/AST/Type.cpp | 60 +---- clang/lib/Sema/Sema.cpp | 18 +- clang/lib/Sema/SemaDecl.cpp | 330 ++++++++++++++++++++--- clang/lib/Sema/SemaExpr.cpp | 33 ++- clang/lib/Sema/SemaType.cpp | 5 + clang/lib/Serialization/ASTReaderDecl.cpp | 3 + clang/lib/Serialization/ASTWriterDecl.cpp | 9 + clang/test/CodeGenObjC/Inputs/strong_in_union.h | 10 - clang/test/CodeGenObjC/strong-in-c-struct.m | 15 +- clang/test/PCH/non-trivial-c-union.m | 24 ++ clang/test/SemaObjC/arc-decls.m | 6 +- clang/test/SemaObjC/non-trivial-c-union.m | 82 ++++++ 18 files changed, 615 insertions(+), 122 deletions(-) delete mode 100644 clang/test/CodeGenObjC/Inputs/strong_in_union.h create mode 100644 clang/test/PCH/non-trivial-c-union.m create mode 100644 clang/test/SemaObjC/non-trivial-c-union.m diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0d9da7d..096df48 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3754,6 +3754,30 @@ public: RecordDeclBits.NonTrivialToPrimitiveDestroy = V; } + bool hasNonTrivialToPrimitiveDefaultInitializeCUnion() const { + return RecordDeclBits.HasNonTrivialToPrimitiveDefaultInitializeCUnion; + } + + void setHasNonTrivialToPrimitiveDefaultInitializeCUnion(bool V) { + RecordDeclBits.HasNonTrivialToPrimitiveDefaultInitializeCUnion = V; + } + + bool hasNonTrivialToPrimitiveDestructCUnion() const { + return RecordDeclBits.HasNonTrivialToPrimitiveDestructCUnion; + } + + void setHasNonTrivialToPrimitiveDestructCUnion(bool V) { + RecordDeclBits.HasNonTrivialToPrimitiveDestructCUnion = V; + } + + bool hasNonTrivialToPrimitiveCopyCUnion() const { + return RecordDeclBits.HasNonTrivialToPrimitiveCopyCUnion; + } + + void setHasNonTrivialToPrimitiveCopyCUnion(bool V) { + RecordDeclBits.HasNonTrivialToPrimitiveCopyCUnion = V; + } + /// Determine whether this class can be passed in registers. In C++ mode, /// it must have at least one trivial, non-deleted copy or move constructor. /// FIXME: This should be set as part of completeDefinition. diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 26edb77..d64d0cb 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1440,6 +1440,13 @@ class DeclContext { uint64_t NonTrivialToPrimitiveCopy : 1; uint64_t NonTrivialToPrimitiveDestroy : 1; + /// The following bits indicate whether this is or contains a C union that + /// is non-trivial to default-initialize, destruct, or copy. These bits + /// imply the associated basic non-triviality predicates declared above. + uint64_t HasNonTrivialToPrimitiveDefaultInitializeCUnion : 1; + uint64_t HasNonTrivialToPrimitiveDestructCUnion : 1; + uint64_t HasNonTrivialToPrimitiveCopyCUnion : 1; + /// Indicates whether this struct is destroyed in the callee. uint64_t ParamDestroyedInCallee : 1; @@ -1448,7 +1455,7 @@ class DeclContext { }; /// Number of non-inherited bits in RecordDeclBitfields. - enum { NumRecordDeclBits = 11 }; + enum { NumRecordDeclBits = 14 }; /// Stores the bits used by OMPDeclareReductionDecl. /// If modified NumOMPDeclareReductionDeclBits and the accessor diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index dc1db05..88f022b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1133,12 +1133,6 @@ public: }; /// Check if this is a non-trivial type that would cause a C struct - /// transitively containing this type to be non-trivial. This function can be - /// used to determine whether a field of this type can be declared inside a C - /// union. - bool isNonTrivialPrimitiveCType(const ASTContext &Ctx) const; - - /// Check if this is a non-trivial type that would cause a C struct /// transitively containing this type to be non-trivial to copy and return the /// kind. PrimitiveCopyKind isNonTrivialToPrimitiveCopy() const; @@ -1167,6 +1161,22 @@ public: return isDestructedTypeImpl(*this); } + /// Check if this is or contains a C union that is non-trivial to + /// default-initialize, which is a union that has a member that is non-trivial + /// to default-initialize. If this returns true, + /// isNonTrivialToPrimitiveDefaultInitialize returns PDIK_Struct. + bool hasNonTrivialToPrimitiveDefaultInitializeCUnion() const; + + /// Check if this is or contains a C union that is non-trivial to destruct, + /// which is a union that has a member that is non-trivial to destruct. If + /// this returns true, isDestructedType returns DK_nontrivial_c_struct. + bool hasNonTrivialToPrimitiveDestructCUnion() const; + + /// Check if this is or contains a C union that is non-trivial to copy, which + /// is a union that has a member that is non-trivial to copy. If this returns + /// true, isNonTrivialToPrimitiveCopy returns PCK_Struct. + bool hasNonTrivialToPrimitiveCopyCUnion() const; + /// Determine whether expressions of the given type are forbidden /// from being lvalues in C. /// @@ -1239,6 +1249,11 @@ private: const ASTContext &C); static QualType IgnoreParens(QualType T); static DestructionKind isDestructedTypeImpl(QualType type); + + /// Check if \param RD is or contains a non-trivial C union. + static bool hasNonTrivialToPrimitiveDefaultInitializeCUnion(const RecordDecl *RD); + static bool hasNonTrivialToPrimitiveDestructCUnion(const RecordDecl *RD); + static bool hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD); }; } // namespace clang @@ -6255,6 +6270,24 @@ inline Qualifiers::GC QualType::getObjCGCAttr() const { return getQualifiers().getObjCGCAttr(); } +inline bool QualType::hasNonTrivialToPrimitiveDefaultInitializeCUnion() const { + if (auto *RD = getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) + return hasNonTrivialToPrimitiveDefaultInitializeCUnion(RD); + return false; +} + +inline bool QualType::hasNonTrivialToPrimitiveDestructCUnion() const { + if (auto *RD = getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) + return hasNonTrivialToPrimitiveDestructCUnion(RD); + return false; +} + +inline bool QualType::hasNonTrivialToPrimitiveCopyCUnion() const { + if (auto *RD = getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) + return hasNonTrivialToPrimitiveCopyCUnion(RD); + return false; +} + inline FunctionType::ExtInfo getFunctionExtInfo(const Type &t) { if (const auto *PT = t.getAs()) { if (const auto *FT = PT->getPointeeType()->getAs()) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d04a96f..9ccf897 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -647,8 +647,23 @@ def warn_cstruct_memaccess : Warning< InGroup; def note_nontrivial_field : Note< "field is non-trivial to %select{copy|default-initialize}0">; -def err_nontrivial_primitive_type_in_union : Error< - "non-trivial C types are disallowed in union">; +def err_non_trivial_c_union_in_invalid_context : Error< + "cannot %select{" + "use type %1 for a function/method parameter|" + "use type %1 for function/method return|" + "default-initialize an object of type %1|" + "declare an automatic variable of type %1|" + "copy-initialize an object of type %1|" + "assign to a variable of type %1|" + "construct an automatic compound literal of type %1|" + "capture a variable of type %1|" + "cannot use volatile type %1 where it causes an lvalue-to-rvalue conversion" + "}3 " + "since it %select{contains|is}2 a union that is non-trivial to " + "%select{default-initialize|destruct|copy}0">; +def note_non_trivial_c_union : Note< + "%select{%2 has subobjects that are|%3 has type %2 that is}0 " + "non-trivial to %select{default-initialize|destruct|copy}1">; def warn_dyn_class_memaccess : Warning< "%select{destination for|source of|first operand of|second operand of}0 this " "%1 call is a pointer to %select{|class containing a }2dynamic class %3; " diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d7581a1..59f16da 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2145,6 +2145,48 @@ public: bool SetParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg, SourceLocation EqualLoc); + // Contexts where using non-trivial C union types can be disallowed. This is + // passed to err_non_trivial_c_union_in_invalid_context. + enum NonTrivialCUnionContext { + // Function parameter. + NTCUC_FunctionParam, + // Function return. + NTCUC_FunctionReturn, + // Default-initialized object. + NTCUC_DefaultInitializedObject, + // Variable with automatic storage duration. + NTCUC_AutoVar, + // Initializer expression that might copy from another object. + NTCUC_CopyInit, + // Assignment. + NTCUC_Assignment, + // Compound literal. + NTCUC_CompoundLiteral, + // Block capture. + NTCUC_BlockCapture, + // lvalue-to-rvalue conversion of volatile type. + NTCUC_LValueToRValueVolatile, + }; + + /// Emit diagnostics if the initializer or any of its explicit or + /// implicitly-generated subexpressions require copying or + /// default-initializing a type that is or contains a C union type that is + /// non-trivial to copy or default-initialize. + void checkNonTrivialCUnionInInitializer(const Expr *Init, SourceLocation Loc); + + // These flags are passed to checkNonTrivialCUnion. + enum NonTrivialCUnionKind { + NTCUK_Init = 0x1, + NTCUK_Destruct = 0x2, + NTCUK_Copy = 0x4, + }; + + /// Emit diagnostics if a non-trivial C union type or a struct that contains + /// a non-trivial C union is used in an invalid context. + void checkNonTrivialCUnion(QualType QT, SourceLocation Loc, + NonTrivialCUnionContext UseContext, + unsigned NonTrivialKind); + void AddInitializerToDecl(Decl *dcl, Expr *init, bool DirectInit); void ActOnUninitializedDecl(Decl *dcl); void ActOnInitializerError(Decl *Dcl); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 6c0c828..1d28f03 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4268,6 +4268,9 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setNonTrivialToPrimitiveDefaultInitialize(false); setNonTrivialToPrimitiveCopy(false); setNonTrivialToPrimitiveDestroy(false); + setHasNonTrivialToPrimitiveDefaultInitializeCUnion(false); + setHasNonTrivialToPrimitiveDestructCUnion(false); + setHasNonTrivialToPrimitiveCopyCUnion(false); setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 60862682..411cb74 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2289,60 +2289,16 @@ bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const { getObjCLifetime() != Qualifiers::OCL_Weak; } -namespace { -// Helper class that determines whether this is a type that is non-trivial to -// primitive copy or move, or is a struct type that has a field of such type. -template -struct IsNonTrivialCopyMoveVisitor - : CopiedTypeVisitor, IsMove, bool> { - using Super = - CopiedTypeVisitor, IsMove, bool>; - IsNonTrivialCopyMoveVisitor(const ASTContext &C) : Ctx(C) {} - void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT) {} - - bool visitWithKind(QualType::PrimitiveCopyKind PCK, QualType QT) { - if (const auto *AT = this->Ctx.getAsArrayType(QT)) - return this->asDerived().visit(Ctx.getBaseElementType(AT)); - return Super::visitWithKind(PCK, QT); - } - - bool visitARCStrong(QualType QT) { return true; } - bool visitARCWeak(QualType QT) { return true; } - bool visitTrivial(QualType QT) { return false; } - // Volatile fields are considered trivial. - bool visitVolatileTrivial(QualType QT) { return false; } - - bool visitStruct(QualType QT) { - const RecordDecl *RD = QT->castAs()->getDecl(); - // We don't want to apply the C restriction in C++ because C++ - // (1) can apply the restriction at a finer grain by banning copying or - // destroying the union, and - // (2) allows users to override these restrictions by declaring explicit - // constructors/etc, which we're not proposing to add to C. - if (isa(RD)) - return false; - for (const FieldDecl *FD : RD->fields()) - if (this->asDerived().visit(FD->getType())) - return true; - return false; - } - - const ASTContext &Ctx; -}; +bool QualType::hasNonTrivialToPrimitiveDefaultInitializeCUnion(const RecordDecl *RD) { + return RD->hasNonTrivialToPrimitiveDefaultInitializeCUnion(); +} -} // namespace +bool QualType::hasNonTrivialToPrimitiveDestructCUnion(const RecordDecl *RD) { + return RD->hasNonTrivialToPrimitiveDestructCUnion(); +} -bool QualType::isNonTrivialPrimitiveCType(const ASTContext &Ctx) const { - if (isNonTrivialToPrimitiveDefaultInitialize()) - return true; - DestructionKind DK = isDestructedType(); - if (DK != DK_none && DK != DK_cxx_destructor) - return true; - if (IsNonTrivialCopyMoveVisitor(Ctx).visit(*this)) - return true; - if (IsNonTrivialCopyMoveVisitor(Ctx).visit(*this)) - return true; - return false; +bool QualType::hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD) { + return RD->hasNonTrivialToPrimitiveCopyCUnion(); } QualType::PrimitiveDefaultInitializeKind diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 152cc7d..3f4dacb 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1684,12 +1684,24 @@ static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) { // Set the EscapingByref flag of __block variables captured by // escaping blocks. for (const BlockDecl *BD : FSI.Blocks) { - if (BD->doesNotEscape()) - continue; for (const BlockDecl::Capture &BC : BD->captures()) { VarDecl *VD = BC.getVariable(); - if (VD->hasAttr()) + if (VD->hasAttr()) { + // Nothing to do if this is a __block variable captured by a + // non-escaping block. + if (BD->doesNotEscape()) + continue; VD->setEscapingByref(); + } + // Check whether the captured variable is or contains an object of + // non-trivial C union type. + QualType CapType = BC.getVariable()->getType(); + if (CapType.hasNonTrivialToPrimitiveDestructCUnion() || + CapType.hasNonTrivialToPrimitiveCopyCUnion()) + S.checkNonTrivialCUnion(BC.getVariable()->getType(), + BD->getCaretLocation(), + Sema::NTCUC_BlockCapture, + Sema::NTCUK_Destruct|Sema::NTCUK_Copy); } } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a2708223..279332a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/PartialDiagnostic.h" @@ -6612,6 +6613,11 @@ NamedDecl *Sema::ActOnVariableDeclarator( if (D.isInvalidType()) NewVD->setInvalidDecl(); + + if (NewVD->getType().hasNonTrivialToPrimitiveDestructCUnion() && + NewVD->hasLocalStorage()) + checkNonTrivialCUnion(NewVD->getType(), NewVD->getLocation(), + NTCUC_AutoVar, NTCUK_Destruct); } else { bool Invalid = false; @@ -9061,6 +9067,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, << FunctionType::getNameForCallConv(CC); } } + + if (NewFD->getReturnType().hasNonTrivialToPrimitiveDestructCUnion() || + NewFD->getReturnType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(NewFD->getReturnType(), + NewFD->getReturnTypeSourceRange().getBegin(), + NTCUC_FunctionReturn, NTCUK_Destruct|NTCUK_Copy); } else { // C++11 [replacement.functions]p3: // The program's definitions shall not be specified as inline. @@ -11207,6 +11219,264 @@ bool Sema::DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, return VDecl->isInvalidDecl(); } +void Sema::checkNonTrivialCUnionInInitializer(const Expr *Init, + SourceLocation Loc) { + if (auto *CE = dyn_cast(Init)) + Init = CE->getSubExpr(); + + QualType InitType = Init->getType(); + assert((InitType.hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + InitType.hasNonTrivialToPrimitiveCopyCUnion()) && + "shouldn't be called if type doesn't have a non-trivial C struct"); + if (auto *ILE = dyn_cast(Init)) { + for (auto I : ILE->inits()) { + if (!I->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() && + !I->getType().hasNonTrivialToPrimitiveCopyCUnion()) + continue; + SourceLocation SL = I->getExprLoc(); + checkNonTrivialCUnionInInitializer(I, SL.isValid() ? SL : Loc); + } + return; + } + + if (isa(Init)) { + if (InitType.hasNonTrivialToPrimitiveDefaultInitializeCUnion()) + checkNonTrivialCUnion(InitType, Loc, NTCUC_DefaultInitializedObject, + NTCUK_Init); + } else { + // Assume all other explicit initializers involving copying some existing + // object. + // TODO: ignore any explicit initializers where we can guarantee + // copy-elision. + if (InitType.hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(InitType, Loc, NTCUC_CopyInit, NTCUK_Copy); + } +} + +namespace { + +struct DiagNonTrivalCUnionDefaultInitializeVisitor + : DefaultInitializedTypeVisitor { + using Super = + DefaultInitializedTypeVisitor; + + DiagNonTrivalCUnionDefaultInitializeVisitor( + QualType OrigTy, SourceLocation OrigLoc, + Sema::NonTrivialCUnionContext UseContext, Sema &S) + : OrigTy(OrigTy), OrigLoc(OrigLoc), UseContext(UseContext), S(S) {} + + void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType QT, + const FieldDecl *FD, bool InNonTrivialUnion) { + if (const auto *AT = S.Context.getAsArrayType(QT)) + return this->asDerived().visit(S.Context.getBaseElementType(AT), FD, + InNonTrivialUnion); + return Super::visitWithKind(PDIK, QT, FD, InNonTrivialUnion); + } + + void visitARCStrong(QualType QT, const FieldDecl *FD, + bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 0 << QT << FD->getName(); + } + + void visitARCWeak(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 0 << QT << FD->getName(); + } + + void visitStruct(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + const RecordDecl *RD = QT->castAs()->getDecl(); + if (RD->isUnion()) { + if (OrigLoc.isValid()) { + bool IsUnion = false; + if (auto *OrigRD = OrigTy->getAsRecordDecl()) + IsUnion = OrigRD->isUnion(); + S.Diag(OrigLoc, diag::err_non_trivial_c_union_in_invalid_context) + << 0 << OrigTy << IsUnion << UseContext; + // Reset OrigLoc so that this diagnostic is emitted only once. + OrigLoc = SourceLocation(); + } + InNonTrivialUnion = true; + } + + if (InNonTrivialUnion) + S.Diag(RD->getLocation(), diag::note_non_trivial_c_union) + << 0 << 0 << QT.getUnqualifiedType() << ""; + + for (const FieldDecl *FD : RD->fields()) + asDerived().visit(FD->getType(), FD, InNonTrivialUnion); + } + + void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} + + // The non-trivial C union type or the struct/union type that contains a + // non-trivial C union. + QualType OrigTy; + SourceLocation OrigLoc; + Sema::NonTrivialCUnionContext UseContext; + Sema &S; +}; + +struct DiagNonTrivalCUnionDestructedTypeVisitor + : DestructedTypeVisitor { + using Super = + DestructedTypeVisitor; + + DiagNonTrivalCUnionDestructedTypeVisitor( + QualType OrigTy, SourceLocation OrigLoc, + Sema::NonTrivialCUnionContext UseContext, Sema &S) + : OrigTy(OrigTy), OrigLoc(OrigLoc), UseContext(UseContext), S(S) {} + + void visitWithKind(QualType::DestructionKind DK, QualType QT, + const FieldDecl *FD, bool InNonTrivialUnion) { + if (const auto *AT = S.Context.getAsArrayType(QT)) + return this->asDerived().visit(S.Context.getBaseElementType(AT), FD, + InNonTrivialUnion); + return Super::visitWithKind(DK, QT, FD, InNonTrivialUnion); + } + + void visitARCStrong(QualType QT, const FieldDecl *FD, + bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 1 << QT << FD->getName(); + } + + void visitARCWeak(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 1 << QT << FD->getName(); + } + + void visitStruct(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + const RecordDecl *RD = QT->castAs()->getDecl(); + if (RD->isUnion()) { + if (OrigLoc.isValid()) { + bool IsUnion = false; + if (auto *OrigRD = OrigTy->getAsRecordDecl()) + IsUnion = OrigRD->isUnion(); + S.Diag(OrigLoc, diag::err_non_trivial_c_union_in_invalid_context) + << 1 << OrigTy << IsUnion << UseContext; + // Reset OrigLoc so that this diagnostic is emitted only once. + OrigLoc = SourceLocation(); + } + InNonTrivialUnion = true; + } + + if (InNonTrivialUnion) + S.Diag(RD->getLocation(), diag::note_non_trivial_c_union) + << 0 << 1 << QT.getUnqualifiedType() << ""; + + for (const FieldDecl *FD : RD->fields()) + asDerived().visit(FD->getType(), FD, InNonTrivialUnion); + } + + void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} + void visitCXXDestructor(QualType QT, const FieldDecl *FD, + bool InNonTrivialUnion) {} + + // The non-trivial C union type or the struct/union type that contains a + // non-trivial C union. + QualType OrigTy; + SourceLocation OrigLoc; + Sema::NonTrivialCUnionContext UseContext; + Sema &S; +}; + +struct DiagNonTrivalCUnionCopyVisitor + : CopiedTypeVisitor { + using Super = CopiedTypeVisitor; + + DiagNonTrivalCUnionCopyVisitor(QualType OrigTy, SourceLocation OrigLoc, + Sema::NonTrivialCUnionContext UseContext, + Sema &S) + : OrigTy(OrigTy), OrigLoc(OrigLoc), UseContext(UseContext), S(S) {} + + void visitWithKind(QualType::PrimitiveCopyKind PCK, QualType QT, + const FieldDecl *FD, bool InNonTrivialUnion) { + if (const auto *AT = S.Context.getAsArrayType(QT)) + return this->asDerived().visit(S.Context.getBaseElementType(AT), FD, + InNonTrivialUnion); + return Super::visitWithKind(PCK, QT, FD, InNonTrivialUnion); + } + + void visitARCStrong(QualType QT, const FieldDecl *FD, + bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 2 << QT << FD->getName(); + } + + void visitARCWeak(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 2 << QT << FD->getName(); + } + + void visitStruct(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + const RecordDecl *RD = QT->castAs()->getDecl(); + if (RD->isUnion()) { + if (OrigLoc.isValid()) { + bool IsUnion = false; + if (auto *OrigRD = OrigTy->getAsRecordDecl()) + IsUnion = OrigRD->isUnion(); + S.Diag(OrigLoc, diag::err_non_trivial_c_union_in_invalid_context) + << 2 << OrigTy << IsUnion << UseContext; + // Reset OrigLoc so that this diagnostic is emitted only once. + OrigLoc = SourceLocation(); + } + InNonTrivialUnion = true; + } + + if (InNonTrivialUnion) + S.Diag(RD->getLocation(), diag::note_non_trivial_c_union) + << 0 << 2 << QT.getUnqualifiedType() << ""; + + for (const FieldDecl *FD : RD->fields()) + asDerived().visit(FD->getType(), FD, InNonTrivialUnion); + } + + void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT, + const FieldDecl *FD, bool InNonTrivialUnion) {} + void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} + void visitVolatileTrivial(QualType QT, const FieldDecl *FD, + bool InNonTrivialUnion) {} + + // The non-trivial C union type or the struct/union type that contains a + // non-trivial C union. + QualType OrigTy; + SourceLocation OrigLoc; + Sema::NonTrivialCUnionContext UseContext; + Sema &S; +}; + +} // namespace + +void Sema::checkNonTrivialCUnion(QualType QT, SourceLocation Loc, + NonTrivialCUnionContext UseContext, + unsigned NonTrivialKind) { + assert((QT.hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + QT.hasNonTrivialToPrimitiveDestructCUnion() || + QT.hasNonTrivialToPrimitiveCopyCUnion()) && + "shouldn't be called if type doesn't have a non-trivial C union"); + + if ((NonTrivialKind & NTCUK_Init) && + QT.hasNonTrivialToPrimitiveDefaultInitializeCUnion()) + DiagNonTrivalCUnionDefaultInitializeVisitor(QT, Loc, UseContext, *this) + .visit(QT, nullptr, false); + if ((NonTrivialKind & NTCUK_Destruct) && + QT.hasNonTrivialToPrimitiveDestructCUnion()) + DiagNonTrivalCUnionDestructedTypeVisitor(QT, Loc, UseContext, *this) + .visit(QT, nullptr, false); + if ((NonTrivialKind & NTCUK_Copy) && QT.hasNonTrivialToPrimitiveCopyCUnion()) + DiagNonTrivalCUnionCopyVisitor(QT, Loc, UseContext, *this) + .visit(QT, nullptr, false); +} + /// AddInitializerToDecl - Adds the initializer Init to the /// declaration dcl. If DirectInit is true, this is C++ direct /// initialization rather than copy initialization. @@ -11615,6 +11885,12 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { CheckForConstantInitializer(Init, DclT); } + QualType InitType = Init->getType(); + if (!InitType.isNull() && + (InitType.hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + InitType.hasNonTrivialToPrimitiveCopyCUnion())) + checkNonTrivialCUnionInInitializer(Init, Init->getExprLoc()); + // We will represent direct-initialization similarly to copy-initialization: // int x(1); -as-> int x = 1; // ClassType x(a,b,c); -as-> ClassType x = ClassType(a,b,c); @@ -11739,7 +12015,14 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { return; } - switch (Var->isThisDeclarationADefinition()) { + VarDecl::DefinitionKind DefKind = Var->isThisDeclarationADefinition(); + if (!Var->isInvalidDecl() && DefKind != VarDecl::DeclarationOnly && + Var->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion()) + checkNonTrivialCUnion(Var->getType(), Var->getLocation(), + NTCUC_DefaultInitializedObject, NTCUK_Init); + + + switch (DefKind) { case VarDecl::Definition: if (!Var->isStaticDataMember() || !Var->getAnyInitializer()) break; @@ -12829,6 +13112,11 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, if (auto *LSI = getEnclosingLambda()) LSI->LocalPacks.push_back(New); + if (New->getType().hasNonTrivialToPrimitiveDestructCUnion() || + New->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(New->getType(), New->getLocation(), + NTCUC_FunctionParam, NTCUK_Destruct|NTCUK_Copy); + // Parameters can not be abstract class types. // For record types, this is done by the AbstractClassUsageDiagnoser once // the class has been completely parsed. @@ -16077,7 +16365,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, // Verify that all the fields are okay. SmallVector RecFields; - bool ObjCFieldLifetimeErrReported = false; for (ArrayRef::iterator i = Fields.begin(), end = Fields.end(); i != end; ++i) { FieldDecl *FD = cast(*i); @@ -16216,38 +16503,12 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, Record->setHasObjectMember(true); if (Record && FDTTy->getDecl()->hasVolatileMember()) Record->setHasVolatileMember(true); - if (Record && Record->isUnion() && - FD->getType().isNonTrivialPrimitiveCType(Context)) - Diag(FD->getLocation(), - diag::err_nontrivial_primitive_type_in_union); } else if (FDTy->isObjCObjectType()) { /// A field cannot be an Objective-c object Diag(FD->getLocation(), diag::err_statically_allocated_object) << FixItHint::CreateInsertion(FD->getLocation(), "*"); QualType T = Context.getObjCObjectPointerType(FD->getType()); FD->setType(T); - } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - Record && !ObjCFieldLifetimeErrReported && Record->isUnion() && - !getLangOpts().CPlusPlus) { - // 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. - // FIXME: that's really not sufficient; we need to make the type - // itself invalid to, say, initialize or copy. - QualType T = FD->getType(); - if (T.hasNonTrivialObjCLifetime()) { - SourceLocation loc = FD->getLocation(); - if (getSourceManager().isInSystemHeader(loc)) { - if (!FD->hasAttr()) { - FD->addAttr(UnavailableAttr::CreateImplicit(Context, "", - UnavailableAttr::IR_ARCFieldWithOwnership, loc)); - } - } else { - Diag(FD->getLocation(), diag::err_arc_objc_object_in_tag) - << T->isBlockPointerType() << Record->getTagKind(); - } - ObjCFieldLifetimeErrReported = true; - } } else if (getLangOpts().ObjC && getLangOpts().getGC() != LangOptions::NonGC && Record && !Record->hasObjectMember()) { @@ -16267,14 +16528,23 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (Record && !getLangOpts().CPlusPlus && !FD->hasAttr()) { QualType FT = FD->getType(); - if (FT.isNonTrivialToPrimitiveDefaultInitialize()) + if (FT.isNonTrivialToPrimitiveDefaultInitialize()) { Record->setNonTrivialToPrimitiveDefaultInitialize(true); + if (FT.hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + Record->isUnion()) + Record->setHasNonTrivialToPrimitiveDefaultInitializeCUnion(true); + } QualType::PrimitiveCopyKind PCK = FT.isNonTrivialToPrimitiveCopy(); - if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial) + if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial) { Record->setNonTrivialToPrimitiveCopy(true); + if (FT.hasNonTrivialToPrimitiveCopyCUnion() || Record->isUnion()) + Record->setHasNonTrivialToPrimitiveCopyCUnion(true); + } if (FT.isDestructedType()) { Record->setNonTrivialToPrimitiveDestroy(true); Record->setParamDestroyedInCallee(true); + if (FT.hasNonTrivialToPrimitiveDestructCUnion() || Record->isUnion()) + Record->setHasNonTrivialToPrimitiveDestructCUnion(true); } if (const auto *RT = FT->getAs()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3001be2..7f00981 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6083,7 +6083,7 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, ILE->setInit(i, ConstantExpr::Create(Context, Init)); } - Expr *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, + auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK, LiteralExpr, isFileScope); if (isFileScope) { if (!LiteralExpr->isTypeDependent() && @@ -6101,6 +6101,19 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, return ExprError(); } + // Compound literals that have automatic storage duration are destroyed at + // the end of the scope. Emit diagnostics if it is or contains a C union type + // that is non-trivial to destruct. + if (!isFileScope) + if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + NTCUC_CompoundLiteral, NTCUK_Destruct); + + if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnionInInitializer(E->getInitializer(), + E->getInitializer()->getExprLoc()); + return MaybeBindToTemporary(E); } @@ -12782,6 +12795,10 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, if (auto *VD = dyn_cast(DRE->getDecl())) if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) BE->getBlockDecl()->setCanAvoidCopyToHeap(); + + if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), + NTCUC_Assignment, NTCUK_Copy); } RecordModifiableNonNullParam(*this, LHS.get()); break; @@ -14194,6 +14211,11 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, !BD->isDependentContext()) computeNRVO(Body, BSI); + if (RetTy.hasNonTrivialToPrimitiveDestructCUnion() || + RetTy.hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn, + NTCUK_Destruct|NTCUK_Copy); + PopDeclContext(); // Pop the block scope now but keep it alive to the end of this function. @@ -16464,6 +16486,15 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, } ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { + // Check whether the operand is or contains an object of non-trivial C union + // type. + if (E->getType().isVolatileQualified() && + (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion())) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + Sema::NTCUC_LValueToRValueVolatile, + NTCUK_Destruct|NTCUK_Copy); + // C++2a [basic.def.odr]p4: // [...] an expression of non-volatile-qualified non-class type to which // the lvalue-to-rvalue conversion is applied [...] diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f9c36e0..4281a6a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2456,6 +2456,11 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { return true; } + if (T.hasNonTrivialToPrimitiveDestructCUnion() || + T.hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(T, Loc, NTCUC_FunctionReturn, + NTCUK_Destruct|NTCUK_Copy); + return false; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 15797ba..ddfb6899 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -794,6 +794,9 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { RD->setNonTrivialToPrimitiveDefaultInitialize(Record.readInt()); RD->setNonTrivialToPrimitiveCopy(Record.readInt()); RD->setNonTrivialToPrimitiveDestroy(Record.readInt()); + RD->setHasNonTrivialToPrimitiveDefaultInitializeCUnion(Record.readInt()); + RD->setHasNonTrivialToPrimitiveDestructCUnion(Record.readInt()); + RD->setHasNonTrivialToPrimitiveCopyCUnion(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt()); RD->setArgPassingRestrictions((RecordDecl::ArgPassingKind)Record.readInt()); return Redecl; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 3d9dd71..b713155 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -476,6 +476,9 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { Record.push_back(D->isNonTrivialToPrimitiveDefaultInitialize()); Record.push_back(D->isNonTrivialToPrimitiveCopy()); Record.push_back(D->isNonTrivialToPrimitiveDestroy()); + Record.push_back(D->hasNonTrivialToPrimitiveDefaultInitializeCUnion()); + Record.push_back(D->hasNonTrivialToPrimitiveDestructCUnion()); + Record.push_back(D->hasNonTrivialToPrimitiveCopyCUnion()); Record.push_back(D->isParamDestroyedInCallee()); Record.push_back(D->getArgPassingRestrictions()); @@ -1999,6 +2002,12 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNonTrivialToPrimitiveDestroy Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + // hasNonTrivialToPrimitiveDefaultInitializeCUnion + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + // hasNonTrivialToPrimitiveDestructCUnion + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + // hasNonTrivialToPrimitiveCopyCUnion + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isParamDestroyedInCallee Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // getArgPassingRestrictions diff --git a/clang/test/CodeGenObjC/Inputs/strong_in_union.h b/clang/test/CodeGenObjC/Inputs/strong_in_union.h deleted file mode 100644 index abe4549..0000000 --- a/clang/test/CodeGenObjC/Inputs/strong_in_union.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef STRONG_IN_UNION_H -#define STRONG_IN_UNION_H -#pragma clang system_header - -typedef union { - id f0; - int *f1; -} U; - -#endif // STRONG_IN_UNION_H diff --git a/clang/test/CodeGenObjC/strong-in-c-struct.m b/clang/test/CodeGenObjC/strong-in-c-struct.m index b385177..eae5013 100644 --- a/clang/test/CodeGenObjC/strong-in-c-struct.m +++ b/clang/test/CodeGenObjC/strong-in-c-struct.m @@ -1,11 +1,10 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - -DUSESTRUCT -I %S/Inputs %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - -DUSESTRUCT %s | FileCheck %s -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-pch -I %S/Inputs -o %t %s -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -include-pch %t -emit-llvm -o - -DUSESTRUCT -I %S/Inputs %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-pch -o %t %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -include-pch %t -emit-llvm -o - -DUSESTRUCT %s | FileCheck %s #ifndef HEADER #define HEADER -#include "strong_in_union.h" typedef void (^BlockTy)(void); @@ -695,14 +694,6 @@ void test_copy_constructor_Bitfield1(Bitfield1 *a) { Bitfield1 t = *a; } -// CHECK: define void @test_strong_in_union() -// CHECK: alloca %{{.*}} -// CHECK-NEXT: ret void - -void test_strong_in_union() { - U t; -} - // CHECK: define void @test_copy_constructor_VolatileArray( // CHECK: call void @__copy_constructor_8_8_s0_AB8s4n16_tv64w32_AE( diff --git a/clang/test/PCH/non-trivial-c-union.m b/clang/test/PCH/non-trivial-c-union.m new file mode 100644 index 0000000..abd3354 --- /dev/null +++ b/clang/test/PCH/non-trivial-c-union.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -emit-pch -o %t.pch %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -include-pch %t.pch -verify %s + +#ifndef HEADER +#define HEADER + +typedef union { + id f0; +} U0; + +#else + +// expected-note@-6 {{'U0' has subobjects that are non-trivial to destruct}} +// expected-note@-7 {{'U0' has subobjects that are non-trivial to copy}} +// expected-note@-8 {{'U0' has subobjects that are non-trivial to default-initialize}} +// expected-note@-8 {{f0 has type '__strong id' that is non-trivial to destruct}} +// expected-note@-9 {{f0 has type '__strong id' that is non-trivial to copy}} +// expected-note@-10 {{f0 has type '__strong id' that is non-trivial to default-initialize}} + +U0 foo0(void); // expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to copy}} + +U0 g0; // expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + +#endif diff --git a/clang/test/SemaObjC/arc-decls.m b/clang/test/SemaObjC/arc-decls.m index 0abd45d..28c3de9 100644 --- a/clang/test/SemaObjC/arc-decls.m +++ b/clang/test/SemaObjC/arc-decls.m @@ -8,11 +8,7 @@ struct A { }; union u { - id u; // expected-error {{ARC forbids Objective-C objects in union}} -}; - -union u_nontrivial_c { - struct A a; // expected-error {{non-trivial C types are disallowed in union}} + id u; }; // Volatile fields are fine. diff --git a/clang/test/SemaObjC/non-trivial-c-union.m b/clang/test/SemaObjC/non-trivial-c-union.m new file mode 100644 index 0000000..7bd8277 --- /dev/null +++ b/clang/test/SemaObjC/non-trivial-c-union.m @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -fobjc-runtime-has-weak -verify %s + +typedef union { // expected-note 12 {{'U0' has subobjects that are non-trivial to default-initialize}} expected-note 36 {{'U0' has subobjects that are non-trivial to destruct}} expected-note 28 {{'U0' has subobjects that are non-trivial to copy}} + id f0; // expected-note 12 {{f0 has type '__strong id' that is non-trivial to default-initialize}} expected-note 36 {{f0 has type '__strong id' that is non-trivial to destruct}} expected-note 28 {{f0 has type '__strong id' that is non-trivial to copy}} + __weak id f1; // expected-note 12 {{f1 has type '__weak id' that is non-trivial to default-initialize}} expected-note 36 {{f1 has type '__weak id' that is non-trivial to destruct}} expected-note 28 {{f1 has type '__weak id' that is non-trivial to copy}} +} U0; + +typedef struct { + U0 f0; + id f1; +} S0; + +id g0; +U0 ug0; // expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} +U0 ug1 = { .f0 = 0 }; +S0 sg0; // expected-error {{cannot default-initialize an object of type 'S0' since it contains a union that is non-trivial to default-initialize}} +S0 sg1 = { .f0 = {0}, .f1 = 0 }; +S0 sg2 = { .f1 = 0 }; // expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + +U0 foo0(U0); // expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to copy}} +S0 foo1(S0); // expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to copy}} + +@interface C +-(U0)m0:(U0)arg; // expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to copy}} +-(S0)m1:(S0)arg; // expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to copy}} +@end + +void testBlockFunction(void) { + (void)^(U0 a){ return ug0; }; // expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for a function/method parameter since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U0' for function/method return since it is a union that is non-trivial to copy}} + (void)^(S0 a){ return sg0; }; // expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for a function/method parameter since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to destruct}} expected-error {{cannot use type 'S0' for function/method return since it contains a union that is non-trivial to copy}} +} +void testAutoVar(void) { + U0 u0; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + U0 u1 = ug0; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot copy-initialize an object of type 'U0' since it is a union that is non-trivial to copy}} + U0 u2 = { g0 }; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} + U0 u3 = { .f1 = g0 }; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} + S0 s0; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'S0' since it contains a union that is non-trivial to default-initialize}} + S0 s1 = sg0; // expected-error {{declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot copy-initialize an object of type 'S0' since it contains a union that is non-trivial to copy}} + S0 s2 = { ug0 }; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot copy-initialize an object of type 'U0' since it is a union that is non-trivial to copy}} + S0 s3 = { .f0 = ug0 }; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot copy-initialize an object of type 'U0' since it is a union that is non-trivial to copy}} + S0 s4 = { .f1 = g0 }; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} +} + +void testAssignment(void) { + ug0 = ug1; // expected-error {{cannot assign to a variable of type 'U0' since it is a union that is non-trivial to copy}} + sg0 = sg1; // expected-error {{cannot assign to a variable of type 'S0' since it contains a union that is non-trivial to copy}} +} + +U0 ug2 = (U0){ .f1 = 0 }; // expected-error {{cannot copy-initialize an object of type 'U0' since it is a union that is non-trivial to copy}} +S0 sg3 = (S0){ .f0 = {0}, .f1 = 0 }; // expected-error {{cannot copy-initialize an object of type 'S0' since it contains a union that is non-trivial to copy}} +S0 *sg4 = &(S0){ .f1 = 0 }; // expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + +void testCompoundLiteral(void) { + const U0 *t0 = &(U0){ .f0 = g0 }; // expected-error {{cannot construct an automatic compound literal of type 'U0' since it is a union that is non-trivial to destruct}} + const U0 *t1 = &(U0){ .f1 = g0 }; // expected-error {{cannot construct an automatic compound literal of type 'U0' since it is a union that is non-trivial to destruct}} + const S0 *t2 = &(S0){ .f0 = ug0 }; // expected-error {{cannot construct an automatic compound literal of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot copy-initialize an object of type 'U0' since it is a union that is non-trivial to copy}} + const S0 *t3 = &(S0){ .f1 = g0 }; // expected-error {{cannot construct an automatic compound literal of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} +} + +typedef void (^BlockTy)(void); +void escapingFunc(BlockTy); +void noescapingFunc(__attribute__((noescape)) BlockTy); + +void testBlockCapture(void) { + U0 t0; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + S0 t1; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'S0' since it contains a union that is non-trivial to default-initialize}} + __block U0 t2; // expected-error {{cannot declare an automatic variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'U0' since it is a union that is non-trivial to default-initialize}} + __block S0 t3; // expected-error {{cannot declare an automatic variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot default-initialize an object of type 'S0' since it contains a union that is non-trivial to default-initialize}} + + escapingFunc(^{ g0 = t0.f0; }); // expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to copy}} + escapingFunc(^{ g0 = t1.f0.f0; }); // expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to copy}} + escapingFunc(^{ g0 = t2.f0; }); // expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to copy}} + escapingFunc(^{ g0 = t3.f0.f0; }); // expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to copy}} + noescapingFunc(^{ g0 = t0.f0; }); // expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'U0' since it is a union that is non-trivial to copy}} + noescapingFunc(^{ g0 = t1.f0.f0; }); // expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to destruct}} expected-error {{cannot capture a variable of type 'S0' since it contains a union that is non-trivial to copy}} + noescapingFunc(^{ g0 = t2.f0; }); + noescapingFunc(^{ g0 = t3.f0.f0; }); +} + +void testVolatileLValueToRValue(volatile U0 *a) { + (void)*a; // expected-error {{cannot use volatile type 'volatile U0' where it causes an lvalue-to-rvalue conversion since it is a union that is non-trivial to destruct}} // expected-error {{cannot use volatile type 'volatile U0' where it causes an lvalue-to-rvalue conversion since it is a union that is non-trivial to copy}} +} -- 2.7.4