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