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