On Thu, Jan 29, 2026 at 9:50 PM Patrick Palka <[email protected]> wrote:

> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> look OK for trunk and ideally backports? The parent libstdc++
> regression PR114865 is present in all release branches, and
> this would allow us to fix it everywhere.
>
> Note that there is a potential library-only solution for
> PR114865, namely adding an empty [[no_unique_address]] data member
> to std::atomic and through its member-initializer sneaking in the
> __builtin_clear_padding call, roughly
>
>   template<typename _Tp>
>     struct atomic
>     {
>       alignas(_S_alignment) _Tp _M_i;
>       [[no_unique_address]] struct __empty { } _M_empty;
>
>       constexpr atomic(_Tp __t)
>       : _M_i(__t),
>         _M_empty(((__atomic_impl::__maybe_has_padding<_Tp>()
>                    && !std::__is_constant_evaluated()
>                    ? __builtin_clear_padding(&_M_i)
>                    : void()), __empty{}))
>       { }
>     };
>
> None of us is confident that adding such a member to atomic is safe
> from an ABI perspective (and e.g. safe from triggering a
> [[no_unique_address]] bug on the release branches).  It seems this
> frontend approach of first relaxing the C++11 constexpr rules is safer
> to backport, all things considered.
>
> -- >8 --
>
> This patch extends our support for C++14 non-empty constexpr
> constructor bodies to C++11, as an extension.  This will make
> it trivial to safely fix the C++11 library regression PR114865
> which requires calling __builtin_clear_padding after initializing
> _M_i in std::atomic's single-parameter constructor, and that's not
> really possible with the C++11 constexpr restrictions.
>
> Since we already desugar member initializers to statements internally
> even in C++11 mode, and so their bodies are already effectively non-empty
> internally, supporting non-empty bodies in user code is mostly a matter
> of relaxing the parse-time error.
>
> But constexpr-ex3.C revealed that by accepting the non-empty body of A's
> constructor, build_data_member_initialization goes on to mistake the
> 'i = _i' assignment as a member-initializer, and we incorrectly accept
> the constructor in C++11 mode (even though it's only valid in C++20).
>
We could enable the missing_mem_inits check in C++11 mode also to address
that, so the warning would be trully for C++14 extensions.

> Turns out this is caused by that function recognizing MODIFY_EXPR only
> in C++11 mode, logic that was last changed by r5-5013 (presumably to
> limit impact of the patch at the time) but I reckon could just be
> removed outright.  This should be safe because the result of
> build_data_member_initialization is only used for rejecting invalid
> constructors (e.g. missing initializers), and actual evaluation uses
> the desugared constructor body.
>
> With this patch we can fix PR114865 by defining the affected
> constructor as (avoding if statements which are still disallowed
> in C++11 constexpr):
>
>   constexpr atomic(_Tp __i) noexcept : _M_i(__i)
>   {
>     (__atomic_impl::__maybe_has_padding<_Tp>()
>      && !std::__is_constant_evaluated()
>      ? __builtin_clear_padding(std::__addressof(_M_i))
>      : void());
>   }
>
> (Note that Clang accepts the code as an extension in C++11 mode as
> well.)
>
>         PR c++/123845
>         PR libstdc++/114865
>
> gcc/cp/ChangeLog:
>
>         * constexpr.cc (build_data_member_initialization): Remove
>         C++11-specific recognition of MODIFY_EXPR.
>         (check_constexpr_ctor_body): Relax error diagnostic to a
>         pedwarn and don't clear DECL_DECLARED_CONSTEXPR_P upon
>         error.  Return true if complaining.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/cpp0x/constexpr-ex3.C: Adjust C++11 non-empty
>         constexpr constructor dg-error to a dg-warning.  Expect
>         a follow-up missing member initializer diagnostic in C++11 mode.
>         * g++.dg/cpp2a/constexpr-try1.C: Expect a follow-up
>         compound-statement in constexpr function diagnostic in C++11
>         mode.
>         * g++.dg/cpp2a/constexpr-try2.C: Likewise.  Adjust C++11
>         non-empty constexpr constructor dg-error to a dg-warning.
>         * g++.dg/cpp2a/constexpr-try3.C:  Adjust C++11 non-empty
>         constexpr constructor dg-error to a dg-warning.
>         * g++.dg/cpp0x/constexpr-ctor23.C: New test.
> ---
>  gcc/cp/constexpr.cc                           | 14 ++++------
>  gcc/testsuite/g++.dg/cpp0x/constexpr-ctor23.C | 26 +++++++++++++++++++
>  gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C    |  3 ++-
>  gcc/testsuite/g++.dg/cpp2a/constexpr-try1.C   |  1 +
>  gcc/testsuite/g++.dg/cpp2a/constexpr-try2.C   |  3 ++-
>  gcc/testsuite/g++.dg/cpp2a/constexpr-try3.C   |  2 +-
>  6 files changed, 37 insertions(+), 12 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-ctor23.C
>
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 1527e9dcbac8..a31fe6b113a9 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -412,11 +412,7 @@ build_data_member_initialization (tree t,
> vec<constructor_elt, va_gc> **vec)
>      }
>    if (TREE_CODE (t) == CONVERT_EXPR)
>      t = TREE_OPERAND (t, 0);
> -  if (TREE_CODE (t) == INIT_EXPR
> -      /* vptr initialization shows up as a MODIFY_EXPR.  In C++14 we only
> -        use what this function builds for cx_check_missing_mem_inits, and
> -        assignment in the ctor body doesn't count.  */
> -      || (cxx_dialect < cxx14 && TREE_CODE (t) == MODIFY_EXPR))
> +  if (TREE_CODE (t) == INIT_EXPR)
>      {
>        member = TREE_OPERAND (t, 0);
>        init = break_out_target_exprs (TREE_OPERAND (t, 1));
> @@ -578,11 +574,11 @@ check_constexpr_ctor_body (tree last, tree list,
> bool complain)
>    else if (list != last
>            && !check_constexpr_ctor_body_1 (last, list))
>      ok = false;
> -  if (!ok)
> +  if (!ok && complain)
>      {
> -      if (complain)
> -       error ("%<constexpr%> constructor does not have empty body");
> -      DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false;
> +      pedwarn (input_location, OPT_Wc__14_extensions,
> +              "%<constexpr%> constructor does not have empty body");
> +      ok = true;
>      }
>    return ok;
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor23.C
> b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor23.C
> new file mode 100644
> index 000000000000..4019804ab166
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor23.C
> @@ -0,0 +1,26 @@
> +// Verify we diagnose and accept, as an extension, a non-empty constexpr
> +// constructor body in C++11 mode.
> +// PR c++/123845
> +// { dg-do compile { target c++11_only } }
> +// { dg-options "" }
> +
> +constexpr int negate(int n) { return -n; }
> +
> +struct A {
> +  int m;
> +  constexpr A() : m(42) {
> +    ++m;
> +    m = negate(m);
> +  } // { dg-warning "does not have empty body \[-Wc++14-extensions\]" }
> +};
> +static_assert(A().m == -43, "");
> +
> +template<class T>
> +struct B {
> +  int m;
> +  constexpr B() : m(42) {
> +    ++m;
> +    m = negate(m);
> +  } // { dg-warning "does not have empty body \[-Wc++14-extensions\]" }
> +};
> +static_assert(B<int>().m == -43, "");
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> index 9d6d5ff587ca..169976afbaf7 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C
> @@ -6,7 +6,8 @@
>  struct A
>  {
>    int i;
> -  constexpr A(int _i) { i = _i; } // { dg-error "empty body|A::i" "" {
> target c++17_down } }
> +  constexpr A(int _i) { i = _i; } // { dg-warning "empty body" "" {
> target c++11_only } }
> +                                 // { dg-error "'A::i' must be init" "" {
> target c++17_down } .-1 }
>  };
>
>  template <class T>
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try1.C
> b/gcc/testsuite/g++.dg/cpp2a/constexpr-try1.C
> index 977eb86dd192..e5e70a62b50f 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try1.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try1.C
> @@ -32,6 +32,7 @@ struct S {
>      } catch (int) {    // { dg-error "compound-statement in 'constexpr'
> function" "" { target c++11_only } }
>      }                  // { dg-error "compound-statement in 'constexpr'
> function" "" { target c++11_only } .-2 }
>    } catch (...) {      // { dg-error "'constexpr' constructor does not
> have empty body" "" { target c++11_only } }
> +                       // { dg-error "compound-statement in 'constexpr'
> function" "" { target c++11_only } .-1 }
>    }
>    int m;
>  };
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try2.C
> b/gcc/testsuite/g++.dg/cpp2a/constexpr-try2.C
> index 7ca7261a9e00..9504fdaa8696 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try2.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try2.C
> @@ -32,7 +32,8 @@ struct S {
>      try {              // { dg-warning "'try' in 'constexpr' function
> only available with" "" { target c++17_down } }
>      } catch (int) {    // { dg-warning "compound-statement in 'constexpr'
> function" "" { target c++11_only } }
>      }                  // { dg-warning "compound-statement in 'constexpr'
> function" "" { target c++11_only } .-2 }
> -  } catch (...) {      // { dg-error "'constexpr' constructor does not
> have empty body" "" { target c++11_only } }
> +  } catch (...) {      // { dg-warning "'constexpr' constructor does not
> have empty body" "" { target c++11_only } }
> +                       // { dg-warning "compound-statement in 'constexpr'
> function" "" { target c++11_only } .-1 }
>    }
>    int m;
>  };
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try3.C
> b/gcc/testsuite/g++.dg/cpp2a/constexpr-try3.C
> index ab7e8f6d4649..070040c5deef 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try3.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try3.C
> @@ -31,7 +31,7 @@ struct S {
>      try {              // { dg-warning "'try' in 'constexpr' function
> only available with" "" { target c++17_down } }
>      } catch (int) {
>      }
> -  } catch (...) {      // { dg-error "'constexpr' constructor does not
> have empty body" "" { target c++11_only } }
> +  } catch (...) {      // { dg-warning "'constexpr' constructor does not
> have empty body" "" { target c++11_only } }
>    }
>    int m;
>  };
> --
> 2.53.0.rc1.65.gea24e2c554
>
>

Reply via email to