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