summaryrefslogtreecommitdiff
path: root/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
blob: 3be8dc7749c6f8864ddb3c7fd099eb520daae97c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s

#ifndef NO_ERRORS
namespace bullet3 {
  // the built-in candidates include all of the candidate operator fnuctions
  // [...] that, compared to the given operator

  // - do not have the same parameter-type-list as any non-member candidate

  enum E { e };

  // Suppress both builtin operator<=>(E, E) and operator<(E, E).
  void operator<=>(E, E); // expected-note {{while rewriting}}
  bool cmp = e < e; // expected-error {{invalid operands to binary expression ('void' and 'int')}}

  // None of the other bullets have anything to test here. In principle we
  // need to suppress both builtin operator@(A, B) and operator@(B, A) when we
  // see a user-declared reversible operator@(A, B), and we do, but that's
  // untestable because the only built-in reversible candidates are
  // operator<=>(E, E) and operator==(E, E) for E an enumeration type, and
  // those are both symmetric anyway.
}

namespace bullet4 {
  // The rewritten candidate set is determined as follows:

  template<int> struct X {};
  X<1> x1;
  X<2> x2;

  struct Y {
    int operator<=>(X<2>) = delete; // #1member
    bool operator==(X<2>) = delete; // #2member
  };
  Y y;

  // - For the relational operators, the rewritten candidates include all
  //   non-rewritten candidates for the expression x <=> y.
  int operator<=>(X<1>, X<2>) = delete; // #1

  // expected-note@#1 5{{candidate function has been explicitly deleted}}
  // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}
  bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}}
  bool le = x1 <= x2; // expected-error {{selected deleted operator '<=>'}}
  bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}}
  bool ge = x1 >= x2; // expected-error {{selected deleted operator '<=>'}}
  bool cmp = x1 <=> x2; // expected-error {{selected deleted operator '<=>'}}

  // expected-note@#1member 5{{candidate function has been explicitly deleted}}
  // expected-note@#1 5{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
  // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}
  bool mem_lt = y < x2; // expected-error {{selected deleted operator '<=>'}}
  bool mem_le = y <= x2; // expected-error {{selected deleted operator '<=>'}}
  bool mem_gt = y > x2; // expected-error {{selected deleted operator '<=>'}}
  bool mem_ge = y >= x2; // expected-error {{selected deleted operator '<=>'}}
  bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator '<=>'}}

  // - For the relational and three-way comparison operators, the rewritten
  //   candidates also include a synthesized candidate, with the order of the
  //   two parameters reversed, for each non-rewritten candidate for the
  //   expression y <=> x.

  // expected-note@#1 5{{candidate function (with reversed parameter order) has been explicitly deleted}}
  // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
  bool rlt = x2 < x1; // expected-error {{selected deleted operator '<=>'}}
  bool rle = x2 <= x1; // expected-error {{selected deleted operator '<=>'}}
  bool rgt = x2 > x1; // expected-error {{selected deleted operator '<=>'}}
  bool rge = x2 >= x1; // expected-error {{selected deleted operator '<=>'}}
  bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator '<=>'}}

  // expected-note@#1member 5{{candidate function (with reversed parameter order) has been explicitly deleted}}
  // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
  // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}
  bool mem_rlt = x2 < y; // expected-error {{selected deleted operator '<=>'}}
  bool mem_rle = x2 <= y; // expected-error {{selected deleted operator '<=>'}}
  bool mem_rgt = x2 > y; // expected-error {{selected deleted operator '<=>'}}
  bool mem_rge = x2 >= y; // expected-error {{selected deleted operator '<=>'}}
  bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator '<=>'}}

  // For the != operator, the rewritten candidates include all non-rewritten
  // candidates for the expression x == y
  int operator==(X<1>, X<2>) = delete; // #2

  // expected-note@#2 2{{candidate function has been explicitly deleted}}
  // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}
  bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}}
  bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}}

  // expected-note@#2member 2{{candidate function has been explicitly deleted}}
  // expected-note@#2 2{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
  // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}
  bool mem_eq = y == x2; // expected-error {{selected deleted operator '=='}}
  bool mem_ne = y != x2; // expected-error {{selected deleted operator '=='}}

  // For the equality operators, the rewritten candidates also include a
  // synthesized candidate, with the order of the two parameters reversed, for
  // each non-rewritten candidate for the expression y == x

  // expected-note@#2 2{{candidate function (with reversed parameter order) has been explicitly deleted}}
  // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
  bool req = x2 == x1; // expected-error {{selected deleted operator '=='}}
  bool rne = x2 != x1; // expected-error {{selected deleted operator '=='}}

  // expected-note@#2member 2{{candidate function (with reversed parameter order) has been explicitly deleted}}
  // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
  // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}
  bool mem_req = x2 == y; // expected-error {{selected deleted operator '=='}}
  bool mem_rne = x2 != y; // expected-error {{selected deleted operator '=='}}

  // For all other operators, the rewritten candidate set is empty.
  X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known conversion from 'X<2>' to 'X<1>'}}
  X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}}
}

// Various C++17 cases that are known to be broken by the C++20 rules.
namespace problem_cases {
  // We can have an ambiguity between an operator and its reversed form. This
  // wasn't intended by the original "consistent comparison" proposal, and we
  // allow it as extension, picking the non-reversed form.
  struct A {
    bool operator==(const A&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}
  };
  bool cmp_non_const = A() == A(); // expected-warning {{ambiguous}}

  struct B {
    virtual bool operator==(const B&) const;
  };
  struct D : B {
    bool operator==(const B&) const override; // expected-note {{operator}}
  };
  bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}}

  template<typename T> struct CRTPBase {
    bool operator==(const T&) const; // expected-note {{operator}}
  };
  struct CRTP : CRTPBase<CRTP> {};
  bool cmp_crtp = CRTP() == CRTP(); // expected-warning {{ambiguous}}

  // We can select a non-rewriteable operator== for a != comparison, when there
  // was a viable operator!= candidate we could have used instead.
  //
  // Rejecting this seems OK on balance.
  using UBool = signed char; // ICU uses this.
  struct ICUBase {
    virtual UBool operator==(const ICUBase&) const;
    UBool operator!=(const ICUBase &arg) const { return !operator==(arg); }
  };
  struct ICUDerived : ICUBase {
    UBool operator==(const ICUBase&) const override; // expected-note {{declared here}}
  };
  bool cmp_icu = ICUDerived() != ICUDerived(); // expected-error {{not 'bool'}}
}

#else // NO_ERRORS

namespace problem_cases {
  // We can select a reversed candidate where we used to select a non-reversed
  // one, and in the worst case this can dramatically change the meaning of the
  // program. Make sure we at least warn on the worst cases under -Wall.
  struct iterator;
  struct const_iterator {
    const_iterator(iterator);
    bool operator==(const const_iterator&) const;
  };
  struct iterator {
    bool operator==(const const_iterator &o) const { // expected-warning {{all paths through this function will call itself}}
      return o == *this;
    }
  };
}
#endif // NO_ERRORS