Bug 112908 (lwg3819) - __reference_{converts,constructs}_from_temporary checks for move constructor when binding prvalue to reference of the same type
Summary: __reference_{converts,constructs}_from_temporary checks for move constructor ...
Status: RESOLVED FIXED
Alias: lwg3819
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: ---
Assignee: Marek Polacek
URL:
Keywords: accepts-invalid, wrong-code
Depends on:
Blocks:
 
Reported: 2023-12-07 21:51 UTC by Mital Ashok
Modified: 2026-07-23 13:56 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-12-20 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mital Ashok 2023-12-07 21:51:58 UTC
The following compiles on trunk:

    struct NonMovable {
      NonMovable() = default;
      NonMovable(NonMovable&&) = delete;
    };

    static_assert( __reference_converts_from_temporary(int&&, int));
    static_assert(!__reference_converts_from_temporary(NonMovable&&, NonMovable));
    static_assert( __reference_constructs_from_temporary(int&&, int));
    static_assert(!__reference_constructs_from_temporary(NonMovable&&, NonMovable));

When the NonMovable assertions should fail because there *is* a temporary being bound to a reference (in exactly the same way as the int case).

We can observe it instantiating constructors that shouldn't be too:

    template<typename T>
    struct S {
      template<typename U = T>
      S(const S&&) noexcept(U()) {}
    };

    static_assert(__reference_converts_from_temporary(const S<bool>&&, const S<bool>));
    static_assert(__reference_converts_from_temporary(const S<void*>&&, const S<void*>));

Which complains about a conversion from void* to bool even though constructors shouldn't be looked at.
Comment 1 Patrick Palka 2023-12-20 16:13:36 UTC
This seems to be https://cplusplus.github.io/LWG/issue3819
Comment 2 Tomasz Kamiński 2025-10-15 14:42:10 UTC
The issue was already resolved and voted into standard.
Comment 3 Marek Polacek 2026-07-20 20:58:35 UTC
I've taken a look and may have a WIP patch.

I *think* the problem is that we aren't implementing this bullet properly:
Otherwise, VAL<T> is a prvalue that initially has type T.
because ref_xes_from_temporary uses build_trait_object + force_rvalue/rvalue, but that actually gets us an xvalue.  Every xvalue is an rvalue so the functions work correctly, but here we want a prvalue so that copy elision works.  We currently do not seem to have a helper function to get a prvalue given a type T.
Comment 4 GCC Commits 2026-07-23 13:52:28 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:83a903f339af9a1ecde675261191cc69e7b1b721

commit r17-2665-g83a903f339af9a1ecde675261191cc69e7b1b721
Author: Marek Polacek <polacek@redhat.com>
Date:   Tue Jul 21 12:59:40 2026 -0400

    c++: implement LWG 3819, reference_xes_from_temporary [PR112908]
    
    This is an attempt to implement <https://cplusplus.github.io/LWG/issue3819>.
    
    My understanding of this issue is that previously, ref_xes_from_temporary
    was defined by using is_constructible, which is implemented by seeing
    if
      T t(declval<Args>()...);
    is well-formed.  But declval always yields an xvalue, never a prvalue.
    In practice this means that for
    
        struct U {
          U();
          U(U&&) = delete;
        };
    
        struct T {
          T(U);
        };
    
    reference_constructs_from_temporary_v<const T&, U> is false due
    to the deleted move ctor.  But if we have a prvalue, then the
    call to the move ctor should be elided and so it doesn't matter
    that it's deleted.  So the result should be 'true'.
    
    Our ref_xes_from_temporary already doesn't check is_constructible<T, U>
    as the comment says, but we always use build_trait_object which
    gives us an xvalue.  What we need is to implement [meta.unary.prop]/5.2:
    Otherwise [not a reference or function type], VAL<T> is a prvalue that
    initially has type T.  For this I've added build_prvalue_trait_object.
    
    The finish_trait_expr change is so that get_target_expr doesn't crash
    on an incomplete type.  This change should be correct since
    https://cplusplus.github.io/LWG/issue2939 didn't adjust
    reference_xes_from_temporary the same way as is_convertible/constructible.
    
            PR c++/112908
    
    gcc/cp/ChangeLog:
    
            * method.cc (build_prvalue_trait_object): New.
            (ref_xes_from_temporary): Use build_prvalue_trait_object.
            Use deferring_access_check_sentinel and cp_unevaluated.  Don't
            call force_rvalue or rvalue.
            * semantics.cc (finish_trait_expr)
            <case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY>: Actually check
            completeness.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ext/is_constructible8.C: Move
            __reference_*_from_temporary testing to a new test.
            * g++.dg/ext/reference_xes_from_temporary2.C: New test.
            * g++.dg/ext/reference_xes_from_temporary3.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 5 Marek Polacek 2026-07-23 13:56:04 UTC
Should be fixed.