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