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