diff options
author | Akira Hatanaka <ahatanaka@apple.com> | 2019-07-13 01:47:15 +0000 |
---|---|---|
committer | Akira Hatanaka <ahatanaka@apple.com> | 2019-07-13 01:47:15 +0000 |
commit | 657b099cc209b4c70658e8cbc1e29e40b7dbffe7 (patch) | |
tree | 1c79967bb90719a1d22e9b8ee2b957570bc15364 /lib | |
parent | dd578bb497322855a0116d2ed3b6ccff7f8ad7db (diff) | |
download | clang-657b099cc209b4c70658e8cbc1e29e40b7dbffe7.tar.gz |
[Sema] Diagnose default-initialization, destruction, and copying of
non-trivial C union types
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
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@365985 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/Type.cpp | 60 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 18 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 329 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 33 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 5 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderDecl.cpp | 3 | ||||
-rw-r--r-- | lib/Serialization/ASTWriterDecl.cpp | 9 |
7 files changed, 371 insertions, 86 deletions
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 733ca232dd..01e93c11aa 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2276,60 +2276,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 <bool IsMove> -struct IsNonTrivialCopyMoveVisitor - : CopiedTypeVisitor<IsNonTrivialCopyMoveVisitor<IsMove>, IsMove, bool> { - using Super = - CopiedTypeVisitor<IsNonTrivialCopyMoveVisitor<IsMove>, 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<RecordType>()->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<CXXRecordDecl>(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<false>(Ctx).visit(*this)) - return true; - if (IsNonTrivialCopyMoveVisitor<true>(Ctx).visit(*this)) - return true; - return false; +bool QualType::hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD) { + return RD->hasNonTrivialToPrimitiveCopyCUnion(); } QualType::PrimitiveDefaultInitializeKind diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 3941643893..11fed28b52 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -1658,12 +1658,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<BlocksAttr>()) + if (VD->hasAttr<BlocksAttr>()) { + // 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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index b2a0632c6e..73407afb49 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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" @@ -6504,6 +6505,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; @@ -8924,6 +8930,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. @@ -11070,6 +11082,263 @@ bool Sema::DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, return VDecl->isInvalidDecl(); } +void Sema::checkNonTrivialCUnionInInitializer(const Expr *Init, SourceLocation Loc) { + if (auto *CE = dyn_cast<ConstantExpr>(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<InitListExpr>(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<ImplicitValueInitExpr>(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<DiagNonTrivalCUnionDefaultInitializeVisitor, + void> { + using Super = + DefaultInitializedTypeVisitor<DiagNonTrivalCUnionDefaultInitializeVisitor, + void>; + + 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<RecordType>()->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<DiagNonTrivalCUnionDestructedTypeVisitor, void> { + using Super = + DestructedTypeVisitor<DiagNonTrivalCUnionDestructedTypeVisitor, void>; + + 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<RecordType>()->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<DiagNonTrivalCUnionCopyVisitor, false, void> { + using Super = CopiedTypeVisitor<DiagNonTrivalCUnionCopyVisitor, false, void>; + + 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<RecordType>()->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. @@ -11475,6 +11744,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); @@ -11599,7 +11874,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; @@ -12692,6 +12974,11 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, Context.getAdjustedParameterType(T), TSInfo, SC, nullptr); + 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. @@ -15938,7 +16225,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, // Verify that all the fields are okay. SmallVector<FieldDecl*, 32> RecFields; - bool ObjCFieldLifetimeErrReported = false; for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end(); i != end; ++i) { FieldDecl *FD = cast<FieldDecl>(*i); @@ -16077,38 +16363,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<UnavailableAttr>()) { - 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()) { @@ -16128,14 +16388,23 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (Record && !getLangOpts().CPlusPlus && !FD->hasAttr<UnavailableAttr>()) { 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<RecordType>()) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 746d3e7e11..1e49a363ab 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -6066,7 +6066,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() && @@ -6084,6 +6084,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); } @@ -12533,6 +12546,10 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, if (auto *VD = dyn_cast<VarDecl>(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; @@ -13945,6 +13962,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. @@ -16196,6 +16218,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/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 88b544068a..514cbd90d3 100644 --- a/lib/Sema/SemaType.cpp +++ b/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/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index b40e3cf892..3cac82ad42 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/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/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 3d9dd7131b..b71315505d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/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 |