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