Bug 86439 - CTAD with deleted copy constructor fails due to deduction-guide taking by value
Summary: CTAD with deleted copy constructor fails due to deduction-guide taking by value
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 8.1.1
: P3 normal
Target Milestone: 12.0
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 88026 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-07-09 12:00 UTC by Barry Revzin
Modified: 2021-12-08 17:20 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-07-09 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Barry Revzin 2018-07-09 12:00:51 UTC
Reduced from: https://stackoverflow.com/q/51244047/2069064

struct NC {
    NC() = default;
    NC(NC const&) = delete;
    NC& operator=(NC const&) = delete;
};

template <int>
struct C {
    C(NC const&);
};

C(NC) -> C<0>;

int main() {
    NC nc;
    C c(nc);
}

clang accepts. 
gcc rejects this program with:

<source>: In function 'int main()':
<source>:16:11: error: class template argument deduction failed:
     C c(nc);
           ^
<source>:16:11: error: use of deleted function 'NC::NC(const NC&)'
<source>:3:5: note: declared here
     NC(NC const&) = delete;
     ^~
<source>:12:1: note:   initializing argument 1 of 'C(NC)-> C<0>'
 C(NC) -> C<0>;
 ^

But nowhere do we need to copy NC here. CTAD doesn't require invoking the function, just picking the best viable candidate. And once we pick C<0>, actually constructing a C<0> is fine since it doesn't require a copy.
Comment 1 Patrick Palka 2021-06-22 14:25:18 UTC
Another side effect of this is that GCC incorrectly rejects the following use of CTAD + braced-init-lists:

struct B { };
struct C { };

template<class T>
struct A {
  A(T, B);
};

template<class T>
A(T, C) -> A<T>;

A a(0, {});

The problem is that building the call to the deduction guide has the side effect of changing the type of the {} to C, which causes later overload resolution to fail because C isn't convertible to B.
Comment 2 GCC Commits 2021-06-23 12:25:33 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:3eecc1db4c691a87ef4a229d059aa863066d9a1c

commit r12-1744-g3eecc1db4c691a87ef4a229d059aa863066d9a1c
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Jun 23 08:24:34 2021 -0400

    c++: CTAD and deduction guide selection [PR86439]
    
    During CTAD, we select the best viable deduction guide using
    build_new_function_call, which performs overload resolution on the set
    of candidate guides and then forms a call to the guide.  As the PR
    points out, this latter step is unnecessary and occasionally incorrect
    since a call to the selected guide may be ill-formed, or forming the
    call may have side effects such as prematurely deducing the type of a {}.
    
    So this patch introduces a specialized subroutine based on
    build_new_function_call that stops short of building a call to the
    selected function, and makes do_class_deduction use this subroutine
    instead.  And since a call is no longer built, do_class_deduction
    doesn't need to set tf_decltype or cp_unevaluated_operand anymore.
    
    This change causes us to reject some container CTAD examples in the
    libstdc++ testsuite due to deduction failure for {}, which AFAICT is the
    correct behavior.  Previously in e.g. the first removed example
    
      std::map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}},
    
    the type of the {} would get deduced to less<int> as a side effect of
    forming a call to the chosen guide
    
      template<typename _Key, typename _Tp, typename _Compare = less<_Key>,
               typename _Allocator = allocator<pair<const _Key, _Tp>>>
          map(initializer_list<pair<_Key, _Tp>>,
              _Compare = _Compare(), _Allocator = _Allocator())
          -> map<_Key, _Tp, _Compare, _Allocator>;
    
    which made later overload resolution for the constructor call
    unambiguous.  Now, the type of the {} remains undeduced until
    constructor overload resolution, and we complain about ambiguity
    for the two equally good constructor candidates
    
      map(initializer_list<value_type>,
          const _Compare& = _Compare(),
          const allocator_type& = allocator_type())
    
      map(initializer_list<value_type>, const allocator_type&).
    
    This patch fixes these problematic container CTAD examples by giving
    the {} an appropriate concrete type.  Two of these adjusted CTAD
    examples (one for std::set and one for std::multiset) end up triggering
    an unrelated CTAD bug on trunk, PR101174, so these two adjusted examples
    are commented out for now.
    
            PR c++/86439
    
    gcc/cp/ChangeLog:
    
            * call.c (print_error_for_call_failure): Constify 'args' parameter.
            (perform_dguide_overload_resolution): Define.
            * cp-tree.h: (perform_dguide_overload_resolution): Declare.
            * pt.c (do_class_deduction): Use perform_dguide_overload_resolution
            instead of build_new_function_call.  Don't use tf_decltype or
            set cp_unevaluated_operand.  Remove unnecessary NULL_TREE tests.
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/23_containers/map/cons/deduction.cc: Replace ambiguous
            CTAD examples.
            * testsuite/23_containers/multimap/cons/deduction.cc: Likewise.
            * testsuite/23_containers/multiset/cons/deduction.cc: Likewise.
            Mention one of the replaced examples is broken due to PR101174.
            * testsuite/23_containers/set/cons/deduction.cc: Likewise.
            * testsuite/23_containers/unordered_map/cons/deduction.cc: Replace
            ambiguous CTAD examples.
            * testsuite/23_containers/unordered_multimap/cons/deduction.cc:
            Likewise.
            * testsuite/23_containers/unordered_multiset/cons/deduction.cc:
            Likewise.
            * testsuite/23_containers/unordered_set/cons/deduction.cc: Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1z/class-deduction88.C: New test.
            * g++.dg/cpp1z/class-deduction89.C: New test.
            * g++.dg/cpp1z/class-deduction90.C: New test.
Comment 3 Patrick Palka 2021-07-09 19:34:05 UTC
*** Bug 88026 has been marked as a duplicate of this bug. ***
Comment 4 Patrick Palka 2021-09-01 14:13:26 UTC
Fixed for GCC 12