]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/op.rs
Auto merge of #90602 - mbartlett21:const-intoiterator, r=oli-obk
[rust.git] / compiler / rustc_typeck / src / check / op.rs
1 //! Code related to processing overloaded binary and unary operators.
2
3 use super::method::MethodCallee;
4 use super::{has_expected_num_generic_args, FnCtxt};
5 use rustc_ast as ast;
6 use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
7 use rustc_hir as hir;
8 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
9 use rustc_middle::ty::adjustment::{
10     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
11 };
12 use rustc_middle::ty::fold::TypeFolder;
13 use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
14 use rustc_middle::ty::{
15     self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
16 };
17 use rustc_span::source_map::Spanned;
18 use rustc_span::symbol::{sym, Ident};
19 use rustc_span::Span;
20 use rustc_trait_selection::infer::InferCtxtExt;
21 use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
22
23 use std::ops::ControlFlow;
24
25 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26     /// Checks a `a <op>= b`
27     pub fn check_binop_assign(
28         &self,
29         expr: &'tcx hir::Expr<'tcx>,
30         op: hir::BinOp,
31         lhs: &'tcx hir::Expr<'tcx>,
32         rhs: &'tcx hir::Expr<'tcx>,
33     ) -> Ty<'tcx> {
34         let (lhs_ty, rhs_ty, return_ty) =
35             self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);
36
37         let ty =
38             if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
39                 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
40                 self.tcx.mk_unit()
41             } else {
42                 return_ty
43             };
44
45         self.check_lhs_assignable(lhs, "E0067", op.span);
46
47         ty
48     }
49
50     /// Checks a potentially overloaded binary operator.
51     pub fn check_binop(
52         &self,
53         expr: &'tcx hir::Expr<'tcx>,
54         op: hir::BinOp,
55         lhs_expr: &'tcx hir::Expr<'tcx>,
56         rhs_expr: &'tcx hir::Expr<'tcx>,
57     ) -> Ty<'tcx> {
58         let tcx = self.tcx;
59
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         );
64
65         match BinOpCategory::from(op) {
66             BinOpCategory::Shortcircuit => {
67                 // && and || are a simple case.
68                 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
69                 let lhs_diverges = self.diverges.get();
70                 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
71
72                 // Depending on the LHS' value, the RHS can never execute.
73                 self.diverges.set(lhs_diverges);
74
75                 tcx.types.bool
76             }
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.
81                 let (lhs_ty, rhs_ty, return_ty) =
82                     self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No);
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.
96                 if !lhs_ty.is_ty_var()
97                     && !rhs_ty.is_ty_var()
98                     && is_builtin_binop(lhs_ty, rhs_ty, op)
99                 {
100                     let builtin_return_ty = self.enforce_builtin_binop_types(
101                         lhs_expr.span,
102                         lhs_ty,
103                         rhs_expr.span,
104                         rhs_ty,
105                         op,
106                     );
107                     self.demand_suptype(expr.span, builtin_return_ty, return_ty);
108                 }
109
110                 return_ty
111             }
112         }
113     }
114
115     fn enforce_builtin_binop_types(
116         &self,
117         lhs_span: Span,
118         lhs_ty: Ty<'tcx>,
119         rhs_span: Span,
120         rhs_ty: Ty<'tcx>,
121         op: hir::BinOp,
122     ) -> Ty<'tcx> {
123         debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
124
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
129         let tcx = self.tcx;
130         match BinOpCategory::from(op) {
131             BinOpCategory::Shortcircuit => {
132                 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
133                 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
134                 tcx.types.bool
135             }
136
137             BinOpCategory::Shift => {
138                 // result type is same as LHS always
139                 lhs_ty
140             }
141
142             BinOpCategory::Math | BinOpCategory::Bitwise => {
143                 // both LHS and RHS and result will have the same type
144                 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
145                 lhs_ty
146             }
147
148             BinOpCategory::Comparison => {
149                 // both LHS and RHS and result will have the same type
150                 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
151                 tcx.types.bool
152             }
153         }
154     }
155
156     fn check_overloaded_binop(
157         &self,
158         expr: &'tcx hir::Expr<'tcx>,
159         lhs_expr: &'tcx hir::Expr<'tcx>,
160         rhs_expr: &'tcx hir::Expr<'tcx>,
161         op: hir::BinOp,
162         is_assign: IsAssign,
163     ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
164         debug!(
165             "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
166             expr.hir_id, op, is_assign
167         );
168
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.
174                 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
175                 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
176                 let lhs_ty = self.check_expr(lhs_expr);
177                 let fresh_var = self.next_ty_var(TypeVariableOrigin {
178                     kind: TypeVariableOriginKind::MiscVariable,
179                     span: lhs_expr.span,
180                 });
181                 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
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.
188                 self.check_expr(lhs_expr)
189             }
190         };
191         let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
192
193         // N.B., as we have not yet type-checked the RHS, we don't have the
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`.
199         let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
200             kind: TypeVariableOriginKind::MiscVariable,
201             span: rhs_expr.span,
202         });
203
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         );
210
211         // see `NB` above
212         let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
213         let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
214
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 {
219                     if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
220                         let mutbl = match mutbl {
221                             hir::Mutability::Not => AutoBorrowMutability::Not,
222                             hir::Mutability::Mut => AutoBorrowMutability::Mut {
223                                 // Allow two-phase borrows for binops in initial deployment
224                                 // since they desugar to methods
225                                 allow_two_phase_borrow: AllowTwoPhase::Yes,
226                             },
227                         };
228                         let autoref = Adjustment {
229                             kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
230                             target: method.sig.inputs()[0],
231                         };
232                         self.apply_adjustments(lhs_expr, vec![autoref]);
233                     }
234                 }
235                 if by_ref_binop {
236                     if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
237                         let mutbl = match mutbl {
238                             hir::Mutability::Not => AutoBorrowMutability::Not,
239                             hir::Mutability::Mut => AutoBorrowMutability::Mut {
240                                 // Allow two-phase borrows for binops in initial deployment
241                                 // since they desugar to methods
242                                 allow_two_phase_borrow: AllowTwoPhase::Yes,
243                             },
244                         };
245                         let autoref = Adjustment {
246                             kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
247                             target: method.sig.inputs()[1],
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]);
253                         self.typeck_results
254                             .borrow_mut()
255                             .adjustments_mut()
256                             .entry(rhs_expr.hir_id)
257                             .or_default()
258                             .push(autoref);
259                     }
260                 }
261                 self.write_method_call(expr.hir_id, method);
262
263                 method.sig.output()
264             }
265             // error types are considered "builtin"
266             Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
267             Err(errors) => {
268                 let source_map = self.tcx.sess.source_map();
269                 let (mut err, missing_trait, use_output) = match is_assign {
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                         };
296                         self.note_unmet_impls_on_type(&mut err, errors);
297                         (err, missing_trait, false)
298                     }
299                     IsAssign::No => {
300                         let (message, missing_trait, use_output) = match op.node {
301                             hir::BinOpKind::Add => (
302                                 format!("cannot add `{rhs_ty}` to `{lhs_ty}`"),
303                                 Some("std::ops::Add"),
304                                 true,
305                             ),
306                             hir::BinOpKind::Sub => (
307                                 format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"),
308                                 Some("std::ops::Sub"),
309                                 true,
310                             ),
311                             hir::BinOpKind::Mul => (
312                                 format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"),
313                                 Some("std::ops::Mul"),
314                                 true,
315                             ),
316                             hir::BinOpKind::Div => (
317                                 format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"),
318                                 Some("std::ops::Div"),
319                                 true,
320                             ),
321                             hir::BinOpKind::Rem => (
322                                 format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"),
323                                 Some("std::ops::Rem"),
324                                 true,
325                             ),
326                             hir::BinOpKind::BitAnd => (
327                                 format!("no implementation for `{lhs_ty} & {rhs_ty}`"),
328                                 Some("std::ops::BitAnd"),
329                                 true,
330                             ),
331                             hir::BinOpKind::BitXor => (
332                                 format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"),
333                                 Some("std::ops::BitXor"),
334                                 true,
335                             ),
336                             hir::BinOpKind::BitOr => (
337                                 format!("no implementation for `{lhs_ty} | {rhs_ty}`"),
338                                 Some("std::ops::BitOr"),
339                                 true,
340                             ),
341                             hir::BinOpKind::Shl => (
342                                 format!("no implementation for `{lhs_ty} << {rhs_ty}`"),
343                                 Some("std::ops::Shl"),
344                                 true,
345                             ),
346                             hir::BinOpKind::Shr => (
347                                 format!("no implementation for `{lhs_ty} >> {rhs_ty}`"),
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());
384                         if !lhs_expr.span.eq(&rhs_expr.span) {
385                             self.add_type_neq_err_label(
386                                 &mut err,
387                                 lhs_expr.span,
388                                 lhs_ty,
389                                 rhs_ty,
390                                 rhs_expr,
391                                 op,
392                                 is_assign,
393                             );
394                             self.add_type_neq_err_label(
395                                 &mut err,
396                                 rhs_expr.span,
397                                 rhs_ty,
398                                 lhs_ty,
399                                 lhs_expr,
400                                 op,
401                                 is_assign,
402                             );
403                         }
404                         self.note_unmet_impls_on_type(&mut err, errors);
405                         (err, missing_trait, use_output)
406                     }
407                 };
408                 if let Ref(_, rty, _) = lhs_ty.kind() {
409                     if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
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()
418                     {
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,
429                             );
430                             err.span_suggestion_verbose(
431                                 lhs_expr.span.shrink_to_lo(),
432                                 msg,
433                                 "*".to_string(),
434                                 rustc_errors::Applicability::MachineApplicable,
435                             );
436                         }
437                     }
438                 }
439                 if let Some(missing_trait) = missing_trait {
440                     let mut visitor = TypeParamVisitor(vec![]);
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
451                     } else if let [ty] = &visitor.0[..] {
452                         if let ty::Param(p) = *ty.kind() {
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.
456                             let mut eraser = TypeParamEraser(self, expr.span);
457                             let needs_bound = self
458                                 .lookup_op_method(
459                                     eraser.fold_ty(lhs_ty),
460                                     Some(eraser.fold_ty(rhs_ty)),
461                                     Some(rhs_expr),
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,
469                                     &mut err,
470                                     *ty,
471                                     rhs_ty,
472                                     missing_trait,
473                                     p,
474                                     use_output,
475                                 );
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!(
480                                     "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
481                                 ));
482                             }
483                         } else {
484                             bug!("type param visitor stored a non type param: {:?}", ty.kind());
485                         }
486                     }
487                 }
488                 err.emit();
489                 self.tcx.ty_error()
490             }
491         };
492
493         (lhs_ty, rhs_ty, return_ty)
494     }
495
496     /// If one of the types is an uncalled function and calling it would yield the other type,
497     /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
498     fn add_type_neq_err_label(
499         &self,
500         err: &mut Diagnostic,
501         span: Span,
502         ty: Ty<'tcx>,
503         other_ty: Ty<'tcx>,
504         other_expr: &'tcx hir::Expr<'tcx>,
505         op: hir::BinOp,
506         is_assign: IsAssign,
507     ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
508         err.span_label(span, ty.to_string());
509         if let FnDef(def_id, _) = *ty.kind() {
510             if !self.tcx.has_typeck_results(def_id) {
511                 return false;
512             }
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.
516             let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
517                 return false;
518             };
519
520             let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
521                 if !self.tcx.has_typeck_results(def_id) {
522                     return false;
523                 }
524                 // We're emitting a suggestion, so we can just ignore regions
525                 self.tcx.fn_sig(def_id).skip_binder().output()
526             } else {
527                 other_ty
528             };
529
530             if self
531                 .lookup_op_method(
532                     fn_sig.output(),
533                     Some(other_ty),
534                     Some(other_expr),
535                     Op::Binary(op, is_assign),
536                 )
537                 .is_ok()
538             {
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                 };
544
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                 );
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
562     fn check_str_addition(
563         &self,
564         lhs_expr: &'tcx hir::Expr<'tcx>,
565         rhs_expr: &'tcx hir::Expr<'tcx>,
566         lhs_ty: Ty<'tcx>,
567         rhs_ty: Ty<'tcx>,
568         err: &mut Diagnostic,
569         is_assign: IsAssign,
570         op: hir::BinOp,
571     ) -> bool {
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";
575
576         let string_type = self.tcx.get_diagnostic_item(sym::String);
577         let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() {
578             Some(ty_def) => Some(ty_def.did()) == string_type,
579             None => false,
580         };
581
582         match (lhs_ty.kind(), rhs_ty.kind()) {
583             (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
584                 if (*l_ty.kind() == Str || is_std_string(l_ty)) && (
585                         *r_ty.kind() == Str || is_std_string(r_ty) ||
586                         &format!("{:?}", rhs_ty) == "&&str"
587                     ) =>
588             {
589                 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
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                     }
607                 }
608                 true
609             }
610             (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
611                 if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
612             {
613                 err.span_label(
614                     op.span,
615                     "`+` cannot be used to concatenate a `&str` with a `String`",
616                 );
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())
623                         } else {
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())
626                         };
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,
634                             Applicability::MachineApplicable,
635                         );
636                     }
637                     IsAssign::Yes => {
638                         err.note(str_concat_note);
639                     }
640                 }
641                 true
642             }
643             _ => false,
644         }
645     }
646
647     pub fn check_user_unop(
648         &self,
649         ex: &'tcx hir::Expr<'tcx>,
650         operand_ty: Ty<'tcx>,
651         op: hir::UnOp,
652     ) -> Ty<'tcx> {
653         assert!(op.is_by_value());
654         match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) {
655             Ok(method) => {
656                 self.write_method_call(ex.hir_id, method);
657                 method.sig.output()
658             }
659             Err(errors) => {
660                 let actual = self.resolve_vars_if_possible(operand_ty);
661                 if !actual.references_error() {
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,
672                         format!("cannot apply unary operator `{}`", op.as_str()),
673                     );
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                     }
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!(
722                                             "you may have meant the maximum value of `{actual}`",
723                                         ),
724                                         format!("{actual}::MAX"),
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);
733                             }
734                         }
735                     }
736                     err.emit();
737                 }
738                 self.tcx.ty_error()
739             }
740         }
741     }
742
743     fn lookup_op_method(
744         &self,
745         lhs_ty: Ty<'tcx>,
746         other_ty: Option<Ty<'tcx>>,
747         other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
748         op: Op,
749     ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
750         let lang = self.tcx.lang_items();
751
752         let span = match op {
753             Op::Binary(op, _) => op.span,
754             Op::Unary(_, span) => span,
755         };
756         let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
757             match op.node {
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()),
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())
777                 }
778             }
779         } else if let Op::Binary(op, IsAssign::No) = op {
780             match op.node {
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()),
797                 hir::BinOpKind::And | hir::BinOpKind::Or => {
798                     span_bug!(span, "&& and || are not overloadable")
799                 }
800             }
801         } else if let Op::Unary(hir::UnOp::Not, _) = op {
802             (sym::not, lang.not_trait())
803         } else if let Op::Unary(hir::UnOp::Neg, _) = op {
804             (sym::neg, lang.neg_trait())
805         } else {
806             bug!("lookup_op_method: op not supported: {:?}", op)
807         };
808
809         debug!(
810             "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
811             lhs_ty, op, opname, trait_did
812         );
813
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         ) {
828             return Err(vec![]);
829         }
830
831         let opname = Ident::with_dummy_span(opname);
832         let method = trait_did.and_then(|trait_did| {
833             self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr)
834         });
835
836         match (method, trait_did) {
837             (Some(ok), _) => {
838                 let method = self.register_infer_ok_obligations(ok);
839                 self.select_obligations_where_possible(false, |_| {});
840                 Ok(method)
841             }
842             (None, None) => Err(vec![]),
843             (None, Some(trait_did)) => {
844                 let (obligation, _) =
845                     self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr);
846                 let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
847                 fulfill.register_predicate_obligation(self, obligation);
848                 Err(fulfill.select_where_possible(&self.infcx))
849             }
850         }
851     }
852 }
853
854 // Binary operator categories. These categories summarize the behavior
855 // with respect to the builtin operations supported.
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 {
878     fn from(op: hir::BinOp) -> BinOpCategory {
879         match op.node {
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,
900         }
901     }
902 }
903
904 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
905 #[derive(Clone, Copy, Debug, PartialEq)]
906 enum IsAssign {
907     No,
908     Yes,
909 }
910
911 #[derive(Clone, Copy, Debug)]
912 enum Op {
913     Binary(hir::BinOp, IsAssign),
914     Unary(hir::UnOp, Span),
915 }
916
917 /// Dereferences a single level of immutable referencing.
918 fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
919     match ty.kind() {
920         ty::Ref(_, ty, hir::Mutability::Not) => *ty,
921         _ => ty,
922     }
923 }
924
925 /// Returns `true` if this is a built-in arithmetic operation (e.g., u32
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
934 ///    not of type `bool`. For example, `i16x4 == i16x4` yields a
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
939 /// overloaded logic and just check the types in constants/codegen after
940 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
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
946     match BinOpCategory::from(op) {
947         BinOpCategory::Shortcircuit => true,
948
949         BinOpCategory::Shift => {
950             lhs.references_error()
951                 || rhs.references_error()
952                 || lhs.is_integral() && rhs.is_integral()
953         }
954
955         BinOpCategory::Math => {
956             lhs.references_error()
957                 || rhs.references_error()
958                 || lhs.is_integral() && rhs.is_integral()
959                 || lhs.is_floating_point() && rhs.is_floating_point()
960         }
961
962         BinOpCategory::Bitwise => {
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()
968         }
969
970         BinOpCategory::Comparison => {
971             lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
972         }
973     }
974 }
975
976 fn suggest_constraining_param(
977     tcx: TyCtxt<'_>,
978     body_id: hir::HirId,
979     mut err: &mut Diagnostic,
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();
987     let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`");
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()
996         .map(|id| hir.local_def_id_to_hir_id(id))
997         .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id)))
998         .as_ref()
999         .and_then(|node| node.generics())
1000     {
1001         let output = if set_output { format!("<Output = {rhs_ty}>") } else { String::new() };
1002         suggest_constraining_type_param(
1003             tcx,
1004             generics,
1005             &mut err,
1006             &lhs_ty.to_string(),
1007             &format!("{missing_trait}{output}"),
1008             None,
1009         );
1010     } else {
1011         let span = tcx.def_span(param_def_id);
1012         err.span_label(span, msg);
1013     }
1014 }
1015
1016 struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
1017
1018 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
1019     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1020         if let ty::Param(_) = ty.kind() {
1021             self.0.push(ty);
1022         }
1023         ty.super_visit_with(self)
1024     }
1025 }
1026
1027 struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
1028
1029 impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
1030     fn tcx(&self) -> TyCtxt<'tcx> {
1031         self.0.tcx
1032     }
1033
1034     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1035         match ty.kind() {
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 }