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