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, TypeVariants};
16 use rustc::ty::TypeVariants::{TyStr, TyRef};
17 use rustc::infer::type_variable::TypeVariableOrigin;
20 use syntax::symbol::Symbol;
23 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
24 /// Check a `a <op>= b`
25 pub fn check_binop_assign(&self,
26 expr: &'gcx hir::Expr,
28 lhs_expr: &'gcx hir::Expr,
29 rhs_expr: &'gcx hir::Expr) -> Ty<'tcx>
31 let lhs_ty = self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue);
33 let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
34 let (rhs_ty, return_ty) =
35 self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
36 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
38 let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var()
39 && is_builtin_binop(lhs_ty, rhs_ty, op) {
40 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
47 if !tcx.expr_is_lval(lhs_expr) {
49 tcx.sess, lhs_expr.span,
50 E0067, "invalid left-hand side expression")
53 &format!("invalid expression for left-hand side"))
59 /// Check a potentially overloaded binary operator.
60 pub fn check_binop(&self,
61 expr: &'gcx hir::Expr,
63 lhs_expr: &'gcx hir::Expr,
64 rhs_expr: &'gcx hir::Expr) -> Ty<'tcx>
68 debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
75 let lhs_ty = self.check_expr(lhs_expr);
76 let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
78 match BinOpCategory::from(op) {
79 BinOpCategory::Shortcircuit => {
80 // && and || are a simple case.
81 let lhs_diverges = self.diverges.get();
82 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
83 self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
85 // Depending on the LHS' value, the RHS can never execute.
86 self.diverges.set(lhs_diverges);
91 // Otherwise, we always treat operators as if they are
92 // overloaded. This is the way to be most flexible w/r/t
93 // types that get inferred.
94 let (rhs_ty, return_ty) =
95 self.check_overloaded_binop(expr, lhs_expr, lhs_ty,
96 rhs_expr, op, IsAssign::No);
98 // Supply type inference hints if relevant. Probably these
99 // hints should be enforced during select as part of the
100 // `consider_unification_despite_ambiguity` routine, but this
101 // more convenient for now.
103 // The basic idea is to help type inference by taking
104 // advantage of things we know about how the impls for
105 // scalar types are arranged. This is important in a
106 // scenario like `1_u32 << 2`, because it lets us quickly
107 // deduce that the result type should be `u32`, even
108 // though we don't know yet what type 2 has and hence
109 // can't pin this down to a specific impl.
110 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
112 !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
113 is_builtin_binop(lhs_ty, rhs_ty, op)
115 let builtin_return_ty =
116 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
117 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
125 fn enforce_builtin_binop_types(&self,
126 lhs_expr: &'gcx hir::Expr,
128 rhs_expr: &'gcx hir::Expr,
133 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
136 match BinOpCategory::from(op) {
137 BinOpCategory::Shortcircuit => {
138 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
139 self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
143 BinOpCategory::Shift => {
144 // result type is same as LHS always
148 BinOpCategory::Math |
149 BinOpCategory::Bitwise => {
150 // both LHS and RHS and result will have the same type
151 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
155 BinOpCategory::Comparison => {
156 // both LHS and RHS and result will have the same type
157 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
163 fn check_overloaded_binop(&self,
164 expr: &'gcx hir::Expr,
165 lhs_expr: &'gcx hir::Expr,
167 rhs_expr: &'gcx hir::Expr,
170 -> (Ty<'tcx>, Ty<'tcx>)
172 debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
177 let (name, trait_def_id) = self.name_and_trait_def_id(op, is_assign);
179 // NB: As we have not yet type-checked the RHS, we don't have the
180 // type at hand. Make a variable to represent it. The whole reason
181 // for this indirection is so that, below, we can check the expr
182 // using this variable as the expected type, which sometimes lets
183 // us do better coercions than we would be able to do otherwise,
184 // particularly for things like `String + &String`.
185 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin::MiscVariable(rhs_expr.span));
187 let return_ty = self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var],
188 Symbol::intern(name), trait_def_id,
192 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
194 let return_ty = match return_ty {
195 Ok(return_ty) => return_ty,
197 // error types are considered "builtin"
198 if !lhs_ty.references_error() {
199 if let IsAssign::Yes = is_assign {
200 struct_span_err!(self.tcx.sess, lhs_expr.span, E0368,
201 "binary assignment operation `{}=` \
202 cannot be applied to type `{}`",
205 .span_label(lhs_expr.span,
206 &format!("cannot use `{}=` on type `{}`",
207 op.node.as_str(), lhs_ty))
210 let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
211 "binary operation `{}` cannot be applied to type `{}`",
215 if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty {
216 if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) &&
217 self.lookup_op_method(expr, ty_mut.ty, vec![rhs_ty],
218 Symbol::intern(name), trait_def_id,
222 "this is a reference to a type that `{}` can be applied \
223 to; you need to dereference this variable once for this \
229 let missing_trait = match op.node {
230 hir::BiAdd => Some("std::ops::Add"),
231 hir::BiSub => Some("std::ops::Sub"),
232 hir::BiMul => Some("std::ops::Mul"),
233 hir::BiDiv => Some("std::ops::Div"),
234 hir::BiRem => Some("std::ops::Rem"),
235 hir::BiBitAnd => Some("std::ops::BitAnd"),
236 hir::BiBitOr => Some("std::ops::BitOr"),
237 hir::BiShl => Some("std::ops::Shl"),
238 hir::BiShr => Some("std::ops::Shr"),
239 hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
240 hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
241 Some("std::cmp::PartialOrd"),
245 if let Some(missing_trait) = missing_trait {
246 if missing_trait == "std::ops::Add" &&
247 self.check_str_addition(expr, lhs_expr, lhs_ty,
248 rhs_expr, rhs_ty, &mut err) {
249 // This has nothing here because it means we did string
250 // concatenation (e.g. "Hello " + "World!"). This means
251 // we don't want the note in the else clause to be emitted
254 &format!("an implementation of `{}` might be missing for `{}`",
255 missing_trait, lhs_ty));
265 (rhs_ty_var, return_ty)
268 fn check_str_addition(&self,
269 expr: &'gcx hir::Expr,
270 lhs_expr: &'gcx hir::Expr,
272 rhs_expr: &'gcx hir::Expr,
274 mut err: &mut errors::DiagnosticBuilder) -> bool {
275 // If this function returns true it means a note was printed, so we don't need
276 // to print the normal "implementation of `std::ops::Add` might be missing" note
277 let mut is_string_addition = false;
278 if let TyRef(_, l_ty) = lhs_ty.sty {
279 if let TyRef(_, r_ty) = rhs_ty.sty {
280 if l_ty.ty.sty == TyStr && r_ty.ty.sty == TyStr {
281 err.note("`+` can't be used to concatenate two `&str` strings");
282 let codemap = self.tcx.sess.codemap();
284 match (codemap.span_to_snippet(lhs_expr.span),
285 codemap.span_to_snippet(rhs_expr.span)) {
286 (Ok(lstring), Ok(rstring)) =>
287 format!("{}.to_owned() + {}", lstring, rstring),
288 _ => format!("<expression>")
290 err.span_suggestion(expr.span,
291 &format!("to_owned() can be used to create an owned `String` \
292 from a string reference. String concatenation \
293 appends the string on the right to the string \
294 on the left and may require reallocation. This \
295 requires ownership of the string on the left."), suggestion);
296 is_string_addition = true;
306 pub fn check_user_unop(&self,
309 trait_did: Option<DefId>,
311 operand_expr: &'gcx hir::Expr,
312 operand_ty: Ty<'tcx>,
316 assert!(op.is_by_value());
317 let mname = Symbol::intern(mname);
318 match self.lookup_op_method(ex, operand_ty, vec![], mname, trait_did, operand_expr) {
321 self.type_error_message(ex.span, |actual| {
322 format!("cannot apply unary operator `{}` to type `{}`",
330 fn name_and_trait_def_id(&self,
333 -> (&'static str, Option<DefId>) {
334 let lang = &self.tcx.lang_items;
336 if let IsAssign::Yes = is_assign {
338 hir::BiAdd => ("add_assign", lang.add_assign_trait()),
339 hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
340 hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
341 hir::BiDiv => ("div_assign", lang.div_assign_trait()),
342 hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
343 hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
344 hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
345 hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
346 hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
347 hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
348 hir::BiLt | hir::BiLe |
349 hir::BiGe | hir::BiGt |
350 hir::BiEq | hir::BiNe |
351 hir::BiAnd | hir::BiOr => {
353 "impossible assignment operation: {}=",
359 hir::BiAdd => ("add", lang.add_trait()),
360 hir::BiSub => ("sub", lang.sub_trait()),
361 hir::BiMul => ("mul", lang.mul_trait()),
362 hir::BiDiv => ("div", lang.div_trait()),
363 hir::BiRem => ("rem", lang.rem_trait()),
364 hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
365 hir::BiBitAnd => ("bitand", lang.bitand_trait()),
366 hir::BiBitOr => ("bitor", lang.bitor_trait()),
367 hir::BiShl => ("shl", lang.shl_trait()),
368 hir::BiShr => ("shr", lang.shr_trait()),
369 hir::BiLt => ("lt", lang.ord_trait()),
370 hir::BiLe => ("le", lang.ord_trait()),
371 hir::BiGe => ("ge", lang.ord_trait()),
372 hir::BiGt => ("gt", lang.ord_trait()),
373 hir::BiEq => ("eq", lang.eq_trait()),
374 hir::BiNe => ("ne", lang.eq_trait()),
375 hir::BiAnd | hir::BiOr => {
376 span_bug!(op.span, "&& and || are not overloadable")
382 fn lookup_op_method(&self,
383 expr: &'gcx hir::Expr,
385 other_tys: Vec<Ty<'tcx>>,
387 trait_did: Option<DefId>,
388 lhs_expr: &'a hir::Expr)
389 -> Result<Ty<'tcx>,()>
391 debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, \
392 trait_did={:?}, lhs_expr={:?})",
399 let method = match trait_did {
401 self.lookup_method_in_trait_adjusted(expr.span,
415 let method_ty = method.ty;
417 // HACK(eddyb) Fully qualified path to work around a resolve bug.
418 let method_call = ::rustc::ty::MethodCall::expr(expr.id);
419 self.tables.borrow_mut().method_map.insert(method_call, method);
421 // extract return type for method; all late bound regions
422 // should have been instantiated by now
423 let ret_ty = method_ty.fn_ret();
424 Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
433 // Binary operator categories. These categories summarize the behavior
434 // with respect to the builtin operationrs supported.
436 /// &&, || -- cannot be overridden
439 /// <<, >> -- when shifting a single integer, rhs can be any
440 /// integer type. For simd, types must match.
443 /// +, -, etc -- takes equal types, produces same type as input,
444 /// applicable to ints/floats/simd
447 /// &, |, ^ -- takes equal types, produces same type as input,
448 /// applicable to ints/floats/simd/bool
451 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
452 /// which produce the input type
457 fn from(op: hir::BinOp) -> BinOpCategory {
459 hir::BiShl | hir::BiShr =>
460 BinOpCategory::Shift,
472 BinOpCategory::Bitwise,
480 BinOpCategory::Comparison,
484 BinOpCategory::Shortcircuit,
489 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
490 #[derive(Clone, Copy, Debug)]
496 /// Returns true if this is a built-in arithmetic operation (e.g. u32
497 /// + u32, i16x4 == i16x4) and false if these types would have to be
498 /// overloaded to be legal. There are two reasons that we distinguish
499 /// builtin operations from overloaded ones (vs trying to drive
500 /// everything uniformly through the trait system and intrinsics or
501 /// something like that):
503 /// 1. Builtin operations can trivially be evaluated in constants.
504 /// 2. For comparison operators applied to SIMD types the result is
505 /// not of type `bool`. For example, `i16x4==i16x4` yields a
506 /// type like `i16x4`. This means that the overloaded trait
507 /// `PartialEq` is not applicable.
509 /// Reason #2 is the killer. I tried for a while to always use
510 /// overloaded logic and just check the types in constants/trans after
511 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
512 fn is_builtin_binop(lhs: Ty, rhs: Ty, op: hir::BinOp) -> bool {
513 match BinOpCategory::from(op) {
514 BinOpCategory::Shortcircuit => {
518 BinOpCategory::Shift => {
519 lhs.references_error() || rhs.references_error() ||
520 lhs.is_integral() && rhs.is_integral()
523 BinOpCategory::Math => {
524 lhs.references_error() || rhs.references_error() ||
525 lhs.is_integral() && rhs.is_integral() ||
526 lhs.is_floating_point() && rhs.is_floating_point()
529 BinOpCategory::Bitwise => {
530 lhs.references_error() || rhs.references_error() ||
531 lhs.is_integral() && rhs.is_integral() ||
532 lhs.is_floating_point() && rhs.is_floating_point() ||
533 lhs.is_bool() && rhs.is_bool()
536 BinOpCategory::Comparison => {
537 lhs.references_error() || rhs.references_error() ||
538 lhs.is_scalar() && rhs.is_scalar()