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