JDK-8357579 : Compilation error: first argument in call to 'memset' is a pointer to non-trivially copyable type
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 25
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2025-05-21
  • Updated: 2025-11-24
  • Resolved: 2025-11-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 26
26 b25Fixed
Related Reports
Causes :  
Duplicate :  
Duplicate :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
clang 20 on Linux

A DESCRIPTION OF THE PROBLEM :
With clang 20, resolveFieldEntry.cpp and resolveMethodEntry.cpp break the build with similar warnings:

src/hotspot/share/oops/resolvedFieldEntry.cpp:49:10: error: first argument in call to 'memset' is a pointer to non-trivially copyable type 'ResolvedFieldEntry' [-Werror,-Wnontrivial-memcall]
   49 | memset(this, 0, sizeof(*this));
      | ^
src/hotspot/share/oops/resolvedFieldEntry.cpp:49:10: note: explicitly cast the pointer to silence this warning
   49 | memset(this, 0, sizeof(*this));
      | ^
      | (void*)
src/hotspot/share/oops/resolvedMethodEntry.cpp:43:12: error: first argument in call to 'memset' is a pointer to non-trivially copyable type 'ResolvedMethodEntry' [-Werror,-Wnontrivial-memcall]
   43 | memset(this, 0, sizeof(*this));
      | ^
src/hotspot/share/oops/resolvedMethodEntry.cpp:43:12: note: explicitly cast the pointer to silence this warning
   43 | memset(this, 0, sizeof(*this));
      | ^
      | (void*)
XXXX/src/hotspot/share/oops/resolvedMethodEntry.cpp:48:12: error: first argument in call to 'memset' is a pointer to non-trivially copyable type 'ResolvedMethodEntry' [-Werror,-Wnontrivial-memcall]
   48 | memset(this, 0, sizeof(*this));
      | ^
XXXX/src/hotspot/share/oops/resolvedMethodEntry.cpp:48:12: note: explicitly cast the pointer to silence this warning
   48 | memset(this, 0, sizeof(*this));
      | ^
      | (void*)


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Build with --with-toolchain-type=clang when clang is version 20.
Comments
Changeset: 66fb0152 Branch: master Author: Jan Kratochvil <jkratochvil@openjdk.org> Committer: Ioi Lam <iklam@openjdk.org> Date: 2025-11-18 21:51:28 +0000 URL: https://git.openjdk.org/jdk/commit/66fb015267058f9b5e6788eaeaa758be56ba553e
18-11-2025

I have tried to add to https://github.com/openjdk/jdk/pull/26098 rather a removal of all compiler paddings so that trivial copy constructors are safe. I find the code more simple. It also produces slightly more optimal code. Sure despite the added checks there remains a risk of reintroducing a compiler padding when changing the class fields.
06-11-2025

Update: I confirmed that my proposed fix works: Without the following lines, Windows build is not deterministic. When enabled, Windows build is deterministic. ResolvedFieldEntry saved = *this; memset(this, 0, sizeof(*this)); // Clear all data, including paddings. copy_from(saved); // Get real data back, but leave zeros in paddings.
06-11-2025

[~jkratochvil] It's not scope-creep to talk about these issues. Just throwing a cast in there doesn't make the code correct. The clang warning is informing us of a genuine problem. The current code is invoking UB. After the call to memset the value pointed to by `this` is no longer a ResolvedFieldEntry. (See C++17 6.8. The memset is ending the lifetime of the object pointed to by `this` by reusing the storage as a byte buffer.) The fact that both gcc and clang currently generate working code doesn't matter. That's one possible behavior in the face of UB. Other behaviors are possible, and may occur in other compiler versions, or different call sites even with the same version, &etc.
06-11-2025

[~jkratochvil] While often the best fix is a quick cast to appease the compiler, we usually try to look for a more elegant fix for the underlying issue. Since your PR https://github.com/openjdk/jdk/pull/26098 was opened 4 month ago and you didn't indicate any urgency, I assumed we had more time for a proper fix. The warning points to a real problem: non-trivially copyable types are slower to copy. So a better fix would be to make the two types trivially copyable. That will fix the warning and make things faster. I have a draft fix here: https://github.com/openjdk/jdk/pull/28172 . Could you try it and see if the warnings goes away for you? The original code tries to zero the paddings every time a ResolvedFieldEntry is created. However, this is not necessary, as regular JVM execution doesn't look into the paddings. The paddings need to be zero only when the ResolvedFieldEntry is stored into the CDS archive. So we can just zero the paddings in ResolvedFieldEntry::remove_unshareable_info() and ResolvedFieldEntry::mark_and_relocate(). All archived ResolvedFieldEntry objects will go through either of these functions before they are stored into the CDS archive. Meanwhile, I am running tests to make sure the CDS archive is still deterministic.
06-11-2025

