[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
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 @@
// 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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a270822..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 @@
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 @@
<< 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 @@
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.
@@ -11615,6 +11885,12 @@
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 @@
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 @@
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 @@
// 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);
@@ -16216,38 +16503,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<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()) {
@@ -16267,14 +16528,23 @@
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/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 @@
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 @@
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 @@
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;
@@ -14194,6 +14211,11 @@
!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 @@
}
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 @@
return true;
}
+ if (T.hasNonTrivialToPrimitiveDestructCUnion() ||
+ T.hasNonTrivialToPrimitiveCopyCUnion())
+ checkNonTrivialCUnion(T, Loc, NTCUC_FunctionReturn,
+ NTCUK_Destruct|NTCUK_Copy);
+
return false;
}