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.
15 check_expr_coercable_to_type,
16 check_expr_with_lvalue_pref,
21 use middle::def_id::DefId;
22 use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue};
24 use syntax::parse::token;
26 use rustc_front::util as hir_util;
28 /// Check a `a <op>= b`
29 pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
30 expr: &'tcx hir::Expr,
32 lhs_expr: &'tcx hir::Expr,
33 rhs_expr: &'tcx hir::Expr)
35 check_expr_with_lvalue_pref(fcx, lhs_expr, PreferMutLvalue);
37 let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
38 let (rhs_ty, return_ty) =
39 check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, true);
40 let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
42 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
43 enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
44 fcx.write_nil(expr.id);
46 fcx.write_ty(expr.id, return_ty);
50 if !tcx.expr_is_lval(lhs_expr) {
51 span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
55 /// Check a potentially overloaded binary operator.
56 pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
57 expr: &'tcx hir::Expr,
59 lhs_expr: &'tcx hir::Expr,
60 rhs_expr: &'tcx hir::Expr)
62 let tcx = fcx.ccx.tcx;
64 debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
71 check_expr(fcx, lhs_expr);
72 let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
74 match BinOpCategory::from(op) {
75 BinOpCategory::Shortcircuit => {
76 // && and || are a simple case.
77 demand::suptype(fcx, lhs_expr.span, tcx.mk_bool(), lhs_ty);
78 check_expr_coercable_to_type(fcx, rhs_expr, tcx.mk_bool());
79 fcx.write_ty(expr.id, tcx.mk_bool());
82 // Otherwise, we always treat operators as if they are
83 // overloaded. This is the way to be most flexible w/r/t
84 // types that get inferred.
85 let (rhs_ty, return_ty) =
86 check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, false);
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 = fcx.resolve_type_vars_if_possible(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 enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
107 demand::suptype(fcx, expr.span, builtin_return_ty, return_ty);
110 fcx.write_ty(expr.id, return_ty);
115 fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
116 lhs_expr: &'tcx hir::Expr,
118 rhs_expr: &'tcx hir::Expr,
123 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
126 match BinOpCategory::from(op) {
127 BinOpCategory::Shortcircuit => {
128 demand::suptype(fcx, lhs_expr.span, tcx.mk_bool(), lhs_ty);
129 demand::suptype(fcx, 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 demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
145 BinOpCategory::Comparison => {
146 // both LHS and RHS and result will have the same type
147 demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
153 fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
154 expr: &'tcx hir::Expr,
155 lhs_expr: &'tcx hir::Expr,
157 rhs_expr: &'tcx hir::Expr,
160 -> (Ty<'tcx>, Ty<'tcx>)
162 debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, assign={})",
167 let (name, trait_def_id) = name_and_trait_def_id(fcx, op, 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 = fcx.infcx().next_ty_var();
177 let return_ty = match lookup_op_method(fcx, 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() {
185 span_err!(fcx.tcx().sess, lhs_expr.span, E0368,
186 "binary assignment operation `{}=` cannot be applied to type `{}`",
187 hir_util::binop_to_string(op.node),
190 span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
191 "binary operation `{}` cannot be applied to type `{}`",
192 hir_util::binop_to_string(op.node),
201 check_expr_coercable_to_type(fcx, rhs_expr, rhs_ty_var);
203 (rhs_ty_var, return_ty)
206 pub fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
209 trait_did: Option<DefId>,
211 operand_expr: &'tcx hir::Expr,
212 operand_ty: Ty<'tcx>,
216 assert!(hir_util::is_by_value_unop(op));
217 match lookup_op_method(fcx, ex, operand_ty, vec![],
218 token::intern(mname), trait_did,
222 fcx.type_error_message(ex.span, |actual| {
223 format!("cannot apply unary operator `{}` to type `{}`",
225 }, operand_ty, None);
231 fn name_and_trait_def_id(fcx: &FnCtxt,
234 -> (&'static str, Option<DefId>) {
235 let lang = &fcx.tcx().lang_items;
239 hir::BiAdd => ("add_assign", lang.add_assign_trait()),
240 hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
241 hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
242 hir::BiDiv => ("div_assign", lang.div_assign_trait()),
243 hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
244 hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
245 hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
246 hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
247 hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
248 hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
249 hir::BiLt | hir::BiLe | hir::BiGe | hir::BiGt | hir::BiEq | hir::BiNe | hir::BiAnd |
251 fcx.tcx().sess.span_bug(op.span, &format!("impossible assignment operation: {}=",
252 hir_util::binop_to_string(op.node)))
257 hir::BiAdd => ("add", lang.add_trait()),
258 hir::BiSub => ("sub", lang.sub_trait()),
259 hir::BiMul => ("mul", lang.mul_trait()),
260 hir::BiDiv => ("div", lang.div_trait()),
261 hir::BiRem => ("rem", lang.rem_trait()),
262 hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
263 hir::BiBitAnd => ("bitand", lang.bitand_trait()),
264 hir::BiBitOr => ("bitor", lang.bitor_trait()),
265 hir::BiShl => ("shl", lang.shl_trait()),
266 hir::BiShr => ("shr", lang.shr_trait()),
267 hir::BiLt => ("lt", lang.ord_trait()),
268 hir::BiLe => ("le", lang.ord_trait()),
269 hir::BiGe => ("ge", lang.ord_trait()),
270 hir::BiGt => ("gt", lang.ord_trait()),
271 hir::BiEq => ("eq", lang.eq_trait()),
272 hir::BiNe => ("ne", lang.eq_trait()),
273 hir::BiAnd | hir::BiOr => {
274 fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
280 fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
281 expr: &'tcx hir::Expr,
283 other_tys: Vec<Ty<'tcx>>,
285 trait_did: Option<DefId>,
286 lhs_expr: &'a hir::Expr)
287 -> Result<Ty<'tcx>,()>
289 debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})",
296 let method = match trait_did {
298 method::lookup_in_trait_adjusted(fcx,
313 let method_ty = method.ty;
315 // HACK(eddyb) Fully qualified path to work around a resolve bug.
316 let method_call = ::middle::ty::MethodCall::expr(expr.id);
317 fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
319 // extract return type for method; all late bound regions
320 // should have been instantiated by now
321 let ret_ty = method_ty.fn_ret();
322 Ok(fcx.tcx().no_late_bound_regions(&ret_ty).unwrap().unwrap())
330 // Binary operator categories. These categories summarize the behavior
331 // with respect to the builtin operationrs supported.
333 /// &&, || -- cannot be overridden
336 /// <<, >> -- when shifting a single integer, rhs can be any
337 /// integer type. For simd, types must match.
340 /// +, -, etc -- takes equal types, produces same type as input,
341 /// applicable to ints/floats/simd
344 /// &, |, ^ -- takes equal types, produces same type as input,
345 /// applicable to ints/floats/simd/bool
348 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
349 /// which produce the input type
354 fn from(op: hir::BinOp) -> BinOpCategory {
356 hir::BiShl | hir::BiShr =>
357 BinOpCategory::Shift,
369 BinOpCategory::Bitwise,
377 BinOpCategory::Comparison,
381 BinOpCategory::Shortcircuit,
386 /// Returns true if this is a built-in arithmetic operation (e.g. u32
387 /// + u32, i16x4 == i16x4) and false if these types would have to be
388 /// overloaded to be legal. There are two reasons that we distinguish
389 /// builtin operations from overloaded ones (vs trying to drive
390 /// everything uniformly through the trait system and intrinsics or
391 /// something like that):
393 /// 1. Builtin operations can trivially be evaluated in constants.
394 /// 2. For comparison operators applied to SIMD types the result is
395 /// not of type `bool`. For example, `i16x4==i16x4` yields a
396 /// type like `i16x4`. This means that the overloaded trait
397 /// `PartialEq` is not applicable.
399 /// Reason #2 is the killer. I tried for a while to always use
400 /// overloaded logic and just check the types in constants/trans after
401 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
402 fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>,
407 match BinOpCategory::from(op) {
408 BinOpCategory::Shortcircuit => {
412 BinOpCategory::Shift => {
413 lhs.references_error() || rhs.references_error() ||
414 lhs.is_integral() && rhs.is_integral()
417 BinOpCategory::Math => {
418 lhs.references_error() || rhs.references_error() ||
419 lhs.is_integral() && rhs.is_integral() ||
420 lhs.is_floating_point() && rhs.is_floating_point()
423 BinOpCategory::Bitwise => {
424 lhs.references_error() || rhs.references_error() ||
425 lhs.is_integral() && rhs.is_integral() ||
426 lhs.is_floating_point() && rhs.is_floating_point() ||
427 lhs.is_bool() && rhs.is_bool()
430 BinOpCategory::Comparison => {
431 lhs.references_error() || rhs.references_error() ||
432 lhs.is_scalar() && rhs.is_scalar()