]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/op.rs
Auto merge of #35162 - canndrew:bang_type_coerced, r=nikomatsakis
[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;
14 use hir::def_id::DefId;
15 use rustc::ty::{Ty, TypeFoldable, PreferMutLvalue};
16 use syntax::ast;
17 use syntax::parse::token;
18 use rustc::hir;
19
20 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21     /// Check a `a <op>= b`
22     pub fn check_binop_assign(&self,
23                               expr: &'gcx hir::Expr,
24                               op: hir::BinOp,
25                               lhs_expr: &'gcx hir::Expr,
26                               rhs_expr: &'gcx hir::Expr)
27     {
28         self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue);
29
30         let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
31         let (rhs_ty, return_ty) =
32             self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
33         let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
34
35         if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
36             self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
37             self.write_nil(expr.id);
38         } else {
39             self.write_ty(expr.id, return_ty);
40         }
41
42         let tcx = self.tcx;
43         if !tcx.expr_is_lval(lhs_expr) {
44             struct_span_err!(
45                 tcx.sess, lhs_expr.span,
46                 E0067, "invalid left-hand side expression")
47             .span_label(
48                 lhs_expr.span,
49                 &format!("invalid expression for left-hand side"))
50             .emit();
51         }
52     }
53
54     /// Check a potentially overloaded binary operator.
55     pub fn check_binop(&self,
56                        expr: &'gcx hir::Expr,
57                        op: hir::BinOp,
58                        lhs_expr: &'gcx hir::Expr,
59                        rhs_expr: &'gcx hir::Expr)
60     {
61         let tcx = self.tcx;
62
63         debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
64                expr.id,
65                expr,
66                op,
67                lhs_expr,
68                rhs_expr);
69
70         self.check_expr(lhs_expr);
71         let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
72
73         match BinOpCategory::from(op) {
74             BinOpCategory::Shortcircuit => {
75                 // && and || are a simple case.
76                 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
77                 self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
78                 self.write_ty(expr.id, tcx.mk_bool());
79             }
80             _ => {
81                 // Otherwise, we always treat operators as if they are
82                 // overloaded. This is the way to be most flexible w/r/t
83                 // types that get inferred.
84                 let (rhs_ty, return_ty) =
85                     self.check_overloaded_binop(expr, lhs_expr, lhs_ty,
86                                                 rhs_expr, op, IsAssign::No);
87
88                 // Supply type inference hints if relevant. Probably these
89                 // hints should be enforced during select as part of the
90                 // `consider_unification_despite_ambiguity` routine, but this
91                 // more convenient for now.
92                 //
93                 // The basic idea is to help type inference by taking
94                 // advantage of things we know about how the impls for
95                 // scalar types are arranged. This is important in a
96                 // scenario like `1_u32 << 2`, because it lets us quickly
97                 // deduce that the result type should be `u32`, even
98                 // though we don't know yet what type 2 has and hence
99                 // can't pin this down to a specific impl.
100                 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
101                 if
102                     !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
103                     is_builtin_binop(lhs_ty, rhs_ty, op)
104                 {
105                     let builtin_return_ty =
106                         self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
107                     self.demand_suptype(expr.span, builtin_return_ty, return_ty);
108                 }
109
110                 self.write_ty(expr.id, return_ty);
111             }
112         }
113     }
114
115     fn enforce_builtin_binop_types(&self,
116                                    lhs_expr: &'gcx hir::Expr,
117                                    lhs_ty: Ty<'tcx>,
118                                    rhs_expr: &'gcx hir::Expr,
119                                    rhs_ty: Ty<'tcx>,
120                                    op: hir::BinOp)
121                                    -> Ty<'tcx>
122     {
123         debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
124
125         let tcx = self.tcx;
126         match BinOpCategory::from(op) {
127             BinOpCategory::Shortcircuit => {
128                 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
129                 self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
130                 tcx.mk_bool()
131             }
132
133             BinOpCategory::Shift => {
134                 // result type is same as LHS always
135                 lhs_ty
136             }
137
138             BinOpCategory::Math |
139             BinOpCategory::Bitwise => {
140                 // both LHS and RHS and result will have the same type
141                 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
142                 lhs_ty
143             }
144
145             BinOpCategory::Comparison => {
146                 // both LHS and RHS and result will have the same type
147                 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
148                 tcx.mk_bool()
149             }
150         }
151     }
152
153     fn check_overloaded_binop(&self,
154                               expr: &'gcx hir::Expr,
155                               lhs_expr: &'gcx hir::Expr,
156                               lhs_ty: Ty<'tcx>,
157                               rhs_expr: &'gcx hir::Expr,
158                               op: hir::BinOp,
159                               is_assign: IsAssign)
160                               -> (Ty<'tcx>, Ty<'tcx>)
161     {
162         debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
163                expr.id,
164                lhs_ty,
165                is_assign);
166
167         let (name, trait_def_id) = self.name_and_trait_def_id(op, is_assign);
168
169         // NB: As we have not yet type-checked the RHS, we don't have the
170         // type at hand. Make a variable to represent it. The whole reason
171         // for this indirection is so that, below, we can check the expr
172         // using this variable as the expected type, which sometimes lets
173         // us do better coercions than we would be able to do otherwise,
174         // particularly for things like `String + &String`.
175         let rhs_ty_var = self.next_ty_var();
176
177         let return_ty = match self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var],
178                                                     token::intern(name), trait_def_id,
179                                                     lhs_expr) {
180             Ok(return_ty) => return_ty,
181             Err(()) => {
182                 // error types are considered "builtin"
183                 if !lhs_ty.references_error() {
184                     if let IsAssign::Yes = is_assign {
185                         struct_span_err!(self.tcx.sess, lhs_expr.span, E0368,
186                                          "binary assignment operation `{}=` \
187                                           cannot be applied to type `{}`",
188                                          op.node.as_str(),
189                                          lhs_ty)
190                             .span_label(lhs_expr.span,
191                                         &format!("cannot use `{}=` on type `{}`",
192                                         op.node.as_str(), lhs_ty))
193                             .emit();
194                     } else {
195                         let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
196                             "binary operation `{}` cannot be applied to type `{}`",
197                             op.node.as_str(),
198                             lhs_ty);
199                         let missing_trait = match op.node {
200                             hir::BiAdd    => Some("std::ops::Add"),
201                             hir::BiSub    => Some("std::ops::Sub"),
202                             hir::BiMul    => Some("std::ops::Mul"),
203                             hir::BiDiv    => Some("std::ops::Div"),
204                             hir::BiRem    => Some("std::ops::Rem"),
205                             hir::BiBitAnd => Some("std::ops::BitAnd"),
206                             hir::BiBitOr  => Some("std::ops::BitOr"),
207                             hir::BiShl    => Some("std::ops::Shl"),
208                             hir::BiShr    => Some("std::ops::Shr"),
209                             hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
210                             hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
211                                 Some("std::cmp::PartialOrd"),
212                             _             => None
213                         };
214
215                         if let Some(missing_trait) = missing_trait {
216                             span_note!(&mut err, lhs_expr.span,
217                                        "an implementation of `{}` might be missing for `{}`",
218                                         missing_trait, lhs_ty);
219                         }
220                         err.emit();
221                     }
222                 }
223                 self.tcx.types.err
224             }
225         };
226
227         // see `NB` above
228         self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
229
230         (rhs_ty_var, return_ty)
231     }
232
233     pub fn check_user_unop(&self,
234                            op_str: &str,
235                            mname: &str,
236                            trait_did: Option<DefId>,
237                            ex: &'gcx hir::Expr,
238                            operand_expr: &'gcx hir::Expr,
239                            operand_ty: Ty<'tcx>,
240                            op: hir::UnOp)
241                            -> Ty<'tcx>
242     {
243         assert!(op.is_by_value());
244         match self.lookup_op_method(ex, operand_ty, vec![],
245                                     token::intern(mname), trait_did,
246                                     operand_expr) {
247             Ok(t) => t,
248             Err(()) => {
249                 self.type_error_message(ex.span, |actual| {
250                     format!("cannot apply unary operator `{}` to type `{}`",
251                             op_str, actual)
252                 }, operand_ty);
253                 self.tcx.types.err
254             }
255         }
256     }
257
258     fn name_and_trait_def_id(&self,
259                              op: hir::BinOp,
260                              is_assign: IsAssign)
261                              -> (&'static str, Option<DefId>) {
262         let lang = &self.tcx.lang_items;
263
264         if let IsAssign::Yes = is_assign {
265             match op.node {
266                 hir::BiAdd => ("add_assign", lang.add_assign_trait()),
267                 hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
268                 hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
269                 hir::BiDiv => ("div_assign", lang.div_assign_trait()),
270                 hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
271                 hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
272                 hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
273                 hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
274                 hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
275                 hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
276                 hir::BiLt | hir::BiLe |
277                 hir::BiGe | hir::BiGt |
278                 hir::BiEq | hir::BiNe |
279                 hir::BiAnd | hir::BiOr => {
280                     span_bug!(op.span,
281                               "impossible assignment operation: {}=",
282                               op.node.as_str())
283                 }
284             }
285         } else {
286             match op.node {
287                 hir::BiAdd => ("add", lang.add_trait()),
288                 hir::BiSub => ("sub", lang.sub_trait()),
289                 hir::BiMul => ("mul", lang.mul_trait()),
290                 hir::BiDiv => ("div", lang.div_trait()),
291                 hir::BiRem => ("rem", lang.rem_trait()),
292                 hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
293                 hir::BiBitAnd => ("bitand", lang.bitand_trait()),
294                 hir::BiBitOr => ("bitor", lang.bitor_trait()),
295                 hir::BiShl => ("shl", lang.shl_trait()),
296                 hir::BiShr => ("shr", lang.shr_trait()),
297                 hir::BiLt => ("lt", lang.ord_trait()),
298                 hir::BiLe => ("le", lang.ord_trait()),
299                 hir::BiGe => ("ge", lang.ord_trait()),
300                 hir::BiGt => ("gt", lang.ord_trait()),
301                 hir::BiEq => ("eq", lang.eq_trait()),
302                 hir::BiNe => ("ne", lang.eq_trait()),
303                 hir::BiAnd | hir::BiOr => {
304                     span_bug!(op.span, "&& and || are not overloadable")
305                 }
306             }
307         }
308     }
309
310     fn lookup_op_method(&self,
311                         expr: &'gcx hir::Expr,
312                         lhs_ty: Ty<'tcx>,
313                         other_tys: Vec<Ty<'tcx>>,
314                         opname: ast::Name,
315                         trait_did: Option<DefId>,
316                         lhs_expr: &'a hir::Expr)
317                         -> Result<Ty<'tcx>,()>
318     {
319         debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, \
320                                  trait_did={:?}, lhs_expr={:?})",
321                expr,
322                lhs_ty,
323                opname,
324                trait_did,
325                lhs_expr);
326
327         let method = match trait_did {
328             Some(trait_did) => {
329                 self.lookup_method_in_trait_adjusted(expr.span,
330                                                      Some(lhs_expr),
331                                                      opname,
332                                                      trait_did,
333                                                      0,
334                                                      false,
335                                                      lhs_ty,
336                                                      Some(other_tys))
337             }
338             None => None
339         };
340
341         match method {
342             Some(method) => {
343                 let method_ty = method.ty;
344
345                 // HACK(eddyb) Fully qualified path to work around a resolve bug.
346                 let method_call = ::rustc::ty::MethodCall::expr(expr.id);
347                 self.tables.borrow_mut().method_map.insert(method_call, method);
348
349                 // extract return type for method; all late bound regions
350                 // should have been instantiated by now
351                 let ret_ty = method_ty.fn_ret();
352                 Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
353             }
354             None => {
355                 Err(())
356             }
357         }
358     }
359 }
360
361 // Binary operator categories. These categories summarize the behavior
362 // with respect to the builtin operationrs supported.
363 enum BinOpCategory {
364     /// &&, || -- cannot be overridden
365     Shortcircuit,
366
367     /// <<, >> -- when shifting a single integer, rhs can be any
368     /// integer type. For simd, types must match.
369     Shift,
370
371     /// +, -, etc -- takes equal types, produces same type as input,
372     /// applicable to ints/floats/simd
373     Math,
374
375     /// &, |, ^ -- takes equal types, produces same type as input,
376     /// applicable to ints/floats/simd/bool
377     Bitwise,
378
379     /// ==, !=, etc -- takes equal types, produces bools, except for simd,
380     /// which produce the input type
381     Comparison,
382 }
383
384 impl BinOpCategory {
385     fn from(op: hir::BinOp) -> BinOpCategory {
386         match op.node {
387             hir::BiShl | hir::BiShr =>
388                 BinOpCategory::Shift,
389
390             hir::BiAdd |
391             hir::BiSub |
392             hir::BiMul |
393             hir::BiDiv |
394             hir::BiRem =>
395                 BinOpCategory::Math,
396
397             hir::BiBitXor |
398             hir::BiBitAnd |
399             hir::BiBitOr =>
400                 BinOpCategory::Bitwise,
401
402             hir::BiEq |
403             hir::BiNe |
404             hir::BiLt |
405             hir::BiLe |
406             hir::BiGe |
407             hir::BiGt =>
408                 BinOpCategory::Comparison,
409
410             hir::BiAnd |
411             hir::BiOr =>
412                 BinOpCategory::Shortcircuit,
413         }
414     }
415 }
416
417 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
418 #[derive(Clone, Copy, Debug)]
419 enum IsAssign {
420     No,
421     Yes,
422 }
423
424 /// Returns true if this is a built-in arithmetic operation (e.g. u32
425 /// + u32, i16x4 == i16x4) and false if these types would have to be
426 /// overloaded to be legal. There are two reasons that we distinguish
427 /// builtin operations from overloaded ones (vs trying to drive
428 /// everything uniformly through the trait system and intrinsics or
429 /// something like that):
430 ///
431 /// 1. Builtin operations can trivially be evaluated in constants.
432 /// 2. For comparison operators applied to SIMD types the result is
433 ///    not of type `bool`. For example, `i16x4==i16x4` yields a
434 ///    type like `i16x4`. This means that the overloaded trait
435 ///    `PartialEq` is not applicable.
436 ///
437 /// Reason #2 is the killer. I tried for a while to always use
438 /// overloaded logic and just check the types in constants/trans after
439 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
440 fn is_builtin_binop(lhs: Ty, rhs: Ty, op: hir::BinOp) -> bool {
441     match BinOpCategory::from(op) {
442         BinOpCategory::Shortcircuit => {
443             true
444         }
445
446         BinOpCategory::Shift => {
447             lhs.references_error() || rhs.references_error() ||
448                 lhs.is_integral() && rhs.is_integral()
449         }
450
451         BinOpCategory::Math => {
452             lhs.references_error() || rhs.references_error() ||
453                 lhs.is_integral() && rhs.is_integral() ||
454                 lhs.is_floating_point() && rhs.is_floating_point()
455         }
456
457         BinOpCategory::Bitwise => {
458             lhs.references_error() || rhs.references_error() ||
459                 lhs.is_integral() && rhs.is_integral() ||
460                 lhs.is_floating_point() && rhs.is_floating_point() ||
461                 lhs.is_bool() && rhs.is_bool()
462         }
463
464         BinOpCategory::Comparison => {
465             lhs.references_error() || rhs.references_error() ||
466                 lhs.is_scalar() && rhs.is_scalar()
467         }
468     }
469 }