From 43fa33b4bedc28d2faa17d678ad1f40eb42817a1 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc@gmail.com>
Date: Fri, 29 Apr 2011 09:46:08 +0000
Subject: [PATCH] Relax the non-POD memset warning to use the less restrictive
 C++11 definition of POD. Specifically, this allows certain non-aggregate
 types due to their data members being private.

The representation of C++11 POD testing is pretty gross. Any suggestions
for improvements there are welcome. Especially the name
'isCXX11PODType()' seems truly unfortunate.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130492 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/Type.h             |  6 ++++
 lib/AST/Type.cpp                     | 46 ++++++++++++++++++++++++++++
 lib/Sema/SemaChecking.cpp            | 37 ++++++++++++++--------
 test/SemaCXX/warn-non-pod-memset.cpp |  9 ++++++
 4 files changed, 86 insertions(+), 12 deletions(-)

diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index edbdaa18484..4254f32e5f0 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -1179,6 +1179,12 @@ public:
   /// (C++0x [basic.types]p9)
   bool isTrivialType() const;
 
+  /// isCXX11PODType() - Return true if this is a POD type according to the
+  /// more relaxed rules of the C++11 standard, regardless of the current
+  /// compilation's language.
+  /// (C++0x [basic.types]p9)
+  bool isCXX11PODType() const;
+
   /// Helper methods to distinguish type categories. All type predicates
   /// operate on the canonical type, ignoring typedefs and qualifiers.
 
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index f4b34f0c063..a8b213889a5 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -943,6 +943,52 @@ bool Type::isTrivialType() const {
   return false;
 }
 
+// This is effectively the intersection of isTrivialType and hasStandardLayout.
+// We implement it dircetly to avoid redundant conversions from a type to
+// a CXXRecordDecl.
+bool Type::isCXX11PODType() const {
+  if (isIncompleteType())
+    return false;
+
+  // C++11 [basic.types]p9:
+  //   Scalar types, POD classes, arrays of such types, and cv-qualified
+  //   versions of these types are collectively called trivial types.
+  const Type *BaseTy = getBaseElementTypeUnsafe();
+  assert(BaseTy && "NULL element type");
+  if (BaseTy->isScalarType()) return true;
+  if (const RecordType *RT = BaseTy->getAs<RecordType>()) {
+    if (const CXXRecordDecl *ClassDecl =
+        dyn_cast<CXXRecordDecl>(RT->getDecl())) {
+      // C++11 [class]p10:
+      //   A POD struct is a non-union class that is both a trivial class [...]
+      // C++11 [class]p5:
+      //   A trivial class is a class that has a trivial default constructor
+      if (!ClassDecl->hasTrivialConstructor()) return false;
+      //   and is trivially copyable.
+      if (!ClassDecl->isTriviallyCopyable()) return false;
+
+      // C++11 [class]p10:
+      //   A POD struct is a non-union class that is both a trivial class and
+      //   a standard-layout class [...]
+      if (!ClassDecl->hasStandardLayout()) return false;
+
+      // C++11 [class]p10:
+      //   A POD struct is a non-union class that is both a trivial class and
+      //   a standard-layout class, and has no non-static data members of type
+      //   non-POD struct, non-POD union (or array of such types). [...]
+      //
+      // We don't directly query the recursive aspect as the requiremets for
+      // both standard-layout classes and trivial classes apply recursively
+      // already.
+    }
+
+    return true;
+  }
+
+  // No other types can match.
+  return false;
+}
+
 bool Type::isPromotableIntegerType() const {
   if (const BuiltinType *BT = getAs<BuiltinType>())
     switch (BT->getKind()) {
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index f616a10524e..dcfb7cc5214 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -1812,21 +1812,34 @@ void Sema::CheckMemsetArguments(const CallExpr *Call) {
 
   const Expr *Dest = Call->getArg(0)->IgnoreParenImpCasts();
 
+  // The type checking for this warning is moderately expensive, only do it
+  // when enabled.
+  if (getDiagnostics().getDiagnosticLevel(diag::warn_non_pod_memset,
+                                          Dest->getExprLoc()) ==
+      Diagnostic::Ignored)
+    return;
+
   QualType DestTy = Dest->getType();
   if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
     QualType PointeeTy = DestPtrTy->getPointeeType();
-    if (!PointeeTy->isPODType() && !PointeeTy->isVoidType()) {
-      DiagRuntimeBehavior(
-        Dest->getExprLoc(), Dest,
-        PDiag(diag::warn_non_pod_memset)
-          << PointeeTy << Call->getCallee()->getSourceRange());
-
-      SourceRange ArgRange = Call->getArg(0)->getSourceRange();
-      DiagRuntimeBehavior(
-        Dest->getExprLoc(), Dest,
-        PDiag(diag::note_non_pod_memset_silence)
-          << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
-    }
+    if (PointeeTy->isVoidType())
+      return;
+
+    // Check the C++11 POD definition regardless of language mode; it is more
+    // relaxed than earlier definitions and we don't want spurrious warnings.
+    if (PointeeTy->isCXX11PODType())
+      return;
+
+    DiagRuntimeBehavior(
+      Dest->getExprLoc(), Dest,
+      PDiag(diag::warn_non_pod_memset)
+        << PointeeTy << Call->getCallee()->getSourceRange());
+
+    SourceRange ArgRange = Call->getArg(0)->getSourceRange();
+    DiagRuntimeBehavior(
+      Dest->getExprLoc(), Dest,
+      PDiag(diag::note_non_pod_memset_silence)
+        << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
   }
 }
 
diff --git a/test/SemaCXX/warn-non-pod-memset.cpp b/test/SemaCXX/warn-non-pod-memset.cpp
index 97bbdc27cc4..1ca71497112 100644
--- a/test/SemaCXX/warn-non-pod-memset.cpp
+++ b/test/SemaCXX/warn-non-pod-memset.cpp
@@ -7,6 +7,14 @@ struct S1 {} s1;
 struct S2 { int x; } s2;
 struct S3 { float x, y; S1 s[4]; void (*f)(S1**); } s3;
 
+// We use the C++11 concept of POD for this warning, so ensure a non-aggregate
+// still warns.
+class C1 {
+  int x, y, z;
+public:
+  void foo() {}
+} c1;
+
 // Non-POD types that should warn.
 struct X1 { X1(); } x1;
 struct X2 { ~X2(); } x2;
@@ -45,6 +53,7 @@ void test_nowarn(void *void_ptr) {
   memset(&s1, 0, sizeof s1);
   memset(&s2, 0, sizeof s2);
   memset(&s3, 0, sizeof s3);
+  memset(&c1, 0, sizeof c1);
 
   // Unevaluated code shouldn't warn.
   (void)sizeof memset(&x1, 0, sizeof x1);
-- 
GitLab