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