[clang] [clang] Warn about memset/memcpy to NonTriviallyCopyable types (PR #111434)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 7 13:55:52 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: None (serge-sans-paille)
<details>
<summary>Changes</summary>
This implements a warning that's similar to what GCC does in that context: both memcpy and memset require their first and second operand to be trivially copyable, let's warn if that's not the case.
---
Full diff: https://github.com/llvm/llvm-project/pull/111434.diff
3 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4)
- (modified) clang/lib/Sema/SemaChecking.cpp (+24)
- (modified) clang/test/SemaCXX/constexpr-string.cpp (+4)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 583475327c5227..d9bff4a559b3b7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -790,6 +790,10 @@ def warn_cstruct_memaccess : Warning<
"%1 call is a pointer to record %2 that is not trivial to "
"%select{primitive-default-initialize|primitive-copy}3">,
InGroup<NonTrivialMemaccess>;
+def warn_cxxstruct_memaccess : Warning<
+ "%select{destination for|source of|first operand of|second operand of}0 this "
+ "%1 call is a pointer to record %2 that is not trivially-copyable">,
+ InGroup<NonTrivialMemaccess>;
def note_nontrivial_field : Note<
"field is non-trivial to %select{copy|default-initialize}0">;
def err_non_trivial_c_union_in_invalid_context : Error<
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2bcb930acdcb57..46dda34d0ac8f3 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8899,18 +8899,42 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
<< ArgIdx << FnName << PointeeTy
<< Call->getCallee()->getSourceRange());
else if (const auto *RT = PointeeTy->getAs<RecordType>()) {
+
+ auto IsTriviallyCopyableCXXRecord = [](auto const *RT) {
+ auto const *D = RT->getDecl();
+ if (!D)
+ return true;
+ auto const *RD = dyn_cast<CXXRecordDecl>(D);
+ if (!RD)
+ return true;
+ RD = RD->getDefinition();
+ if (!RD)
+ return true;
+ return RD->isTriviallyCopyable();
+ };
+
if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
RT->getDecl()->isNonTrivialToPrimitiveDefaultInitialize()) {
DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
PDiag(diag::warn_cstruct_memaccess)
<< ArgIdx << FnName << PointeeTy << 0);
SearchNonTrivialToInitializeField::diag(PointeeTy, Dest, *this);
+ } else if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
+ !IsTriviallyCopyableCXXRecord(RT)) {
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cxxstruct_memaccess)
+ << ArgIdx << FnName << PointeeTy);
} else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
RT->getDecl()->isNonTrivialToPrimitiveCopy()) {
DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
PDiag(diag::warn_cstruct_memaccess)
<< ArgIdx << FnName << PointeeTy << 1);
SearchNonTrivialToCopyField::diag(PointeeTy, Dest, *this);
+ } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
+ !IsTriviallyCopyableCXXRecord(RT)) {
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cxxstruct_memaccess)
+ << ArgIdx << FnName << PointeeTy);
} else {
continue;
}
diff --git a/clang/test/SemaCXX/constexpr-string.cpp b/clang/test/SemaCXX/constexpr-string.cpp
index c456740ef7551f..26e2e138ef34e0 100644
--- a/clang/test/SemaCXX/constexpr-string.cpp
+++ b/clang/test/SemaCXX/constexpr-string.cpp
@@ -603,12 +603,16 @@ namespace MemcpyEtc {
};
constexpr bool test_nontrivial_memcpy() { // expected-error {{never produces a constant}}
NonTrivial arr[3] = {};
+ // expected-warning at +2 {{source of this '__builtin_memcpy' call is a pointer to record 'NonTrivial' that is not trivially-copyable}}
+ // expected-note at +1 {{explicitly cast the pointer to silence this warning}}
__builtin_memcpy(arr, arr + 1, sizeof(NonTrivial)); // expected-note 2{{non-trivially-copyable}}
return true;
}
static_assert(test_nontrivial_memcpy()); // expected-error {{constant}} expected-note {{in call}}
constexpr bool test_nontrivial_memmove() { // expected-error {{never produces a constant}}
NonTrivial arr[3] = {};
+ // expected-warning at +2 {{source of this '__builtin_memcpy' call is a pointer to record 'NonTrivial' that is not trivially-copyable}}
+ // expected-note at +1 {{explicitly cast the pointer to silence this warning}}
__builtin_memcpy(arr, arr + 1, sizeof(NonTrivial)); // expected-note 2{{non-trivially-copyable}}
return true;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/111434
More information about the cfe-commits
mailing list