]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
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 | }; | |
c34b1796 | 23 | use middle::traits; |
c1a9b12d | 24 | use middle::ty::{self, Ty, HasTypeFlags}; |
c34b1796 AL |
25 | use syntax::ast; |
26 | use syntax::ast_util; | |
27 | use syntax::parse::token; | |
c34b1796 AL |
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" | |
c1a9b12d | 49 | assert!(!lhs_ty.references_error() || !rhs_ty.references_error()); |
c34b1796 AL |
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), | |
62682a34 SL |
53 | lhs_ty, |
54 | rhs_ty); | |
c34b1796 AL |
55 | fcx.write_error(expr.id); |
56 | } | |
57 | ||
58 | let tcx = fcx.tcx(); | |
c1a9b12d SL |
59 | if !tcx.expr_is_lval(lhs_expr) { |
60 | span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression"); | |
c34b1796 AL |
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 | ||
62682a34 | 75 | debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})", |
c34b1796 | 76 | expr.id, |
62682a34 | 77 | expr, |
c34b1796 | 78 | op, |
62682a34 SL |
79 | lhs_expr, |
80 | rhs_expr); | |
c34b1796 AL |
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. | |
c1a9b12d | 89 | if lhs_ty.is_simd(fcx.tcx()) { |
c34b1796 AL |
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. | |
c1a9b12d SL |
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()); | |
c34b1796 AL |
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 | |
c1a9b12d | 125 | !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && |
c34b1796 AL |
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 => { | |
c1a9b12d SL |
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() | |
c34b1796 AL |
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. | |
c1a9b12d | 159 | if lhs_ty.is_simd(tcx) { |
c34b1796 AL |
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 | |
c1a9b12d SL |
179 | if lhs_ty.is_simd(tcx) { |
180 | let unit_ty = lhs_ty.simd_type(tcx); | |
62682a34 SL |
181 | debug!("enforce_builtin_binop_types: lhs_ty={:?} unit_ty={:?}", |
182 | lhs_ty, | |
183 | unit_ty); | |
c1a9b12d | 184 | if !unit_ty.is_integral() { |
c34b1796 AL |
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), | |
62682a34 | 190 | lhs_ty)); |
c34b1796 AL |
191 | tcx.types.err |
192 | } else { | |
193 | lhs_ty | |
194 | } | |
195 | } else { | |
c1a9b12d | 196 | tcx.mk_bool() |
c34b1796 AL |
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 | { | |
62682a34 | 210 | debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?})", |
c34b1796 | 211 | expr.id, |
62682a34 | 212 | lhs_ty); |
c34b1796 AL |
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" | |
c1a9b12d | 230 | if !lhs_ty.references_error() { |
c34b1796 AL |
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), | |
62682a34 | 234 | lhs_ty); |
c34b1796 AL |
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 | { | |
62682a34 SL |
305 | debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})", |
306 | expr, | |
307 | lhs_ty, | |
c34b1796 | 308 | opname, |
62682a34 SL |
309 | trait_did, |
310 | lhs_expr); | |
c34b1796 AL |
311 | |
312 | let method = match trait_did { | |
313 | Some(trait_did) => { | |
9346a6ac AL |
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)) | |
c34b1796 AL |
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); | |
c1a9b12d | 333 | fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); |
c34b1796 AL |
334 | |
335 | // extract return type for method; all late bound regions | |
336 | // should have been instantiated by now | |
c1a9b12d SL |
337 | let ret_ty = method_ty.fn_ret(); |
338 | Ok(fcx.tcx().no_late_bound_regions(&ret_ty).unwrap().unwrap()) | |
c34b1796 AL |
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 => { | |
c1a9b12d SL |
430 | lhs.references_error() || rhs.references_error() || |
431 | lhs.is_integral() && rhs.is_integral() || | |
432 | lhs.is_simd(cx) && rhs.is_simd(cx) | |
c34b1796 AL |
433 | } |
434 | ||
435 | BinOpCategory::Math => { | |
c1a9b12d SL |
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) | |
c34b1796 AL |
440 | } |
441 | ||
442 | BinOpCategory::Bitwise => { | |
c1a9b12d SL |
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() | |
c34b1796 AL |
448 | } |
449 | ||
450 | BinOpCategory::Comparison => { | |
c1a9b12d SL |
451 | lhs.references_error() || rhs.references_error() || |
452 | lhs.is_scalar() && rhs.is_scalar() || | |
453 | lhs.is_simd(cx) && rhs.is_simd(cx) | |
c34b1796 AL |
454 | } |
455 | } | |
456 | } |