]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
1 | //! Code related to processing overloaded binary and unary operators. |
2 | ||
7cac9316 | 3 | use super::method::MethodCallee; |
17df50a5 | 4 | use super::{has_expected_num_generic_args, FnCtxt}; |
29967ef6 | 5 | use rustc_ast as ast; |
ee023bcb | 6 | use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; |
dfeec247 | 7 | use rustc_hir as hir; |
74b04a01 | 8 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; |
ba9703b0 XL |
9 | use rustc_middle::ty::adjustment::{ |
10 | Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, | |
11 | }; | |
f035d41b | 12 | use rustc_middle::ty::fold::TypeFolder; |
ba9703b0 | 13 | use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; |
f035d41b XL |
14 | use rustc_middle::ty::{ |
15 | self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor, | |
16 | }; | |
29967ef6 | 17 | use rustc_span::source_map::Spanned; |
3dfed10e | 18 | use rustc_span::symbol::{sym, Ident}; |
dfeec247 | 19 | use rustc_span::Span; |
ba9703b0 | 20 | use rustc_trait_selection::infer::InferCtxtExt; |
c295e0f8 | 21 | use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; |
60c5eb7d | 22 | |
29967ef6 XL |
23 | use std::ops::ControlFlow; |
24 | ||
dc9dc135 | 25 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
9fa01778 | 26 | /// Checks a `a <op>= b` |
dc9dc135 XL |
27 | pub fn check_binop_assign( |
28 | &self, | |
dfeec247 | 29 | expr: &'tcx hir::Expr<'tcx>, |
dc9dc135 | 30 | op: hir::BinOp, |
dfeec247 XL |
31 | lhs: &'tcx hir::Expr<'tcx>, |
32 | rhs: &'tcx hir::Expr<'tcx>, | |
dc9dc135 | 33 | ) -> Ty<'tcx> { |
abe05a73 | 34 | let (lhs_ty, rhs_ty, return_ty) = |
dfeec247 | 35 | self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes); |
a7813a04 | 36 | |
dfeec247 XL |
37 | let ty = |
38 | if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { | |
ee023bcb | 39 | self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); |
dfeec247 XL |
40 | self.tcx.mk_unit() |
41 | } else { | |
42 | return_ty | |
43 | }; | |
44 | ||
3c0e092e | 45 | self.check_lhs_assignable(lhs, "E0067", op.span); |
c34b1796 | 46 | |
9e0c209e | 47 | ty |
c34b1796 | 48 | } |
c34b1796 | 49 | |
9fa01778 | 50 | /// Checks a potentially overloaded binary operator. |
dc9dc135 XL |
51 | pub fn check_binop( |
52 | &self, | |
dfeec247 | 53 | expr: &'tcx hir::Expr<'tcx>, |
dc9dc135 | 54 | op: hir::BinOp, |
dfeec247 XL |
55 | lhs_expr: &'tcx hir::Expr<'tcx>, |
56 | rhs_expr: &'tcx hir::Expr<'tcx>, | |
dc9dc135 | 57 | ) -> Ty<'tcx> { |
a7813a04 XL |
58 | let tcx = self.tcx; |
59 | ||
dfeec247 XL |
60 | debug!( |
61 | "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})", | |
62 | expr.hir_id, expr, op, lhs_expr, rhs_expr | |
63 | ); | |
a7813a04 | 64 | |
a7813a04 XL |
65 | match BinOpCategory::from(op) { |
66 | BinOpCategory::Shortcircuit => { | |
67 | // && and || are a simple case. | |
f035d41b | 68 | self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); |
476ff2be | 69 | let lhs_diverges = self.diverges.get(); |
f035d41b | 70 | self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); |
476ff2be SL |
71 | |
72 | // Depending on the LHS' value, the RHS can never execute. | |
73 | self.diverges.set(lhs_diverges); | |
74 | ||
abe05a73 | 75 | tcx.types.bool |
c34b1796 | 76 | } |
a7813a04 XL |
77 | _ => { |
78 | // Otherwise, we always treat operators as if they are | |
79 | // overloaded. This is the way to be most flexible w/r/t | |
80 | // types that get inferred. | |
abe05a73 | 81 | let (lhs_ty, rhs_ty, return_ty) = |
dfeec247 | 82 | self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No); |
a7813a04 XL |
83 | |
84 | // Supply type inference hints if relevant. Probably these | |
85 | // hints should be enforced during select as part of the | |
86 | // `consider_unification_despite_ambiguity` routine, but this | |
87 | // more convenient for now. | |
88 | // | |
89 | // The basic idea is to help type inference by taking | |
90 | // advantage of things we know about how the impls for | |
91 | // scalar types are arranged. This is important in a | |
92 | // scenario like `1_u32 << 2`, because it lets us quickly | |
93 | // deduce that the result type should be `u32`, even | |
94 | // though we don't know yet what type 2 has and hence | |
95 | // can't pin this down to a specific impl. | |
dfeec247 XL |
96 | if !lhs_ty.is_ty_var() |
97 | && !rhs_ty.is_ty_var() | |
98 | && is_builtin_binop(lhs_ty, rhs_ty, op) | |
a7813a04 | 99 | { |
74b04a01 | 100 | let builtin_return_ty = self.enforce_builtin_binop_types( |
ee023bcb | 101 | lhs_expr.span, |
74b04a01 | 102 | lhs_ty, |
ee023bcb | 103 | rhs_expr.span, |
74b04a01 XL |
104 | rhs_ty, |
105 | op, | |
106 | ); | |
a7813a04 XL |
107 | self.demand_suptype(expr.span, builtin_return_ty, return_ty); |
108 | } | |
c34b1796 | 109 | |
9e0c209e | 110 | return_ty |
a7813a04 | 111 | } |
c34b1796 AL |
112 | } |
113 | } | |
c34b1796 | 114 | |
dc9dc135 XL |
115 | fn enforce_builtin_binop_types( |
116 | &self, | |
ee023bcb | 117 | lhs_span: Span, |
dc9dc135 | 118 | lhs_ty: Ty<'tcx>, |
ee023bcb | 119 | rhs_span: Span, |
dc9dc135 XL |
120 | rhs_ty: Ty<'tcx>, |
121 | op: hir::BinOp, | |
122 | ) -> Ty<'tcx> { | |
a7813a04 XL |
123 | debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); |
124 | ||
74b04a01 XL |
125 | // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. |
126 | // (See https://github.com/rust-lang/rust/issues/57447.) | |
127 | let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); | |
128 | ||
a7813a04 XL |
129 | let tcx = self.tcx; |
130 | match BinOpCategory::from(op) { | |
131 | BinOpCategory::Shortcircuit => { | |
ee023bcb FG |
132 | self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); |
133 | self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); | |
f9f354fc | 134 | tcx.types.bool |
a7813a04 | 135 | } |
c34b1796 | 136 | |
a7813a04 XL |
137 | BinOpCategory::Shift => { |
138 | // result type is same as LHS always | |
139 | lhs_ty | |
140 | } | |
c34b1796 | 141 | |
dfeec247 | 142 | BinOpCategory::Math | BinOpCategory::Bitwise => { |
a7813a04 | 143 | // both LHS and RHS and result will have the same type |
ee023bcb | 144 | self.demand_suptype(rhs_span, lhs_ty, rhs_ty); |
a7813a04 XL |
145 | lhs_ty |
146 | } | |
c34b1796 | 147 | |
a7813a04 XL |
148 | BinOpCategory::Comparison => { |
149 | // both LHS and RHS and result will have the same type | |
ee023bcb | 150 | self.demand_suptype(rhs_span, lhs_ty, rhs_ty); |
f9f354fc | 151 | tcx.types.bool |
a7813a04 | 152 | } |
c34b1796 AL |
153 | } |
154 | } | |
c34b1796 | 155 | |
dc9dc135 XL |
156 | fn check_overloaded_binop( |
157 | &self, | |
dfeec247 XL |
158 | expr: &'tcx hir::Expr<'tcx>, |
159 | lhs_expr: &'tcx hir::Expr<'tcx>, | |
160 | rhs_expr: &'tcx hir::Expr<'tcx>, | |
dc9dc135 XL |
161 | op: hir::BinOp, |
162 | is_assign: IsAssign, | |
163 | ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { | |
dfeec247 XL |
164 | debug!( |
165 | "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", | |
166 | expr.hir_id, op, is_assign | |
167 | ); | |
a7813a04 | 168 | |
74d20737 XL |
169 | let lhs_ty = match is_assign { |
170 | IsAssign::No => { | |
171 | // Find a suitable supertype of the LHS expression's type, by coercing to | |
172 | // a type variable, to pass as the `Self` to the trait, avoiding invariant | |
173 | // trait matching creating lifetime constraints that are too strict. | |
0731742a | 174 | // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result |
74d20737 | 175 | // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. |
f035d41b | 176 | let lhs_ty = self.check_expr(lhs_expr); |
dc9dc135 XL |
177 | let fresh_var = self.next_ty_var(TypeVariableOrigin { |
178 | kind: TypeVariableOriginKind::MiscVariable, | |
179 | span: lhs_expr.span, | |
180 | }); | |
f035d41b | 181 | self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) |
74d20737 XL |
182 | } |
183 | IsAssign::Yes => { | |
184 | // rust-lang/rust#52126: We have to use strict | |
185 | // equivalence on the LHS of an assign-op like `+=`; | |
186 | // overwritten or mutably-borrowed places cannot be | |
187 | // coerced to a supertype. | |
f035d41b | 188 | self.check_expr(lhs_expr) |
74d20737 | 189 | } |
abe05a73 | 190 | }; |
e74abb32 | 191 | let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); |
abe05a73 | 192 | |
0731742a | 193 | // N.B., as we have not yet type-checked the RHS, we don't have the |
a7813a04 XL |
194 | // type at hand. Make a variable to represent it. The whole reason |
195 | // for this indirection is so that, below, we can check the expr | |
196 | // using this variable as the expected type, which sometimes lets | |
197 | // us do better coercions than we would be able to do otherwise, | |
198 | // particularly for things like `String + &String`. | |
dc9dc135 XL |
199 | let rhs_ty_var = self.next_ty_var(TypeVariableOrigin { |
200 | kind: TypeVariableOriginKind::MiscVariable, | |
201 | span: rhs_expr.span, | |
202 | }); | |
a7813a04 | 203 | |
ee023bcb FG |
204 | let result = self.lookup_op_method( |
205 | lhs_ty, | |
206 | Some(rhs_ty_var), | |
207 | Some(rhs_expr), | |
208 | Op::Binary(op, is_assign), | |
209 | ); | |
8bb4bdeb XL |
210 | |
211 | // see `NB` above | |
f035d41b | 212 | let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); |
e74abb32 | 213 | let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); |
8bb4bdeb | 214 | |
7cac9316 XL |
215 | let return_ty = match result { |
216 | Ok(method) => { | |
217 | let by_ref_binop = !op.node.is_by_value(); | |
218 | if is_assign == IsAssign::Yes || by_ref_binop { | |
1b1a35ee | 219 | if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { |
94b46f34 | 220 | let mutbl = match mutbl { |
dfeec247 XL |
221 | hir::Mutability::Not => AutoBorrowMutability::Not, |
222 | hir::Mutability::Mut => AutoBorrowMutability::Mut { | |
0531ce1d XL |
223 | // Allow two-phase borrows for binops in initial deployment |
224 | // since they desugar to methods | |
83c7162d | 225 | allow_two_phase_borrow: AllowTwoPhase::Yes, |
dfeec247 | 226 | }, |
2c00a5a8 | 227 | }; |
7cac9316 | 228 | let autoref = Adjustment { |
5099ac24 | 229 | kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), |
dfeec247 | 230 | target: method.sig.inputs()[0], |
7cac9316 XL |
231 | }; |
232 | self.apply_adjustments(lhs_expr, vec![autoref]); | |
233 | } | |
234 | } | |
235 | if by_ref_binop { | |
1b1a35ee | 236 | if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { |
94b46f34 | 237 | let mutbl = match mutbl { |
dfeec247 XL |
238 | hir::Mutability::Not => AutoBorrowMutability::Not, |
239 | hir::Mutability::Mut => AutoBorrowMutability::Mut { | |
0531ce1d XL |
240 | // Allow two-phase borrows for binops in initial deployment |
241 | // since they desugar to methods | |
83c7162d | 242 | allow_two_phase_borrow: AllowTwoPhase::Yes, |
dfeec247 | 243 | }, |
2c00a5a8 | 244 | }; |
7cac9316 | 245 | let autoref = Adjustment { |
5099ac24 | 246 | kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), |
dfeec247 | 247 | target: method.sig.inputs()[1], |
7cac9316 XL |
248 | }; |
249 | // HACK(eddyb) Bypass checks due to reborrows being in | |
250 | // some cases applied on the RHS, on top of which we need | |
251 | // to autoref, which is not allowed by apply_adjustments. | |
252 | // self.apply_adjustments(rhs_expr, vec![autoref]); | |
3dfed10e | 253 | self.typeck_results |
3b2f2976 XL |
254 | .borrow_mut() |
255 | .adjustments_mut() | |
256 | .entry(rhs_expr.hir_id) | |
b7449926 | 257 | .or_default() |
3b2f2976 | 258 | .push(autoref); |
7cac9316 XL |
259 | } |
260 | } | |
3b2f2976 | 261 | self.write_method_call(expr.hir_id, method); |
7cac9316 XL |
262 | |
263 | method.sig.output() | |
264 | } | |
f035d41b | 265 | // error types are considered "builtin" |
c295e0f8 XL |
266 | Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), |
267 | Err(errors) => { | |
f035d41b | 268 | let source_map = self.tcx.sess.source_map(); |
c295e0f8 | 269 | let (mut err, missing_trait, use_output) = match is_assign { |
f035d41b XL |
270 | IsAssign::Yes => { |
271 | let mut err = struct_span_err!( | |
272 | self.tcx.sess, | |
273 | expr.span, | |
274 | E0368, | |
275 | "binary assignment operation `{}=` cannot be applied to type `{}`", | |
276 | op.node.as_str(), | |
277 | lhs_ty, | |
278 | ); | |
279 | err.span_label( | |
280 | lhs_expr.span, | |
281 | format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), | |
282 | ); | |
283 | let missing_trait = match op.node { | |
284 | hir::BinOpKind::Add => Some("std::ops::AddAssign"), | |
285 | hir::BinOpKind::Sub => Some("std::ops::SubAssign"), | |
286 | hir::BinOpKind::Mul => Some("std::ops::MulAssign"), | |
287 | hir::BinOpKind::Div => Some("std::ops::DivAssign"), | |
288 | hir::BinOpKind::Rem => Some("std::ops::RemAssign"), | |
289 | hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"), | |
290 | hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"), | |
291 | hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"), | |
292 | hir::BinOpKind::Shl => Some("std::ops::ShlAssign"), | |
293 | hir::BinOpKind::Shr => Some("std::ops::ShrAssign"), | |
294 | _ => None, | |
295 | }; | |
c295e0f8 XL |
296 | self.note_unmet_impls_on_type(&mut err, errors); |
297 | (err, missing_trait, false) | |
f035d41b XL |
298 | } |
299 | IsAssign::No => { | |
300 | let (message, missing_trait, use_output) = match op.node { | |
301 | hir::BinOpKind::Add => ( | |
ee023bcb | 302 | format!("cannot add `{rhs_ty}` to `{lhs_ty}`"), |
f035d41b XL |
303 | Some("std::ops::Add"), |
304 | true, | |
305 | ), | |
306 | hir::BinOpKind::Sub => ( | |
ee023bcb | 307 | format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"), |
f035d41b XL |
308 | Some("std::ops::Sub"), |
309 | true, | |
310 | ), | |
311 | hir::BinOpKind::Mul => ( | |
ee023bcb | 312 | format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"), |
f035d41b XL |
313 | Some("std::ops::Mul"), |
314 | true, | |
315 | ), | |
316 | hir::BinOpKind::Div => ( | |
ee023bcb | 317 | format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"), |
f035d41b XL |
318 | Some("std::ops::Div"), |
319 | true, | |
320 | ), | |
321 | hir::BinOpKind::Rem => ( | |
ee023bcb | 322 | format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"), |
f035d41b XL |
323 | Some("std::ops::Rem"), |
324 | true, | |
325 | ), | |
326 | hir::BinOpKind::BitAnd => ( | |
ee023bcb | 327 | format!("no implementation for `{lhs_ty} & {rhs_ty}`"), |
f035d41b XL |
328 | Some("std::ops::BitAnd"), |
329 | true, | |
330 | ), | |
331 | hir::BinOpKind::BitXor => ( | |
ee023bcb | 332 | format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"), |
f035d41b XL |
333 | Some("std::ops::BitXor"), |
334 | true, | |
335 | ), | |
336 | hir::BinOpKind::BitOr => ( | |
ee023bcb | 337 | format!("no implementation for `{lhs_ty} | {rhs_ty}`"), |
f035d41b XL |
338 | Some("std::ops::BitOr"), |
339 | true, | |
340 | ), | |
341 | hir::BinOpKind::Shl => ( | |
ee023bcb | 342 | format!("no implementation for `{lhs_ty} << {rhs_ty}`"), |
f035d41b XL |
343 | Some("std::ops::Shl"), |
344 | true, | |
345 | ), | |
346 | hir::BinOpKind::Shr => ( | |
ee023bcb | 347 | format!("no implementation for `{lhs_ty} >> {rhs_ty}`"), |
f035d41b XL |
348 | Some("std::ops::Shr"), |
349 | true, | |
350 | ), | |
351 | hir::BinOpKind::Eq | hir::BinOpKind::Ne => ( | |
352 | format!( | |
353 | "binary operation `{}` cannot be applied to type `{}`", | |
354 | op.node.as_str(), | |
355 | lhs_ty | |
356 | ), | |
357 | Some("std::cmp::PartialEq"), | |
358 | false, | |
359 | ), | |
360 | hir::BinOpKind::Lt | |
361 | | hir::BinOpKind::Le | |
362 | | hir::BinOpKind::Gt | |
363 | | hir::BinOpKind::Ge => ( | |
364 | format!( | |
365 | "binary operation `{}` cannot be applied to type `{}`", | |
366 | op.node.as_str(), | |
367 | lhs_ty | |
368 | ), | |
369 | Some("std::cmp::PartialOrd"), | |
370 | false, | |
371 | ), | |
372 | _ => ( | |
373 | format!( | |
374 | "binary operation `{}` cannot be applied to type `{}`", | |
375 | op.node.as_str(), | |
376 | lhs_ty | |
377 | ), | |
378 | None, | |
379 | false, | |
380 | ), | |
381 | }; | |
382 | let mut err = | |
383 | struct_span_err!(self.tcx.sess, op.span, E0369, "{}", message.as_str()); | |
f035d41b | 384 | if !lhs_expr.span.eq(&rhs_expr.span) { |
c295e0f8 | 385 | self.add_type_neq_err_label( |
f035d41b XL |
386 | &mut err, |
387 | lhs_expr.span, | |
b7449926 | 388 | lhs_ty, |
f035d41b | 389 | rhs_ty, |
ee023bcb | 390 | rhs_expr, |
f035d41b XL |
391 | op, |
392 | is_assign, | |
b7449926 | 393 | ); |
c295e0f8 | 394 | self.add_type_neq_err_label( |
f035d41b XL |
395 | &mut err, |
396 | rhs_expr.span, | |
397 | rhs_ty, | |
398 | lhs_ty, | |
ee023bcb | 399 | lhs_expr, |
f035d41b XL |
400 | op, |
401 | is_assign, | |
b7449926 | 402 | ); |
32a655c1 | 403 | } |
c295e0f8 XL |
404 | self.note_unmet_impls_on_type(&mut err, errors); |
405 | (err, missing_trait, use_output) | |
f035d41b XL |
406 | } |
407 | }; | |
1b1a35ee | 408 | if let Ref(_, rty, _) = lhs_ty.kind() { |
5099ac24 | 409 | if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) |
ee023bcb FG |
410 | && self |
411 | .lookup_op_method( | |
412 | *rty, | |
413 | Some(rhs_ty), | |
414 | Some(rhs_expr), | |
415 | Op::Binary(op, is_assign), | |
416 | ) | |
417 | .is_ok() | |
3c0e092e | 418 | { |
f035d41b XL |
419 | if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { |
420 | let msg = &format!( | |
421 | "`{}{}` can be used on `{}`, you can dereference `{}`", | |
422 | op.node.as_str(), | |
423 | match is_assign { | |
424 | IsAssign::Yes => "=", | |
425 | IsAssign::No => "", | |
426 | }, | |
427 | rty.peel_refs(), | |
428 | lstring, | |
dfeec247 | 429 | ); |
f035d41b XL |
430 | err.span_suggestion_verbose( |
431 | lhs_expr.span.shrink_to_lo(), | |
432 | msg, | |
433 | "*".to_string(), | |
434 | rustc_errors::Applicability::MachineApplicable, | |
435 | ); | |
f035d41b XL |
436 | } |
437 | } | |
438 | } | |
439 | if let Some(missing_trait) = missing_trait { | |
5099ac24 | 440 | let mut visitor = TypeParamVisitor(vec![]); |
f035d41b XL |
441 | visitor.visit_ty(lhs_ty); |
442 | ||
443 | if op.node == hir::BinOpKind::Add | |
444 | && self.check_str_addition( | |
445 | lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, | |
446 | ) | |
447 | { | |
448 | // This has nothing here because it means we did string | |
449 | // concatenation (e.g., "Hello " + "World!"). This means | |
450 | // we don't want the note in the else clause to be emitted | |
5099ac24 | 451 | } else if let [ty] = &visitor.0[..] { |
1b1a35ee | 452 | if let ty::Param(p) = *ty.kind() { |
f035d41b XL |
453 | // Check if the method would be found if the type param wasn't |
454 | // involved. If so, it means that adding a trait bound to the param is | |
455 | // enough. Otherwise we do not give the suggestion. | |
c295e0f8 | 456 | let mut eraser = TypeParamEraser(self, expr.span); |
f035d41b XL |
457 | let needs_bound = self |
458 | .lookup_op_method( | |
459 | eraser.fold_ty(lhs_ty), | |
ee023bcb FG |
460 | Some(eraser.fold_ty(rhs_ty)), |
461 | Some(rhs_expr), | |
f035d41b XL |
462 | Op::Binary(op, is_assign), |
463 | ) | |
464 | .is_ok(); | |
465 | if needs_bound { | |
466 | suggest_constraining_param( | |
467 | self.tcx, | |
468 | self.body_id, | |
48663c56 | 469 | &mut err, |
5099ac24 | 470 | *ty, |
48663c56 | 471 | rhs_ty, |
f035d41b XL |
472 | missing_trait, |
473 | p, | |
474 | use_output, | |
48663c56 | 475 | ); |
f035d41b XL |
476 | } else if *ty != lhs_ty { |
477 | // When we know that a missing bound is responsible, we don't show | |
478 | // this note as it is redundant. | |
479 | err.note(&format!( | |
ee023bcb | 480 | "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" |
f035d41b | 481 | )); |
532ac7d7 | 482 | } |
f035d41b | 483 | } else { |
1b1a35ee | 484 | bug!("type param visitor stored a non type param: {:?}", ty.kind()); |
a7813a04 | 485 | } |
b039eaaf SL |
486 | } |
487 | } | |
f035d41b XL |
488 | err.emit(); |
489 | self.tcx.ty_error() | |
c34b1796 | 490 | } |
a7813a04 | 491 | }; |
c34b1796 | 492 | |
abe05a73 | 493 | (lhs_ty, rhs_ty, return_ty) |
a7813a04 | 494 | } |
c34b1796 | 495 | |
48663c56 | 496 | /// If one of the types is an uncalled function and calling it would yield the other type, |
ba9703b0 | 497 | /// suggest calling the function. Returns `true` if suggestion would apply (even if not given). |
48663c56 XL |
498 | fn add_type_neq_err_label( |
499 | &self, | |
ee023bcb | 500 | err: &mut Diagnostic, |
48663c56 XL |
501 | span: Span, |
502 | ty: Ty<'tcx>, | |
503 | other_ty: Ty<'tcx>, | |
ee023bcb | 504 | other_expr: &'tcx hir::Expr<'tcx>, |
48663c56 XL |
505 | op: hir::BinOp, |
506 | is_assign: IsAssign, | |
3c0e092e | 507 | ) -> bool /* did we suggest to call a function because of missing parentheses? */ { |
48663c56 | 508 | err.span_label(span, ty.to_string()); |
1b1a35ee | 509 | if let FnDef(def_id, _) = *ty.kind() { |
3dfed10e | 510 | if !self.tcx.has_typeck_results(def_id) { |
48663c56 XL |
511 | return false; |
512 | } | |
fc512014 XL |
513 | // FIXME: Instead of exiting early when encountering bound vars in |
514 | // the function signature, consider keeping the binder here and | |
515 | // propagating it downwards. | |
3c0e092e | 516 | let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else { |
fc512014 XL |
517 | return false; |
518 | }; | |
48663c56 | 519 | |
1b1a35ee | 520 | let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { |
3dfed10e | 521 | if !self.tcx.has_typeck_results(def_id) { |
48663c56 XL |
522 | return false; |
523 | } | |
ba9703b0 XL |
524 | // We're emitting a suggestion, so we can just ignore regions |
525 | self.tcx.fn_sig(def_id).skip_binder().output() | |
48663c56 XL |
526 | } else { |
527 | other_ty | |
528 | }; | |
529 | ||
dfeec247 | 530 | if self |
ee023bcb FG |
531 | .lookup_op_method( |
532 | fn_sig.output(), | |
533 | Some(other_ty), | |
534 | Some(other_expr), | |
535 | Op::Binary(op, is_assign), | |
536 | ) | |
dfeec247 XL |
537 | .is_ok() |
538 | { | |
a2a8927a XL |
539 | let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { |
540 | ("( /* arguments */ )".to_string(), Applicability::HasPlaceholders) | |
541 | } else { | |
542 | ("()".to_string(), Applicability::MaybeIncorrect) | |
543 | }; | |
48663c56 | 544 | |
a2a8927a XL |
545 | err.span_suggestion_verbose( |
546 | span.shrink_to_hi(), | |
547 | "you might have forgotten to call this function", | |
548 | variable_snippet, | |
549 | applicability, | |
550 | ); | |
48663c56 XL |
551 | return true; |
552 | } | |
553 | } | |
554 | false | |
555 | } | |
556 | ||
557 | /// Provide actionable suggestions when trying to add two strings with incorrect types, | |
558 | /// like `&str + &str`, `String + String` and `&str + &String`. | |
559 | /// | |
560 | /// If this function returns `true` it means a note was printed, so we don't need | |
561 | /// to print the normal "implementation of `std::ops::Add` might be missing" note | |
8faf50e0 XL |
562 | fn check_str_addition( |
563 | &self, | |
dfeec247 XL |
564 | lhs_expr: &'tcx hir::Expr<'tcx>, |
565 | rhs_expr: &'tcx hir::Expr<'tcx>, | |
8faf50e0 XL |
566 | lhs_ty: Ty<'tcx>, |
567 | rhs_ty: Ty<'tcx>, | |
ee023bcb | 568 | err: &mut Diagnostic, |
f035d41b | 569 | is_assign: IsAssign, |
532ac7d7 | 570 | op: hir::BinOp, |
8faf50e0 | 571 | ) -> bool { |
5099ac24 FG |
572 | let str_concat_note = "string concatenation requires an owned `String` on the left"; |
573 | let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; | |
574 | let to_owned_msg = "create an owned `String` from a string reference"; | |
48663c56 | 575 | |
c295e0f8 | 576 | let string_type = self.tcx.get_diagnostic_item(sym::String); |
3dfed10e | 577 | let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() { |
ee023bcb | 578 | Some(ty_def) => Some(ty_def.did()) == string_type, |
3dfed10e XL |
579 | None => false, |
580 | }; | |
48663c56 | 581 | |
1b1a35ee | 582 | match (lhs_ty.kind(), rhs_ty.kind()) { |
48663c56 | 583 | (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str |
1b1a35ee XL |
584 | if (*l_ty.kind() == Str || is_std_string(l_ty)) && ( |
585 | *r_ty.kind() == Str || is_std_string(r_ty) || | |
48663c56 XL |
586 | &format!("{:?}", rhs_ty) == "&&str" |
587 | ) => | |
588 | { | |
f035d41b | 589 | if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` |
5099ac24 FG |
590 | err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); |
591 | err.note(str_concat_note); | |
592 | if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { | |
593 | err.span_suggestion_verbose( | |
594 | lhs_expr.span.until(lhs_inner_expr.span), | |
595 | rm_borrow_msg, | |
596 | "".to_owned(), | |
597 | Applicability::MachineApplicable | |
598 | ); | |
599 | } else { | |
600 | err.span_suggestion_verbose( | |
601 | lhs_expr.span.shrink_to_hi(), | |
602 | to_owned_msg, | |
603 | ".to_owned()".to_owned(), | |
604 | Applicability::MachineApplicable | |
605 | ); | |
606 | } | |
8faf50e0 | 607 | } |
0531ce1d | 608 | true |
8bb4bdeb | 609 | } |
48663c56 | 610 | (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` |
1b1a35ee | 611 | if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => |
48663c56 XL |
612 | { |
613 | err.span_label( | |
614 | op.span, | |
615 | "`+` cannot be used to concatenate a `&str` with a `String`", | |
616 | ); | |
5099ac24 FG |
617 | match is_assign { |
618 | IsAssign::No => { | |
619 | let sugg_msg; | |
620 | let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { | |
621 | sugg_msg = "remove the borrow on the left and add one on the right"; | |
622 | (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned()) | |
48663c56 | 623 | } else { |
5099ac24 FG |
624 | sugg_msg = "create an owned `String` on the left and add a borrow on the right"; |
625 | (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned()) | |
48663c56 | 626 | }; |
5099ac24 FG |
627 | let suggestions = vec![ |
628 | lhs_sugg, | |
629 | (rhs_expr.span.shrink_to_lo(), "&".to_owned()), | |
630 | ]; | |
631 | err.multipart_suggestion_verbose( | |
632 | sugg_msg, | |
633 | suggestions, | |
0bf4aa26 XL |
634 | Applicability::MachineApplicable, |
635 | ); | |
8faf50e0 | 636 | } |
5099ac24 FG |
637 | IsAssign::Yes => { |
638 | err.note(str_concat_note); | |
0531ce1d | 639 | } |
5099ac24 | 640 | } |
0531ce1d XL |
641 | true |
642 | } | |
643 | _ => false, | |
8bb4bdeb | 644 | } |
8bb4bdeb XL |
645 | } |
646 | ||
dc9dc135 XL |
647 | pub fn check_user_unop( |
648 | &self, | |
dfeec247 | 649 | ex: &'tcx hir::Expr<'tcx>, |
dc9dc135 XL |
650 | operand_ty: Ty<'tcx>, |
651 | op: hir::UnOp, | |
652 | ) -> Ty<'tcx> { | |
a7813a04 | 653 | assert!(op.is_by_value()); |
ee023bcb | 654 | match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) { |
7cac9316 | 655 | Ok(method) => { |
3b2f2976 | 656 | self.write_method_call(ex.hir_id, method); |
7cac9316 XL |
657 | method.sig.output() |
658 | } | |
c295e0f8 | 659 | Err(errors) => { |
fc512014 | 660 | let actual = self.resolve_vars_if_possible(operand_ty); |
7cac9316 | 661 | if !actual.references_error() { |
dfeec247 XL |
662 | let mut err = struct_span_err!( |
663 | self.tcx.sess, | |
664 | ex.span, | |
665 | E0600, | |
666 | "cannot apply unary operator `{}` to type `{}`", | |
667 | op.as_str(), | |
668 | actual | |
669 | ); | |
670 | err.span_label( | |
671 | ex.span, | |
f035d41b | 672 | format!("cannot apply unary operator `{}`", op.as_str()), |
dfeec247 | 673 | ); |
ee023bcb FG |
674 | let missing_trait = match op { |
675 | hir::UnOp::Deref => unreachable!("check unary op `-` or `!` only"), | |
676 | hir::UnOp::Not => "std::ops::Not", | |
677 | hir::UnOp::Neg => "std::ops::Neg", | |
678 | }; | |
679 | let mut visitor = TypeParamVisitor(vec![]); | |
680 | visitor.visit_ty(operand_ty); | |
681 | if let [ty] = &visitor.0[..] && let ty::Param(p) = *operand_ty.kind() { | |
682 | suggest_constraining_param( | |
683 | self.tcx, | |
684 | self.body_id, | |
685 | &mut err, | |
686 | *ty, | |
687 | operand_ty, | |
688 | missing_trait, | |
689 | p, | |
690 | true, | |
691 | ); | |
692 | } | |
c295e0f8 XL |
693 | |
694 | let sp = self.tcx.sess.source_map().start_point(ex.span); | |
695 | if let Some(sp) = | |
696 | self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) | |
697 | { | |
698 | // If the previous expression was a block expression, suggest parentheses | |
699 | // (turning this into a binary subtraction operation instead.) | |
700 | // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) | |
701 | self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); | |
702 | } else { | |
703 | match actual.kind() { | |
704 | Uint(_) if op == hir::UnOp::Neg => { | |
705 | err.note("unsigned values cannot be negated"); | |
706 | ||
707 | if let hir::ExprKind::Unary( | |
708 | _, | |
709 | hir::Expr { | |
710 | kind: | |
711 | hir::ExprKind::Lit(Spanned { | |
712 | node: ast::LitKind::Int(1, _), | |
713 | .. | |
714 | }), | |
715 | .. | |
716 | }, | |
717 | ) = ex.kind | |
718 | { | |
719 | err.span_suggestion( | |
720 | ex.span, | |
721 | &format!( | |
ee023bcb | 722 | "you may have meant the maximum value of `{actual}`", |
c295e0f8 | 723 | ), |
ee023bcb | 724 | format!("{actual}::MAX"), |
c295e0f8 XL |
725 | Applicability::MaybeIncorrect, |
726 | ); | |
727 | } | |
728 | } | |
729 | Str | Never | Char | Tuple(_) | Array(_, _) => {} | |
730 | Ref(_, lty, _) if *lty.kind() == Str => {} | |
731 | _ => { | |
732 | self.note_unmet_impls_on_type(&mut err, errors); | |
29967ef6 | 733 | } |
94b46f34 XL |
734 | } |
735 | } | |
736 | err.emit(); | |
7cac9316 | 737 | } |
f035d41b | 738 | self.tcx.ty_error() |
a7813a04 | 739 | } |
c34b1796 AL |
740 | } |
741 | } | |
c34b1796 | 742 | |
dfeec247 XL |
743 | fn lookup_op_method( |
744 | &self, | |
745 | lhs_ty: Ty<'tcx>, | |
ee023bcb FG |
746 | other_ty: Option<Ty<'tcx>>, |
747 | other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, | |
dfeec247 | 748 | op: Op, |
c295e0f8 | 749 | ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { |
ea8adc8c | 750 | let lang = self.tcx.lang_items(); |
a7813a04 | 751 | |
7cac9316 XL |
752 | let span = match op { |
753 | Op::Binary(op, _) => op.span, | |
dfeec247 | 754 | Op::Unary(_, span) => span, |
7cac9316 XL |
755 | }; |
756 | let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op { | |
a7813a04 | 757 | match op.node { |
3dfed10e XL |
758 | hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), |
759 | hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), | |
760 | hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), | |
761 | hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), | |
762 | hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), | |
763 | hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), | |
764 | hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), | |
765 | hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), | |
766 | hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), | |
767 | hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), | |
dfeec247 XL |
768 | hir::BinOpKind::Lt |
769 | | hir::BinOpKind::Le | |
770 | | hir::BinOpKind::Ge | |
771 | | hir::BinOpKind::Gt | |
772 | | hir::BinOpKind::Eq | |
773 | | hir::BinOpKind::Ne | |
774 | | hir::BinOpKind::And | |
775 | | hir::BinOpKind::Or => { | |
776 | span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) | |
a7813a04 | 777 | } |
b039eaaf | 778 | } |
7cac9316 | 779 | } else if let Op::Binary(op, IsAssign::No) = op { |
a7813a04 | 780 | match op.node { |
3dfed10e XL |
781 | hir::BinOpKind::Add => (sym::add, lang.add_trait()), |
782 | hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), | |
783 | hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), | |
784 | hir::BinOpKind::Div => (sym::div, lang.div_trait()), | |
785 | hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), | |
786 | hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), | |
787 | hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), | |
788 | hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), | |
789 | hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), | |
790 | hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), | |
791 | hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), | |
792 | hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), | |
793 | hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), | |
794 | hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), | |
795 | hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), | |
796 | hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), | |
8faf50e0 | 797 | hir::BinOpKind::And | hir::BinOpKind::Or => { |
7cac9316 | 798 | span_bug!(span, "&& and || are not overloadable") |
a7813a04 | 799 | } |
b039eaaf | 800 | } |
6a06907d | 801 | } else if let Op::Unary(hir::UnOp::Not, _) = op { |
3dfed10e | 802 | (sym::not, lang.not_trait()) |
6a06907d | 803 | } else if let Op::Unary(hir::UnOp::Neg, _) = op { |
3dfed10e | 804 | (sym::neg, lang.neg_trait()) |
7cac9316 XL |
805 | } else { |
806 | bug!("lookup_op_method: op not supported: {:?}", op) | |
807 | }; | |
c34b1796 | 808 | |
dfeec247 XL |
809 | debug!( |
810 | "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", | |
811 | lhs_ty, op, opname, trait_did | |
812 | ); | |
7cac9316 | 813 | |
17df50a5 XL |
814 | // Catches cases like #83893, where a lang item is declared with the |
815 | // wrong number of generic arguments. Should have yielded an error | |
816 | // elsewhere by now, but we have to catch it here so that we do not | |
817 | // index `other_tys` out of bounds (if the lang item has too many | |
818 | // generic arguments, `other_tys` is too short). | |
819 | if !has_expected_num_generic_args( | |
820 | self.tcx, | |
821 | trait_did, | |
822 | match op { | |
823 | // Binary ops have a generic right-hand side, unary ops don't | |
824 | Op::Binary(..) => 1, | |
825 | Op::Unary(..) => 0, | |
826 | }, | |
827 | ) { | |
c295e0f8 | 828 | return Err(vec![]); |
17df50a5 XL |
829 | } |
830 | ||
c295e0f8 | 831 | let opname = Ident::with_dummy_span(opname); |
7cac9316 | 832 | let method = trait_did.and_then(|trait_did| { |
ee023bcb | 833 | self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr) |
7cac9316 | 834 | }); |
c34b1796 | 835 | |
c295e0f8 XL |
836 | match (method, trait_did) { |
837 | (Some(ok), _) => { | |
cc61c64b | 838 | let method = self.register_infer_ok_obligations(ok); |
e1599b0c | 839 | self.select_obligations_where_possible(false, |_| {}); |
7cac9316 | 840 | Ok(method) |
a7813a04 | 841 | } |
c295e0f8 XL |
842 | (None, None) => Err(vec![]), |
843 | (None, Some(trait_did)) => { | |
844 | let (obligation, _) = | |
ee023bcb | 845 | self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr); |
c295e0f8 XL |
846 | let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); |
847 | fulfill.register_predicate_obligation(self, obligation); | |
3c0e092e | 848 | Err(fulfill.select_where_possible(&self.infcx)) |
c295e0f8 | 849 | } |
c34b1796 AL |
850 | } |
851 | } | |
852 | } | |
853 | ||
854 | // Binary operator categories. These categories summarize the behavior | |
ee023bcb | 855 | // with respect to the builtin operations supported. |
c34b1796 AL |
856 | enum BinOpCategory { |
857 | /// &&, || -- cannot be overridden | |
858 | Shortcircuit, | |
859 | ||
860 | /// <<, >> -- when shifting a single integer, rhs can be any | |
861 | /// integer type. For simd, types must match. | |
862 | Shift, | |
863 | ||
864 | /// +, -, etc -- takes equal types, produces same type as input, | |
865 | /// applicable to ints/floats/simd | |
866 | Math, | |
867 | ||
868 | /// &, |, ^ -- takes equal types, produces same type as input, | |
869 | /// applicable to ints/floats/simd/bool | |
870 | Bitwise, | |
871 | ||
872 | /// ==, !=, etc -- takes equal types, produces bools, except for simd, | |
873 | /// which produce the input type | |
874 | Comparison, | |
875 | } | |
876 | ||
877 | impl BinOpCategory { | |
e9174d1e | 878 | fn from(op: hir::BinOp) -> BinOpCategory { |
c34b1796 | 879 | match op.node { |
dfeec247 XL |
880 | hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, |
881 | ||
882 | hir::BinOpKind::Add | |
883 | | hir::BinOpKind::Sub | |
884 | | hir::BinOpKind::Mul | |
885 | | hir::BinOpKind::Div | |
886 | | hir::BinOpKind::Rem => BinOpCategory::Math, | |
887 | ||
888 | hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { | |
889 | BinOpCategory::Bitwise | |
890 | } | |
891 | ||
892 | hir::BinOpKind::Eq | |
893 | | hir::BinOpKind::Ne | |
894 | | hir::BinOpKind::Lt | |
895 | | hir::BinOpKind::Le | |
896 | | hir::BinOpKind::Ge | |
897 | | hir::BinOpKind::Gt => BinOpCategory::Comparison, | |
898 | ||
899 | hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, | |
c34b1796 AL |
900 | } |
901 | } | |
902 | } | |
903 | ||
b039eaaf | 904 | /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) |
7cac9316 | 905 | #[derive(Clone, Copy, Debug, PartialEq)] |
b039eaaf SL |
906 | enum IsAssign { |
907 | No, | |
908 | Yes, | |
909 | } | |
910 | ||
7cac9316 XL |
911 | #[derive(Clone, Copy, Debug)] |
912 | enum Op { | |
913 | Binary(hir::BinOp, IsAssign), | |
914 | Unary(hir::UnOp, Span), | |
915 | } | |
916 | ||
74b04a01 | 917 | /// Dereferences a single level of immutable referencing. |
a2a8927a | 918 | fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { |
1b1a35ee | 919 | match ty.kind() { |
5099ac24 | 920 | ty::Ref(_, ty, hir::Mutability::Not) => *ty, |
74b04a01 XL |
921 | _ => ty, |
922 | } | |
923 | } | |
924 | ||
9fa01778 | 925 | /// Returns `true` if this is a built-in arithmetic operation (e.g., u32 |
c34b1796 AL |
926 | /// + u32, i16x4 == i16x4) and false if these types would have to be |
927 | /// overloaded to be legal. There are two reasons that we distinguish | |
928 | /// builtin operations from overloaded ones (vs trying to drive | |
929 | /// everything uniformly through the trait system and intrinsics or | |
930 | /// something like that): | |
931 | /// | |
932 | /// 1. Builtin operations can trivially be evaluated in constants. | |
933 | /// 2. For comparison operators applied to SIMD types the result is | |
9fa01778 | 934 | /// not of type `bool`. For example, `i16x4 == i16x4` yields a |
c34b1796 AL |
935 | /// type like `i16x4`. This means that the overloaded trait |
936 | /// `PartialEq` is not applicable. | |
937 | /// | |
938 | /// Reason #2 is the killer. I tried for a while to always use | |
94b46f34 | 939 | /// overloaded logic and just check the types in constants/codegen after |
c34b1796 | 940 | /// the fact, and it worked fine, except for SIMD types. -nmatsakis |
74b04a01 XL |
941 | fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { |
942 | // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. | |
943 | // (See https://github.com/rust-lang/rust/issues/57447.) | |
944 | let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); | |
945 | ||
c34b1796 | 946 | match BinOpCategory::from(op) { |
dfeec247 | 947 | BinOpCategory::Shortcircuit => true, |
c34b1796 AL |
948 | |
949 | BinOpCategory::Shift => { | |
dfeec247 XL |
950 | lhs.references_error() |
951 | || rhs.references_error() | |
952 | || lhs.is_integral() && rhs.is_integral() | |
c34b1796 AL |
953 | } |
954 | ||
955 | BinOpCategory::Math => { | |
dfeec247 XL |
956 | lhs.references_error() |
957 | || rhs.references_error() | |
958 | || lhs.is_integral() && rhs.is_integral() | |
959 | || lhs.is_floating_point() && rhs.is_floating_point() | |
c34b1796 AL |
960 | } |
961 | ||
962 | BinOpCategory::Bitwise => { | |
dfeec247 XL |
963 | lhs.references_error() |
964 | || rhs.references_error() | |
965 | || lhs.is_integral() && rhs.is_integral() | |
966 | || lhs.is_floating_point() && rhs.is_floating_point() | |
967 | || lhs.is_bool() && rhs.is_bool() | |
c34b1796 AL |
968 | } |
969 | ||
970 | BinOpCategory::Comparison => { | |
dfeec247 | 971 | lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() |
c34b1796 AL |
972 | } |
973 | } | |
974 | } | |
74b04a01 | 975 | |
f9f354fc XL |
976 | fn suggest_constraining_param( |
977 | tcx: TyCtxt<'_>, | |
978 | body_id: hir::HirId, | |
ee023bcb | 979 | mut err: &mut Diagnostic, |
f9f354fc XL |
980 | lhs_ty: Ty<'_>, |
981 | rhs_ty: Ty<'_>, | |
982 | missing_trait: &str, | |
983 | p: ty::ParamTy, | |
984 | set_output: bool, | |
985 | ) { | |
986 | let hir = tcx.hir(); | |
ee023bcb | 987 | let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`"); |
f9f354fc XL |
988 | // Try to find the def-id and details for the parameter p. We have only the index, |
989 | // so we have to find the enclosing function's def-id, then look through its declared | |
990 | // generic parameters to get the declaration. | |
991 | let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id }); | |
992 | let generics = tcx.generics_of(def_id); | |
993 | let param_def_id = generics.type_param(&p, tcx).def_id; | |
994 | if let Some(generics) = param_def_id | |
995 | .as_local() | |
3dfed10e | 996 | .map(|id| hir.local_def_id_to_hir_id(id)) |
5099ac24 | 997 | .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id))) |
f9f354fc XL |
998 | .as_ref() |
999 | .and_then(|node| node.generics()) | |
1000 | { | |
ee023bcb | 1001 | let output = if set_output { format!("<Output = {rhs_ty}>") } else { String::new() }; |
f9f354fc XL |
1002 | suggest_constraining_type_param( |
1003 | tcx, | |
1004 | generics, | |
1005 | &mut err, | |
ee023bcb FG |
1006 | &lhs_ty.to_string(), |
1007 | &format!("{missing_trait}{output}"), | |
f9f354fc XL |
1008 | None, |
1009 | ); | |
1010 | } else { | |
1011 | let span = tcx.def_span(param_def_id); | |
1012 | err.span_label(span, msg); | |
1013 | } | |
1014 | } | |
f035d41b | 1015 | |
5099ac24 | 1016 | struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>); |
f035d41b XL |
1017 | |
1018 | impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { | |
fc512014 | 1019 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { |
1b1a35ee | 1020 | if let ty::Param(_) = ty.kind() { |
5099ac24 | 1021 | self.0.push(ty); |
f035d41b XL |
1022 | } |
1023 | ty.super_visit_with(self) | |
1024 | } | |
1025 | } | |
1026 | ||
1027 | struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); | |
1028 | ||
a2a8927a | 1029 | impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { |
f035d41b XL |
1030 | fn tcx(&self) -> TyCtxt<'tcx> { |
1031 | self.0.tcx | |
1032 | } | |
1033 | ||
1034 | fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { | |
1b1a35ee | 1035 | match ty.kind() { |
f035d41b XL |
1036 | ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { |
1037 | kind: TypeVariableOriginKind::MiscVariable, | |
1038 | span: self.1, | |
1039 | }), | |
1040 | _ => ty.super_fold_with(self), | |
1041 | } | |
1042 | } | |
1043 | } |