[~iklam] There are discussions below about issues around padding. I do not see that related to the submitted PR https://github.com/openjdk/jdk/pull/26098 . The PR only adds (void *) cast for memset. That has no effect for gcc and it makes the codebase compilable by clang. It has no effect on generated code by either compiler. (It has no effect on gcc debug info, it has a subtle effect on clang debug info.) The (void *) cast only removes the compiler warning. It keeps the same pointer value. So I find the patch as an obvious clang compilation fix. Why the padding discussion happened below could be explained by common review issues that can be called "scope creep", "review hijacking" or "blocking review for orthogonal reasons". While I consider it unrelated I can try to follow-up on the problem description below. ResolvedFieldEntry has non-trivial copy constructor defined at: https://github.com/openjdk/jdk/blob/463f5dc112386802b9ce0cc985a961ecfd3fbc55/src/hotspot/share/oops/resolvedFieldEntry.hpp#L83 Mentioned rfi->mark_and_relocate(); calls: https://github.com/openjdk/jdk/blob/463f5dc112386802b9ce0cc985a961ecfd3fbc55/src/hotspot/share/oops/resolvedFieldEntry.cpp#L53 I do not see there any padding issue there, function ResolvedFieldEntry::mark_and_relocate() processes only the field: InstanceKlass* _field_holder; // Field holder klass You mention "If we use the default copy constructor" but I do not see why anyone would do that. The PR above makes no code change. And the default copy constructor is not in use due to: https://github.com/openjdk/jdk/blob/463f5dc112386802b9ce0cc985a961ecfd3fbc55/src/hotspot/share/oops/resolvedFieldEntry.hpp#L83 The other PR part deals with ResolvedMethodEntry but you do not mention any problem with it and the PR above makes no code change.
04-11-2025

Sorry, my above comment is incorrect regarding remove_unshareable_info(). The problem is not with ResolvedFieldEntry::remove_unshareable_info(), but rather the ResolvedFieldEntries that are not cleaned by this function. https://github.com/openjdk/jdk/blob/0f7c0e956e278458e3d875bbda174e3b9e143135/src/hotspot/share/oops/cpCache.cpp#L433-L438 if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { rfi->mark_and_relocate(); /// <--- HERE archived = true; } else { rfi->remove_unshareable_info(); } Note that rfi is allocated from within the metaspace, so originally it contains all zeros. However, sometimes we read a ResolvedFieldEntry from a GrowableArray and store it inside *rfi. If we use the default copy constructor, the copy inside the GrowableArray may have junk in its gaps, and the junk will leak into *rfi. If I remember correctly, this problems is most prominent on Windows. It's probably because Windows likes to use aligned copies to move stack variables into the GrowableArray, taking whatever junk from the stack.
26-08-2025

[~iklam] So my supposition was correct. Padding is very not portable, though one can figure it out from ABI docs. I need to go read about object lifetime again before I can perhaps suggest a way out of this.
26-08-2025

[~kbarrett] The problem with the default copy constructor is it might copy random values from the padding bytes: InstanceKlass* _field_holder; // Field holder klass int _field_offset; // Field offset in bytes u2 _field_index; // Index into field information in holder InstanceKlass u2 _cpool_index; // Constant pool index u1 _tos_state; // TOS state u1 _flags; // Flags: [0000|00|is_final|is_volatile] u1 _get_code, _put_code; // Get and Put bytecodes of the field // 1 padding bytes on 32-bit // 5 padding bytes on 64-bit This will cause failures in test/hotspot/jtreg/runtime/cds/DeterministicDump.java on certain platforms. If you want to use the copy constructors, you need to add the padding bytes by hand and explicitly set them to zero in ResolvedFieldEntry::remove_unshareable_info(). I am not sure if structure padding is compiler-specific or not, so it might be difficult to write portable code.
25-08-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/26098 Date: 2025-07-02 16:29:27 +0000
02-07-2025

The warning is obviously correct; the class involved is not trivially copyable. The first thing I wondered, is why is the class not trivially copyable. It defines a private helper `copy_from` (which just does member-by-member copying, so what default copyiers are spedified to do) and has the copy ctor and assignment operators call it. So why don't we just use the default copy ctor and assignment operator? That is, why isn't remove_unshareable_info something like ``` *this = ResolvedFieldEntry(_cpool_index); ``` with no `memset` at all? Maybe there is some issue with padding vs reproducible builds? But I can't find any discussion of that, either in code comments or in the review of JDK-8293980 (which added `copy_from`). I found no explanation at all for the introduction of `copy_from`. And without more information, it's hard to suggest alternatives.
23-05-2025