Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -411,8 +411,8 @@ InGroup, DefaultError; 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 dynamic class %2; vtable pointer will be " - "%select{overwritten|copied|moved|compared}3">, + "%1 call is a pointer to %select{|class containing a }2dynamic class %3; " + "vtable pointer will be %select{overwritten|copied|moved|compared}4">, InGroup>; def note_bad_memaccess_silence : Note< "explicitly cast the pointer to silence this warning">; Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -3983,14 +3983,31 @@ return true; } -/// \brief Determine whether the given type is a dynamic class type (e.g., -/// whether it has a vtable). -static bool isDynamicClassType(QualType T) { - if (CXXRecordDecl *Record = T->getAsCXXRecordDecl()) - if (CXXRecordDecl *Definition = Record->getDefinition()) - if (Definition->isDynamicClass()) - return true; - +/// \brief Determine whether the given type is or contains a dynamic class type +/// (e.g., whether it has a vtable). +static bool isOrContainsDynamicClass(QualType T, bool &IsContained) { + // Look through array types while ignoring qualifiers. + const Type *Ty = T->getBaseElementTypeUnsafe(); + IsContained = false; + + const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + RD = RD ? RD->getDefinition() : nullptr; + if (!RD) + return false; + + if (RD->isDynamicClass()) + return true; + + // Check all the fields. If any bases were dynamic, the class is dynamic. + // It's impossible for a class to transitively contain itself by value, so + // infinite recursion is impossible. + for (auto *FD : RD->fields()) { + if (isOrContainsDynamicClass(FD->getType(), IsContained)) { + IsContained = true; + return true; + } + } + return false; } @@ -4135,7 +4152,8 @@ } // Always complain about dynamic classes. - if (isDynamicClassType(PointeeTy)) { + bool IsContained; + if (isOrContainsDynamicClass(PointeeTy, IsContained)) { unsigned OperationType = 0; // "overwritten" if we're warning about the destination for any call @@ -4153,8 +4171,7 @@ Dest->getExprLoc(), Dest, PDiag(diag::warn_dyn_class_memaccess) << (BId == Builtin::BImemcmp ? ArgIdx + 2 : ArgIdx) - << FnName << PointeeTy - << OperationType + << FnName << IsContained << PointeeTy << OperationType << Call->getCallee()->getSourceRange()); } else if (PointeeTy.hasNonTrivialObjCLifetime() && BId != Builtin::BImemset) Index: test/SemaCXX/warn-bad-memaccess.cpp =================================================================== --- test/SemaCXX/warn-bad-memaccess.cpp +++ test/SemaCXX/warn-bad-memaccess.cpp @@ -24,6 +24,11 @@ struct X1 { virtual void f(); } x1; struct X2 : virtual S1 {} x2; +struct ContainsDynamic { X1 dynamic; } contains_dynamic; +struct DeepContainsDynamic { ContainsDynamic m; } deep_contains_dynamic; +struct ContainsArrayDynamic { X1 dynamic[1]; } contains_array_dynamic; +struct ContainsPointerDynamic { X1 *dynamic; } contains_pointer_dynamic; + void test_warn() { memset(&x1, 0, sizeof x1); // \ // expected-warning {{destination for this 'memset' call is a pointer to dynamic class}} \ @@ -90,6 +95,16 @@ __builtin___memcpy_chk(0, &x1, sizeof x1, sizeof x1); // \ // expected-warning{{source of this '__builtin___memcpy_chk' call is a pointer to dynamic class}} \ // expected-note {{explicitly cast the pointer to silence this warning}} + + // expected-warning@+2 {{destination for this 'memset' call is a pointer to class containing a dynamic class}} + // expected-note@+1 {{explicitly cast the pointer to silence this warning}} + memset(&contains_dynamic, 0, sizeof(contains_dynamic)); + // expected-warning@+2 {{destination for this 'memset' call is a pointer to class containing a dynamic class}} + // expected-note@+1 {{explicitly cast the pointer to silence this warning}} + memset(&deep_contains_dynamic, 0, sizeof(deep_contains_dynamic)); + // expected-warning@+2 {{destination for this 'memset' call is a pointer to class containing a dynamic class}} + // expected-note@+1 {{explicitly cast the pointer to silence this warning}} + memset(&contains_array_dynamic, 0, sizeof(contains_array_dynamic)); } void test_nowarn(void *void_ptr) { @@ -107,6 +122,8 @@ memset(&s3, 0, sizeof s3); memset(&c1, 0, sizeof c1); + memset(&contains_pointer_dynamic, 0, sizeof(contains_pointer_dynamic)); + // Unevaluated code shouldn't warn. (void)sizeof memset(&x1, 0, sizeof x1);