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