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