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