summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAkira Hatanaka <ahatanaka@apple.com>2019-07-13 01:47:15 +0000
committerAkira Hatanaka <ahatanaka@apple.com>2019-07-13 01:47:15 +0000
commit657b099cc209b4c70658e8cbc1e29e40b7dbffe7 (patch)
tree1c79967bb90719a1d22e9b8ee2b957570bc15364 /lib
parentdd578bb497322855a0116d2ed3b6ccff7f8ad7db (diff)
downloadclang-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.cpp60
-rw-r--r--lib/Sema/Sema.cpp18
-rw-r--r--lib/Sema/SemaDecl.cpp329
-rw-r--r--lib/Sema/SemaExpr.cpp33
-rw-r--r--lib/Sema/SemaType.cpp5
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp3
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp9
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