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