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