C++ Logo

std-proposals

Advanced search

Re: [std-proposals] <type_traits> std::contains_mutable

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Fri, 14 Nov 2025 14:26:08 -0500
On Fri, Nov 14, 2025 at 9:38 AM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:

> [...]
> If we store a 'const' object in a section of memory that later gets
> set read-only, we need to be sure that it is really a const object --
> i.e. it can't contain mutable members.
>
> So I've implemented "std::contains_mutable". Here's my compiler patch
> for GNU g++:
>
> https://github.com/healytpk/gcc-thomas-healy/commit/tag_contains_mutable
> And here it is tested up on GodBolt:
> https://godbolt.org/z/KosvqTETo


The stuff about flash isn't necessary or useful. If there is a rationale
for adding something like `std::contains_mutable(_member)` to the Standard,
it would be because compiler vendors "already have to do it anyway," i.e.
the same rationale we saw for std::is_implicit_lifetime,
std::is_scoped_enum, std::reference_converts_from_temporary, etc. These are
properties that affect what the front-end and/or back-end allow you to do
with a type, so, every compiler must have a way to check for them already.
All the type-trait does is *expose that way* to the end-user.

The property of "(recursively) containing mutable members" is needed by the
compiler in order safely to implement P2752 static storage for braced
initializers
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2752r3.html>.
This was pointed out by Hubert Tong; see §4 of that paper.
The example is:
// https://godbolt.org/z/j8cxfbo39

struct C {
int i_;
};
struct M {
mutable int i_;
};

void fc(std::initializer_list<C> il);
void fm(std::initializer_list<M> il);

void withC() {
static_assert(!__builtin_contains_mutable(C));
fc({{1},{2},{3}});
fc({{1},{2},{3}});
}

void withM() {
static_assert(__builtin_contains_mutable(M));
fm({{1},{2},{3}});
fm({{1},{2},{3}});
}

Notice the difference in codegen between withC() and withM(). `withC()` can
store a single backing array in .rodata and hand to `fc` a view over that
constant backing array. `withM()` needs to copy that backing array onto the
stack, because `fm` is permitted to modify its partially-mutable elements
through its argument `il`. (That is, `withM()` does not benefit from the
optimization newly permitted by P2752.)

So, GCC clearly already has a notion of "contains mutable members." Your
type-trait should just expose that notion to the end-user, and as such,
it's a *plausible* candidate to propose for standardization, IMO.
However, I took a quick look at your GCC patch, and it looks to me as if
you reinvented the wheel there. Why is your patch not (in spirit) a
one-liner? Why are you manually iterating over members and bases? This is
not the implementation I would expect to see, given the above rationale.

It is possible that GCC's existing "contains mutable" logic is not yet
factored out into a simple function (because I bet it's used in only one
place right now). In that case, instead of merely *finding and calling*
that function, you'll need to *factor out* that function as part of your
patch. I have no special knowledge or connections re GCC, but I'd imagine
that a well-written such patch might actually be accepted upstream, if
accompanied by a rationale such as I've given here.

Last I checked, Clang trunk doesn't do P2752's optimization at all. I'm
fairly certain that, if you were to work up a well-written patch for
checking this type-property in *Clang* and submit it, along with the
explanation that this is a (compiler-internal) building block for the
optimization permitted by P2752, it would be graciously accepted. Even more
so if you simultaneously submit a second patch that uses this building
block to *implement* P2752, bringing Clang into parity with GCC.

my $.02,
–Arthur

Received on 2025-11-14 19:26:24