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