Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1129,12 +1129,6 @@ PCK_Struct }; - /// 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. Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -620,8 +620,24 @@ 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|" + "implicitly initialize an object of type %1|" + "declare an unitialized automatic variable of type %1|" + "declare an automatic variable of type %1|" + "initialize an object that has automatic storage duration 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; " Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2113,6 +2113,42 @@ 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, + // Implicitly initialized subobject. + NTCUC_ImplicitInitSubObject, + // Uninialized variable with automatic storage duration. + NTCUC_UninitAutoVar, + // Variable with automatic storage duration. + NTCUC_AutoVar, + // Initializer expression for object with automatic storage duration that + // might copy from another object. + NTCUC_AutoObjInit, + // Assignment. + NTCUC_Assignment, + // Compound literal. + NTCUC_CompoundLiteral, + // Block capture. + NTCUC_BlockCapture, + // lvalue-to-rvalue conversion of volatile type. + NTCUC_LValueToRValueVolatile, + }; + + /// Emit diagnostics if any of the initilizer expressions in the passed + /// InitListExpr copies a type that is or contains a C union type that is + /// non-trivial to copy. + void checkNonTrivialCUnionCopyInInitList(const InitListExpr *ILE); + + /// 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); + void AddInitializerToDecl(Decl *dcl, Expr *init, bool DirectInit); void ActOnUninitializedDecl(Decl *dcl); void ActOnInitializerError(Decl *Dcl); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -22,7 +22,6 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" -#include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" @@ -2276,62 +2275,6 @@ 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; -}; - -} // namespace - -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; -} - QualType::PrimitiveDefaultInitializeKind QualType::isNonTrivialToPrimitiveDefaultInitialize() const { if (const auto *RT = Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1658,12 +1658,21 @@ // 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. + if (!S.getLangOpts().CPlusPlus) + S.checkNonTrivialCUnion(BC.getVariable()->getType(), + BD->getCaretLocation(), + Sema::NTCUC_BlockCapture); } } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ 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" @@ -6503,6 +6504,10 @@ if (D.isInvalidType()) NewVD->setInvalidDecl(); + + if (NewVD->hasLocalStorage()) + checkNonTrivialCUnion(NewVD->getType(), NewVD->getLocation(), + NTCUC_AutoVar); } else { bool Invalid = false; @@ -8923,6 +8928,10 @@ << FunctionType::getNameForCallConv(CC); } } + + checkNonTrivialCUnion(NewFD->getReturnType(), + NewFD->getReturnTypeSourceRange().getBegin(), + NTCUC_FunctionReturn); } else { // C++11 [replacement.functions]p3: // The program's definitions shall not be specified as inline. @@ -11069,6 +11078,273 @@ return VDecl->isInvalidDecl(); } +void Sema::checkNonTrivialCUnionCopyInInitList(const InitListExpr *ILE) { + for (auto I : ILE->inits()) { + if (isa(I)) + checkNonTrivialCUnion(I->getType(), ILE->getExprLoc(), + NTCUC_ImplicitInitSubObject); + else if (auto E = dyn_cast(I)) + checkNonTrivialCUnionCopyInInitList(E); + else + // Assume all other initializers involving copying some existing object. + // TODO: ignore any initializers where we can guarantee copy-elision. + checkNonTrivialCUnion(I->getType(), I->getExprLoc(), NTCUC_AutoObjInit); + } +}; + +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) { + bool CheckInit = false, CheckDestruct = false, CheckCopy = false; + + switch (UseContext) { + case NTCUC_FunctionParam: + CheckDestruct = CheckCopy = true; + break; + case NTCUC_FunctionReturn: + CheckDestruct = CheckCopy = true; + break; + case NTCUC_ImplicitInitSubObject: + CheckInit = true; + break; + case NTCUC_UninitAutoVar: + CheckInit = true; + break; + case NTCUC_AutoVar: + CheckDestruct = true; + break; + case NTCUC_AutoObjInit: + CheckCopy = true; + break; + case NTCUC_Assignment: + CheckCopy = true; + break; + case NTCUC_CompoundLiteral: + CheckDestruct = true; + break; + case NTCUC_BlockCapture: + CheckDestruct = CheckCopy = true; + break; + case NTCUC_LValueToRValueVolatile: + CheckDestruct = CheckCopy = true; + break; + } + + if (CheckInit && + QT.isNonTrivialToPrimitiveDefaultInitialize() == QualType::PDIK_Struct) + DiagNonTrivalCUnionDefaultInitializeVisitor(QT, Loc, UseContext, *this) + .visit(QT, nullptr, false); + if (CheckDestruct && + QT.isDestructedType() == QualType::DK_nontrivial_c_struct) + DiagNonTrivalCUnionDestructedTypeVisitor(QT, Loc, UseContext, *this) + .visit(QT, nullptr, false); + if (CheckCopy && QT.isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct) + 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. @@ -11343,6 +11619,14 @@ } } + if (!getLangOpts().CPlusPlus && VDecl->hasLocalStorage()) { + if (auto *ILE = dyn_cast(Init)) + checkNonTrivialCUnionCopyInInitList(ILE); + else + checkNonTrivialCUnion(VDecl->getType(), VDecl->getLocation(), + NTCUC_AutoObjInit); + } + if (auto *E = dyn_cast(Init)) if (auto *BE = dyn_cast(E->getSubExpr()->IgnoreParens())) if (VDecl->hasLocalStorage()) @@ -11736,7 +12020,11 @@ if (!CXXRecord->isPOD()) setFunctionHasBranchProtectedScope(); } + } else if (!getLangOpts().CPlusPlus && Var->hasLocalStorage()) { + checkNonTrivialCUnion(Var->getType(), Var->getLocation(), + NTCUC_UninitAutoVar); } + // In OpenCL, we can't initialize objects in the __local address space, // even implicitly, so don't synthesize an implicit initializer. if (getLangOpts().OpenCL && @@ -12691,6 +12979,10 @@ Context.getAdjustedParameterType(T), TSInfo, SC, nullptr); + if (!getLangOpts().CPlusPlus) + checkNonTrivialCUnion(New->getType(), New->getLocation(), + NTCUC_FunctionParam); + // Parameters can not be abstract class types. // For record types, this is done by the AbstractClassUsageDiagnoser once // the class has been completely parsed. @@ -15937,7 +16229,6 @@ // 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); @@ -16076,38 +16367,12 @@ 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()) { Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -6066,7 +6066,7 @@ 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() && @@ -6084,6 +6084,18 @@ return ExprError(); } + if (!isFileScope && !getLangOpts().CPlusPlus) { + // 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. + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + NTCUC_CompoundLiteral); + if (auto ILE = dyn_cast(E->getInitializer())) + // Emit diagnostics if any of the initilizer expressions copies a type + // that is or contains a C union type that is non-trivial to copy. + checkNonTrivialCUnionCopyInInitList(ILE); + } + return MaybeBindToTemporary(E); } @@ -12533,6 +12545,10 @@ if (auto *VD = dyn_cast(DRE->getDecl())) if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) BE->getBlockDecl()->setCanAvoidCopyToHeap(); + + if (!getLangOpts().CPlusPlus) + checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), + NTCUC_Assignment); } RecordModifiableNonNullParam(*this, LHS.get()); break; @@ -13943,6 +13959,9 @@ !BD->isDependentContext()) computeNRVO(Body, BSI); + if (!getLangOpts().CPlusPlus) + checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn); + PopDeclContext(); // Pop the block scope now but keep it alive to the end of this function. @@ -16192,6 +16211,12 @@ } ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { + // Check whether the operand is or contains an object of non-trivial C union + // type. + if (E->getType().isVolatileQualified()) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + Sema::NTCUC_LValueToRValueVolatile); + // C++2a [basic.def.odr]p4: // [...] an expression of non-volatile-qualified non-class type to which // the lvalue-to-rvalue conversion is applied [...] Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2456,6 +2456,9 @@ return true; } + if (!getLangOpts().CPlusPlus) + checkNonTrivialCUnion(T, Loc, NTCUC_FunctionReturn); + return false; } Index: test/CodeGenObjC/Inputs/strong_in_union.h =================================================================== --- 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 Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- test/CodeGenObjC/strong-in-c-struct.m +++ 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); @@ -689,14 +688,6 @@ 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( Index: test/SemaObjC/arc-decls.m =================================================================== --- test/SemaObjC/arc-decls.m +++ test/SemaObjC/arc-decls.m @@ -8,11 +8,7 @@ }; 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. Index: test/SemaObjC/non-trivial-c-union.m =================================================================== --- /dev/null +++ test/SemaObjC/non-trivial-c-union.m @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -fobjc-runtime-has-weak -verify %s + +typedef union { // expected-note 8 {{'U0' has subobjects that are non-trivial to default-initialize}} expected-note 36 {{'U0' has subobjects that are non-trivial to destruct}} expected-note 26 {{'U0' has subobjects that are non-trivial to copy}} + id f0; // expected-note 8 {{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 26 {{f0 has type '__strong id' that is non-trivial to copy}} + __weak id f1; // expected-note 8 {{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 26 {{f1 has type '__weak id' that is non-trivial to copy}} +} U0; + +typedef struct { + U0 f0; + id f1; +} S0; + +id g0; +U0 ug0; +U0 ug1 = { .f0 = 0 }; +S0 sg0; +S0 sg1 = { .f0 = {0}, .f1 = 0 }; + +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 declare an unitialized automatic variable 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 initialize an object that has automatic storage duration 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 declare an unitialized automatic variable 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 initialize an object that has automatic storage duration 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 initialize an object that has automatic storage duration 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 initialize an object that has automatic storage duration 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 implicitly 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 }; +S0 sg2 = (S0){ .f0 = {0}, .f1 = 0 }; + +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 initialize an object that has automatic storage duration 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 implicitly 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 declare an unitialized automatic variable 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 declare an unitialized automatic variable 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 declare an unitialized automatic variable 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 declare an unitialized automatic variable 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}} +}