]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/op.rs
Auto merge of #23678 - richo:check-flightcheck, r=alexcrichton
[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::infer;
24 use middle::traits;
25 use middle::ty::{self, Ty};
26 use syntax::ast;
27 use syntax::ast_util;
28 use syntax::parse::token;
29 use util::ppaux::{Repr, UserString};
30
31 /// Check a `a <op>= b`
32 pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
33                                    expr: &'tcx ast::Expr,
34                                    op: ast::BinOp,
35                                    lhs_expr: &'tcx ast::Expr,
36                                    rhs_expr: &'tcx ast::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(fcx.tcx(), 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!(!ty::type_is_error(lhs_ty) || !ty::type_is_error(rhs_ty));
52         span_err!(tcx.sess, lhs_expr.span, E0368,
53                   "binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
54                   ast_util::binop_to_string(op.node),
55                   lhs_ty.user_string(fcx.tcx()),
56                   rhs_ty.user_string(fcx.tcx()));
57         fcx.write_error(expr.id);
58     }
59
60     let tcx = fcx.tcx();
61     if !ty::expr_is_lval(tcx, lhs_expr) {
62         span_err!(tcx.sess, lhs_expr.span, E0067, "illegal 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 ast::Expr,
71                              op: ast::BinOp,
72                              lhs_expr: &'tcx ast::Expr,
73                              rhs_expr: &'tcx ast::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.repr(tcx),
80            op,
81            lhs_expr.repr(tcx),
82            rhs_expr.repr(tcx));
83
84     check_expr(fcx, lhs_expr);
85     let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
86
87     // Annoyingly, SIMD ops don't fit into the PartialEq/PartialOrd
88     // traits, because their return type is not bool. Perhaps this
89     // should change, but for now if LHS is SIMD we go down a
90     // different path that bypassess all traits.
91     if ty::type_is_simd(fcx.tcx(), lhs_ty) {
92         check_expr_coercable_to_type(fcx, rhs_expr, lhs_ty);
93         let rhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
94         let return_ty = enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
95         fcx.write_ty(expr.id, return_ty);
96         return;
97     }
98
99     match BinOpCategory::from(op) {
100         BinOpCategory::Shortcircuit => {
101             // && and || are a simple case.
102             demand::suptype(fcx, lhs_expr.span, ty::mk_bool(tcx), lhs_ty);
103             check_expr_coercable_to_type(fcx, rhs_expr, ty::mk_bool(tcx));
104             fcx.write_ty(expr.id, ty::mk_bool(tcx));
105         }
106         _ => {
107             // Otherwise, we always treat operators as if they are
108             // overloaded. This is the way to be most flexible w/r/t
109             // types that get inferred.
110             let (rhs_ty, return_ty) =
111                 check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op);
112
113             // Supply type inference hints if relevant. Probably these
114             // hints should be enforced during select as part of the
115             // `consider_unification_despite_ambiguity` routine, but this
116             // more convenient for now.
117             //
118             // The basic idea is to help type inference by taking
119             // advantage of things we know about how the impls for
120             // scalar types are arranged. This is important in a
121             // scenario like `1_u32 << 2`, because it lets us quickly
122             // deduce that the result type should be `u32`, even
123             // though we don't know yet what type 2 has and hence
124             // can't pin this down to a specific impl.
125             let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
126             if
127                 !ty::type_is_ty_var(lhs_ty) &&
128                 !ty::type_is_ty_var(rhs_ty) &&
129                 is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op)
130             {
131                 let builtin_return_ty =
132                     enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
133                 demand::suptype(fcx, expr.span, builtin_return_ty, return_ty);
134             }
135
136             fcx.write_ty(expr.id, return_ty);
137         }
138     }
139 }
140
141 fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
142                                          lhs_expr: &'tcx ast::Expr,
143                                          lhs_ty: Ty<'tcx>,
144                                          rhs_expr: &'tcx ast::Expr,
145                                          rhs_ty: Ty<'tcx>,
146                                          op: ast::BinOp)
147                                          -> Ty<'tcx>
148 {
149     debug_assert!(is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op));
150
151     let tcx = fcx.tcx();
152     match BinOpCategory::from(op) {
153         BinOpCategory::Shortcircuit => {
154             demand::suptype(fcx, lhs_expr.span, ty::mk_bool(tcx), lhs_ty);
155             demand::suptype(fcx, rhs_expr.span, ty::mk_bool(tcx), rhs_ty);
156             ty::mk_bool(tcx)
157         }
158
159         BinOpCategory::Shift => {
160             // For integers, the shift amount can be of any integral
161             // type. For simd, the type must match exactly.
162             if ty::type_is_simd(tcx, lhs_ty) {
163                 demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
164             }
165
166             // result type is same as LHS always
167             lhs_ty
168         }
169
170         BinOpCategory::Math |
171         BinOpCategory::Bitwise => {
172             // both LHS and RHS and result will have the same type
173             demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
174             lhs_ty
175         }
176
177         BinOpCategory::Comparison => {
178             // both LHS and RHS and result will have the same type
179             demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
180
181             // if this is simd, result is same as lhs, else bool
182             if ty::type_is_simd(tcx, lhs_ty) {
183                 let unit_ty = ty::simd_type(tcx, lhs_ty);
184                 debug!("enforce_builtin_binop_types: lhs_ty={} unit_ty={}",
185                        lhs_ty.repr(tcx),
186                        unit_ty.repr(tcx));
187                 if !ty::type_is_integral(unit_ty) {
188                     tcx.sess.span_err(
189                         lhs_expr.span,
190                         &format!("binary comparison operation `{}` not supported \
191                                   for floating point SIMD vector `{}`",
192                                  ast_util::binop_to_string(op.node),
193                                  lhs_ty.user_string(tcx)));
194                     tcx.types.err
195                 } else {
196                     lhs_ty
197                 }
198             } else {
199                 ty::mk_bool(tcx)
200             }
201         }
202     }
203 }
204
205 fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
206                                     expr: &'tcx ast::Expr,
207                                     lhs_expr: &'tcx ast::Expr,
208                                     lhs_ty: Ty<'tcx>,
209                                     rhs_expr: &'tcx ast::Expr,
210                                     op: ast::BinOp)
211                                     -> (Ty<'tcx>, Ty<'tcx>)
212 {
213     debug!("check_overloaded_binop(expr.id={}, lhs_ty={})",
214            expr.id,
215            lhs_ty.repr(fcx.tcx()));
216
217     let (name, trait_def_id) = name_and_trait_def_id(fcx, op);
218
219     // NB: As we have not yet type-checked the RHS, we don't have the
220     // type at hand. Make a variable to represent it. The whole reason
221     // for this indirection is so that, below, we can check the expr
222     // using this variable as the expected type, which sometimes lets
223     // us do better coercions than we would be able to do otherwise,
224     // particularly for things like `String + &String`.
225     let rhs_ty_var = fcx.infcx().next_ty_var();
226
227     let return_ty = match lookup_op_method(fcx, expr, lhs_ty, vec![rhs_ty_var],
228                                            token::intern(name), trait_def_id,
229                                            lhs_expr) {
230         Ok(return_ty) => return_ty,
231         Err(()) => {
232             // error types are considered "builtin"
233             if !ty::type_is_error(lhs_ty) {
234                 span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
235                           "binary operation `{}` cannot be applied to type `{}`",
236                           ast_util::binop_to_string(op.node),
237                           lhs_ty.user_string(fcx.tcx()));
238             }
239             fcx.tcx().types.err
240         }
241     };
242
243     // see `NB` above
244     check_expr_coercable_to_type(fcx, rhs_expr, rhs_ty_var);
245
246     (rhs_ty_var, return_ty)
247 }
248
249 pub fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
250                                  op_str: &str,
251                                  mname: &str,
252                                  trait_did: Option<ast::DefId>,
253                                  ex: &'tcx ast::Expr,
254                                  operand_expr: &'tcx ast::Expr,
255                                  operand_ty: Ty<'tcx>,
256                                  op: ast::UnOp)
257                                  -> Ty<'tcx>
258 {
259     assert!(ast_util::is_by_value_unop(op));
260     match lookup_op_method(fcx, ex, operand_ty, vec![],
261                            token::intern(mname), trait_did,
262                            operand_expr) {
263         Ok(t) => t,
264         Err(()) => {
265             fcx.type_error_message(ex.span, |actual| {
266                 format!("cannot apply unary operator `{}` to type `{}`",
267                         op_str, actual)
268             }, operand_ty, None);
269             fcx.tcx().types.err
270         }
271     }
272 }
273
274 fn name_and_trait_def_id(fcx: &FnCtxt, op: ast::BinOp) -> (&'static str, Option<ast::DefId>) {
275     let lang = &fcx.tcx().lang_items;
276     match op.node {
277         ast::BiAdd => ("add", lang.add_trait()),
278         ast::BiSub => ("sub", lang.sub_trait()),
279         ast::BiMul => ("mul", lang.mul_trait()),
280         ast::BiDiv => ("div", lang.div_trait()),
281         ast::BiRem => ("rem", lang.rem_trait()),
282         ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
283         ast::BiBitAnd => ("bitand", lang.bitand_trait()),
284         ast::BiBitOr => ("bitor", lang.bitor_trait()),
285         ast::BiShl => ("shl", lang.shl_trait()),
286         ast::BiShr => ("shr", lang.shr_trait()),
287         ast::BiLt => ("lt", lang.ord_trait()),
288         ast::BiLe => ("le", lang.ord_trait()),
289         ast::BiGe => ("ge", lang.ord_trait()),
290         ast::BiGt => ("gt", lang.ord_trait()),
291         ast::BiEq => ("eq", lang.eq_trait()),
292         ast::BiNe => ("ne", lang.eq_trait()),
293         ast::BiAnd | ast::BiOr => {
294             fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
295         }
296     }
297 }
298
299 fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
300                               expr: &'tcx ast::Expr,
301                               lhs_ty: Ty<'tcx>,
302                               other_tys: Vec<Ty<'tcx>>,
303                               opname: ast::Name,
304                               trait_did: Option<ast::DefId>,
305                               lhs_expr: &'a ast::Expr)
306                               -> Result<Ty<'tcx>,()>
307 {
308     debug!("lookup_op_method(expr={}, lhs_ty={}, opname={:?}, trait_did={}, lhs_expr={})",
309            expr.repr(fcx.tcx()),
310            lhs_ty.repr(fcx.tcx()),
311            opname,
312            trait_did.repr(fcx.tcx()),
313            lhs_expr.repr(fcx.tcx()));
314
315     let method = match trait_did {
316         Some(trait_did) => {
317             // We do eager coercions to make using operators
318             // more ergonomic:
319             //
320             // - If the input is of type &'a T (resp. &'a mut T),
321             //   then reborrow it to &'b T (resp. &'b mut T) where
322             //   'b <= 'a.  This makes things like `x == y`, where
323             //   `x` and `y` are both region pointers, work.  We
324             //   could also solve this with variance or different
325             //   traits that don't force left and right to have same
326             //   type.
327             let (adj_ty, adjustment) = match lhs_ty.sty {
328                 ty::ty_rptr(r_in, mt) => {
329                     let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs_expr.span));
330                     fcx.mk_subr(infer::Reborrow(lhs_expr.span), r_adj, *r_in);
331                     let adjusted_ty = ty::mk_rptr(fcx.tcx(), fcx.tcx().mk_region(r_adj), mt);
332                     let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
333                     let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
334                     (adjusted_ty, adjustment)
335                 }
336                 _ => {
337                     (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
338                 }
339             };
340
341             debug!("adjusted_ty={} adjustment={:?}",
342                    adj_ty.repr(fcx.tcx()),
343                    adjustment);
344
345             method::lookup_in_trait_adjusted(fcx, expr.span, Some(lhs_expr), opname,
346                                              trait_did, adjustment, adj_ty, Some(other_tys))
347         }
348         None => None
349     };
350
351     match method {
352         Some(method) => {
353             let method_ty = method.ty;
354
355             // HACK(eddyb) Fully qualified path to work around a resolve bug.
356             let method_call = ::middle::ty::MethodCall::expr(expr.id);
357             fcx.inh.method_map.borrow_mut().insert(method_call, method);
358
359             // extract return type for method; all late bound regions
360             // should have been instantiated by now
361             let ret_ty = ty::ty_fn_ret(method_ty);
362             Ok(ty::no_late_bound_regions(fcx.tcx(), &ret_ty).unwrap().unwrap())
363         }
364         None => {
365             Err(())
366         }
367     }
368 }
369
370 // Binary operator categories. These categories summarize the behavior
371 // with respect to the builtin operationrs supported.
372 enum BinOpCategory {
373     /// &&, || -- cannot be overridden
374     Shortcircuit,
375
376     /// <<, >> -- when shifting a single integer, rhs can be any
377     /// integer type. For simd, types must match.
378     Shift,
379
380     /// +, -, etc -- takes equal types, produces same type as input,
381     /// applicable to ints/floats/simd
382     Math,
383
384     /// &, |, ^ -- takes equal types, produces same type as input,
385     /// applicable to ints/floats/simd/bool
386     Bitwise,
387
388     /// ==, !=, etc -- takes equal types, produces bools, except for simd,
389     /// which produce the input type
390     Comparison,
391 }
392
393 impl BinOpCategory {
394     fn from(op: ast::BinOp) -> BinOpCategory {
395         match op.node {
396             ast::BiShl | ast::BiShr =>
397                 BinOpCategory::Shift,
398
399             ast::BiAdd |
400             ast::BiSub |
401             ast::BiMul |
402             ast::BiDiv |
403             ast::BiRem =>
404                 BinOpCategory::Math,
405
406             ast::BiBitXor |
407             ast::BiBitAnd |
408             ast::BiBitOr =>
409                 BinOpCategory::Bitwise,
410
411             ast::BiEq |
412             ast::BiNe |
413             ast::BiLt |
414             ast::BiLe |
415             ast::BiGe |
416             ast::BiGt =>
417                 BinOpCategory::Comparison,
418
419             ast::BiAnd |
420             ast::BiOr =>
421                 BinOpCategory::Shortcircuit,
422         }
423     }
424 }
425
426 /// Returns true if this is a built-in arithmetic operation (e.g. u32
427 /// + u32, i16x4 == i16x4) and false if these types would have to be
428 /// overloaded to be legal. There are two reasons that we distinguish
429 /// builtin operations from overloaded ones (vs trying to drive
430 /// everything uniformly through the trait system and intrinsics or
431 /// something like that):
432 ///
433 /// 1. Builtin operations can trivially be evaluated in constants.
434 /// 2. For comparison operators applied to SIMD types the result is
435 ///    not of type `bool`. For example, `i16x4==i16x4` yields a
436 ///    type like `i16x4`. This means that the overloaded trait
437 ///    `PartialEq` is not applicable.
438 ///
439 /// Reason #2 is the killer. I tried for a while to always use
440 /// overloaded logic and just check the types in constants/trans after
441 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
442 fn is_builtin_binop<'tcx>(cx: &ty::ctxt<'tcx>,
443                           lhs: Ty<'tcx>,
444                           rhs: Ty<'tcx>,
445                           op: ast::BinOp)
446                           -> bool
447 {
448     match BinOpCategory::from(op) {
449         BinOpCategory::Shortcircuit => {
450             true
451         }
452
453         BinOpCategory::Shift => {
454             ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
455                 ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
456                 ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
457         }
458
459         BinOpCategory::Math => {
460             ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
461                 ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
462                 ty::type_is_floating_point(lhs) && ty::type_is_floating_point(rhs) ||
463                 ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
464         }
465
466         BinOpCategory::Bitwise => {
467             ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
468                 ty::type_is_integral(lhs) && ty::type_is_integral(rhs) ||
469                 ty::type_is_floating_point(lhs) && ty::type_is_floating_point(rhs) ||
470                 ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs) ||
471                 ty::type_is_bool(lhs) && ty::type_is_bool(rhs)
472         }
473
474         BinOpCategory::Comparison => {
475             ty::type_is_error(lhs) || ty::type_is_error(rhs) ||
476                 ty::type_is_scalar(lhs) && ty::type_is_scalar(rhs) ||
477                 ty::type_is_simd(cx, lhs) && ty::type_is_simd(cx, rhs)
478         }
479     }
480 }
481