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.
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.
11 //! Code related to processing overloaded binary and unary operators.
14 use hir::def_id::DefId;
15 use rustc::ty::{Ty, TypeFoldable, PreferMutLvalue};
17 use syntax::parse::token;
20 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21 /// Check a `a <op>= b`
22 pub fn check_binop_assign(&self,
23 expr: &'gcx hir::Expr,
25 lhs_expr: &'gcx hir::Expr,
26 rhs_expr: &'gcx hir::Expr)
28 self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue);
30 let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
31 let (rhs_ty, return_ty) =
32 self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
33 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
35 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
36 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
37 self.write_nil(expr.id);
39 self.write_ty(expr.id, return_ty);
43 if !tcx.expr_is_lval(lhs_expr) {
45 tcx.sess, lhs_expr.span,
46 E0067, "invalid left-hand side expression")
49 &format!("invalid expression for left-hand side"))
54 /// Check a potentially overloaded binary operator.
55 pub fn check_binop(&self,
56 expr: &'gcx hir::Expr,
58 lhs_expr: &'gcx hir::Expr,
59 rhs_expr: &'gcx hir::Expr)
63 debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
70 self.check_expr(lhs_expr);
71 let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
73 match BinOpCategory::from(op) {
74 BinOpCategory::Shortcircuit => {
75 // && and || are a simple case.
76 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
77 self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
78 self.write_ty(expr.id, tcx.mk_bool());
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 self.check_overloaded_binop(expr, lhs_expr, lhs_ty,
86 rhs_expr, op, IsAssign::No);
88 // Supply type inference hints if relevant. Probably these
89 // hints should be enforced during select as part of the
90 // `consider_unification_despite_ambiguity` routine, but this
91 // more convenient for now.
93 // The basic idea is to help type inference by taking
94 // advantage of things we know about how the impls for
95 // scalar types are arranged. This is important in a
96 // scenario like `1_u32 << 2`, because it lets us quickly
97 // deduce that the result type should be `u32`, even
98 // though we don't know yet what type 2 has and hence
99 // can't pin this down to a specific impl.
100 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
102 !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
103 is_builtin_binop(lhs_ty, rhs_ty, op)
105 let builtin_return_ty =
106 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
107 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
110 self.write_ty(expr.id, return_ty);
115 fn enforce_builtin_binop_types(&self,
116 lhs_expr: &'gcx hir::Expr,
118 rhs_expr: &'gcx hir::Expr,
123 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
126 match BinOpCategory::from(op) {
127 BinOpCategory::Shortcircuit => {
128 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
129 self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
133 BinOpCategory::Shift => {
134 // result type is same as LHS always
138 BinOpCategory::Math |
139 BinOpCategory::Bitwise => {
140 // both LHS and RHS and result will have the same type
141 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
145 BinOpCategory::Comparison => {
146 // both LHS and RHS and result will have the same type
147 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
153 fn check_overloaded_binop(&self,
154 expr: &'gcx hir::Expr,
155 lhs_expr: &'gcx hir::Expr,
157 rhs_expr: &'gcx hir::Expr,
160 -> (Ty<'tcx>, Ty<'tcx>)
162 debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
167 let (name, trait_def_id) = self.name_and_trait_def_id(op, is_assign);
169 // NB: As we have not yet type-checked the RHS, we don't have the
170 // type at hand. Make a variable to represent it. The whole reason
171 // for this indirection is so that, below, we can check the expr
172 // using this variable as the expected type, which sometimes lets
173 // us do better coercions than we would be able to do otherwise,
174 // particularly for things like `String + &String`.
175 let rhs_ty_var = self.next_ty_var();
177 let return_ty = match self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var],
178 token::intern(name), trait_def_id,
180 Ok(return_ty) => return_ty,
182 // error types are considered "builtin"
183 if !lhs_ty.references_error() {
184 if let IsAssign::Yes = is_assign {
185 struct_span_err!(self.tcx.sess, lhs_expr.span, E0368,
186 "binary assignment operation `{}=` \
187 cannot be applied to type `{}`",
190 .span_label(lhs_expr.span,
191 &format!("cannot use `{}=` on type `{}`",
192 op.node.as_str(), lhs_ty))
195 let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
196 "binary operation `{}` cannot be applied to type `{}`",
199 let missing_trait = match op.node {
200 hir::BiAdd => Some("std::ops::Add"),
201 hir::BiSub => Some("std::ops::Sub"),
202 hir::BiMul => Some("std::ops::Mul"),
203 hir::BiDiv => Some("std::ops::Div"),
204 hir::BiRem => Some("std::ops::Rem"),
205 hir::BiBitAnd => Some("std::ops::BitAnd"),
206 hir::BiBitOr => Some("std::ops::BitOr"),
207 hir::BiShl => Some("std::ops::Shl"),
208 hir::BiShr => Some("std::ops::Shr"),
209 hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
210 hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
211 Some("std::cmp::PartialOrd"),
215 if let Some(missing_trait) = missing_trait {
216 span_note!(&mut err, lhs_expr.span,
217 "an implementation of `{}` might be missing for `{}`",
218 missing_trait, lhs_ty);
228 self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
230 (rhs_ty_var, return_ty)
233 pub fn check_user_unop(&self,
236 trait_did: Option<DefId>,
238 operand_expr: &'gcx hir::Expr,
239 operand_ty: Ty<'tcx>,
243 assert!(op.is_by_value());
244 match self.lookup_op_method(ex, operand_ty, vec![],
245 token::intern(mname), trait_did,
249 self.type_error_message(ex.span, |actual| {
250 format!("cannot apply unary operator `{}` to type `{}`",
258 fn name_and_trait_def_id(&self,
261 -> (&'static str, Option<DefId>) {
262 let lang = &self.tcx.lang_items;
264 if let IsAssign::Yes = is_assign {
266 hir::BiAdd => ("add_assign", lang.add_assign_trait()),
267 hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
268 hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
269 hir::BiDiv => ("div_assign", lang.div_assign_trait()),
270 hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
271 hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
272 hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
273 hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
274 hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
275 hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
276 hir::BiLt | hir::BiLe |
277 hir::BiGe | hir::BiGt |
278 hir::BiEq | hir::BiNe |
279 hir::BiAnd | hir::BiOr => {
281 "impossible assignment operation: {}=",
287 hir::BiAdd => ("add", lang.add_trait()),
288 hir::BiSub => ("sub", lang.sub_trait()),
289 hir::BiMul => ("mul", lang.mul_trait()),
290 hir::BiDiv => ("div", lang.div_trait()),
291 hir::BiRem => ("rem", lang.rem_trait()),
292 hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
293 hir::BiBitAnd => ("bitand", lang.bitand_trait()),
294 hir::BiBitOr => ("bitor", lang.bitor_trait()),
295 hir::BiShl => ("shl", lang.shl_trait()),
296 hir::BiShr => ("shr", lang.shr_trait()),
297 hir::BiLt => ("lt", lang.ord_trait()),
298 hir::BiLe => ("le", lang.ord_trait()),
299 hir::BiGe => ("ge", lang.ord_trait()),
300 hir::BiGt => ("gt", lang.ord_trait()),
301 hir::BiEq => ("eq", lang.eq_trait()),
302 hir::BiNe => ("ne", lang.eq_trait()),
303 hir::BiAnd | hir::BiOr => {
304 span_bug!(op.span, "&& and || are not overloadable")
310 fn lookup_op_method(&self,
311 expr: &'gcx hir::Expr,
313 other_tys: Vec<Ty<'tcx>>,
315 trait_did: Option<DefId>,
316 lhs_expr: &'a hir::Expr)
317 -> Result<Ty<'tcx>,()>
319 debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, \
320 trait_did={:?}, lhs_expr={:?})",
327 let method = match trait_did {
329 self.lookup_method_in_trait_adjusted(expr.span,
343 let method_ty = method.ty;
345 // HACK(eddyb) Fully qualified path to work around a resolve bug.
346 let method_call = ::rustc::ty::MethodCall::expr(expr.id);
347 self.tables.borrow_mut().method_map.insert(method_call, method);
349 // extract return type for method; all late bound regions
350 // should have been instantiated by now
351 let ret_ty = method_ty.fn_ret();
352 Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
361 // Binary operator categories. These categories summarize the behavior
362 // with respect to the builtin operationrs supported.
364 /// &&, || -- cannot be overridden
367 /// <<, >> -- when shifting a single integer, rhs can be any
368 /// integer type. For simd, types must match.
371 /// +, -, etc -- takes equal types, produces same type as input,
372 /// applicable to ints/floats/simd
375 /// &, |, ^ -- takes equal types, produces same type as input,
376 /// applicable to ints/floats/simd/bool
379 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
380 /// which produce the input type
385 fn from(op: hir::BinOp) -> BinOpCategory {
387 hir::BiShl | hir::BiShr =>
388 BinOpCategory::Shift,
400 BinOpCategory::Bitwise,
408 BinOpCategory::Comparison,
412 BinOpCategory::Shortcircuit,
417 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
418 #[derive(Clone, Copy, Debug)]
424 /// Returns true if this is a built-in arithmetic operation (e.g. u32
425 /// + u32, i16x4 == i16x4) and false if these types would have to be
426 /// overloaded to be legal. There are two reasons that we distinguish
427 /// builtin operations from overloaded ones (vs trying to drive
428 /// everything uniformly through the trait system and intrinsics or
429 /// something like that):
431 /// 1. Builtin operations can trivially be evaluated in constants.
432 /// 2. For comparison operators applied to SIMD types the result is
433 /// not of type `bool`. For example, `i16x4==i16x4` yields a
434 /// type like `i16x4`. This means that the overloaded trait
435 /// `PartialEq` is not applicable.
437 /// Reason #2 is the killer. I tried for a while to always use
438 /// overloaded logic and just check the types in constants/trans after
439 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
440 fn is_builtin_binop(lhs: Ty, rhs: Ty, op: hir::BinOp) -> bool {
441 match BinOpCategory::from(op) {
442 BinOpCategory::Shortcircuit => {
446 BinOpCategory::Shift => {
447 lhs.references_error() || rhs.references_error() ||
448 lhs.is_integral() && rhs.is_integral()
451 BinOpCategory::Math => {
452 lhs.references_error() || rhs.references_error() ||
453 lhs.is_integral() && rhs.is_integral() ||
454 lhs.is_floating_point() && rhs.is_floating_point()
457 BinOpCategory::Bitwise => {
458 lhs.references_error() || rhs.references_error() ||
459 lhs.is_integral() && rhs.is_integral() ||
460 lhs.is_floating_point() && rhs.is_floating_point() ||
461 lhs.is_bool() && rhs.is_bool()
464 BinOpCategory::Comparison => {
465 lhs.references_error() || rhs.references_error() ||
466 lhs.is_scalar() && rhs.is_scalar()