1 //! Code related to processing overloaded binary and unary operators.
3 use super::{FnCtxt, Needs};
4 use super::method::MethodCallee;
5 use rustc::ty::{self, Ty, TypeFoldable};
6 use rustc::ty::TyKind::{Ref, Adt, FnDef, Str, Uint, Never, Tuple, Char, Array};
7 use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
8 use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
9 use errors::{self,Applicability};
11 use syntax::ast::Ident;
14 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15 /// Checks a `a <op>= b`
16 pub fn check_binop_assign(
18 expr: &'tcx hir::Expr,
20 lhs_expr: &'tcx hir::Expr,
21 rhs_expr: &'tcx hir::Expr,
23 let (lhs_ty, rhs_ty, return_ty) =
24 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::Yes);
26 let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var()
27 && is_builtin_binop(lhs_ty, rhs_ty, op) {
28 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
34 if !lhs_expr.is_place_expr() {
36 self.tcx.sess, lhs_expr.span,
37 E0067, "invalid left-hand side expression")
40 "invalid expression for left-hand side")
46 /// Checks a potentially overloaded binary operator.
49 expr: &'tcx hir::Expr,
51 lhs_expr: &'tcx hir::Expr,
52 rhs_expr: &'tcx hir::Expr,
56 debug!("check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
63 match BinOpCategory::from(op) {
64 BinOpCategory::Shortcircuit => {
65 // && and || are a simple case.
66 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool);
67 let lhs_diverges = self.diverges.get();
68 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool);
70 // Depending on the LHS' value, the RHS can never execute.
71 self.diverges.set(lhs_diverges);
76 // Otherwise, we always treat operators as if they are
77 // overloaded. This is the way to be most flexible w/r/t
78 // types that get inferred.
79 let (lhs_ty, rhs_ty, return_ty) =
80 self.check_overloaded_binop(expr, lhs_expr,
81 rhs_expr, op, IsAssign::No);
83 // Supply type inference hints if relevant. Probably these
84 // hints should be enforced during select as part of the
85 // `consider_unification_despite_ambiguity` routine, but this
86 // more convenient for now.
88 // The basic idea is to help type inference by taking
89 // advantage of things we know about how the impls for
90 // scalar types are arranged. This is important in a
91 // scenario like `1_u32 << 2`, because it lets us quickly
92 // deduce that the result type should be `u32`, even
93 // though we don't know yet what type 2 has and hence
94 // can't pin this down to a specific impl.
96 !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
97 is_builtin_binop(lhs_ty, rhs_ty, op)
99 let builtin_return_ty =
100 self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
101 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
109 fn enforce_builtin_binop_types(
111 lhs_expr: &'tcx hir::Expr,
113 rhs_expr: &'tcx hir::Expr,
117 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
120 match BinOpCategory::from(op) {
121 BinOpCategory::Shortcircuit => {
122 self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
123 self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
127 BinOpCategory::Shift => {
128 // result type is same as LHS always
132 BinOpCategory::Math |
133 BinOpCategory::Bitwise => {
134 // both LHS and RHS and result will have the same type
135 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
139 BinOpCategory::Comparison => {
140 // both LHS and RHS and result will have the same type
141 self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
147 fn check_overloaded_binop(
149 expr: &'tcx hir::Expr,
150 lhs_expr: &'tcx hir::Expr,
151 rhs_expr: &'tcx hir::Expr,
154 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
155 debug!("check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
160 let lhs_ty = match is_assign {
162 // Find a suitable supertype of the LHS expression's type, by coercing to
163 // a type variable, to pass as the `Self` to the trait, avoiding invariant
164 // trait matching creating lifetime constraints that are too strict.
165 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
166 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
167 let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None);
168 let fresh_var = self.next_ty_var(TypeVariableOrigin {
169 kind: TypeVariableOriginKind::MiscVariable,
172 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No)
175 // rust-lang/rust#52126: We have to use strict
176 // equivalence on the LHS of an assign-op like `+=`;
177 // overwritten or mutably-borrowed places cannot be
178 // coerced to a supertype.
179 self.check_expr_with_needs(lhs_expr, Needs::MutPlace)
182 let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
184 // N.B., as we have not yet type-checked the RHS, we don't have the
185 // type at hand. Make a variable to represent it. The whole reason
186 // for this indirection is so that, below, we can check the expr
187 // using this variable as the expected type, which sometimes lets
188 // us do better coercions than we would be able to do otherwise,
189 // particularly for things like `String + &String`.
190 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
191 kind: TypeVariableOriginKind::MiscVariable,
195 let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
198 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
199 let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
201 let return_ty = match result {
203 let by_ref_binop = !op.node.is_by_value();
204 if is_assign == IsAssign::Yes || by_ref_binop {
205 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].sty {
206 let mutbl = match mutbl {
207 hir::MutImmutable => AutoBorrowMutability::Immutable,
208 hir::MutMutable => AutoBorrowMutability::Mutable {
209 // Allow two-phase borrows for binops in initial deployment
210 // since they desugar to methods
211 allow_two_phase_borrow: AllowTwoPhase::Yes,
214 let autoref = Adjustment {
215 kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
216 target: method.sig.inputs()[0]
218 self.apply_adjustments(lhs_expr, vec![autoref]);
222 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].sty {
223 let mutbl = match mutbl {
224 hir::MutImmutable => AutoBorrowMutability::Immutable,
225 hir::MutMutable => AutoBorrowMutability::Mutable {
226 // Allow two-phase borrows for binops in initial deployment
227 // since they desugar to methods
228 allow_two_phase_borrow: AllowTwoPhase::Yes,
231 let autoref = Adjustment {
232 kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
233 target: method.sig.inputs()[1]
235 // HACK(eddyb) Bypass checks due to reborrows being in
236 // some cases applied on the RHS, on top of which we need
237 // to autoref, which is not allowed by apply_adjustments.
238 // self.apply_adjustments(rhs_expr, vec![autoref]);
242 .entry(rhs_expr.hir_id)
247 self.write_method_call(expr.hir_id, method);
252 // error types are considered "builtin"
253 if !lhs_ty.references_error() {
254 let source_map = self.tcx.sess.source_map();
257 let mut err = struct_span_err!(
261 "binary assignment operation `{}=` cannot be applied to type `{}`",
267 format!("cannot use `{}=` on type `{}`",
268 op.node.as_str(), lhs_ty),
270 let mut suggested_deref = false;
271 if let Ref(_, mut rty, _) = lhs_ty.sty {
273 self.infcx.type_is_copy_modulo_regions(self.param_env,
276 self.lookup_op_method(rty,
278 Op::Binary(op, is_assign))
281 if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
282 while let Ref(_, rty_inner, _) = rty.sty {
286 "`{}=` can be used on '{}', you can dereference `{}`",
294 format!("*{}", lstring),
295 errors::Applicability::MachineApplicable,
297 suggested_deref = true;
301 let missing_trait = match op.node {
302 hir::BinOpKind::Add => Some("std::ops::AddAssign"),
303 hir::BinOpKind::Sub => Some("std::ops::SubAssign"),
304 hir::BinOpKind::Mul => Some("std::ops::MulAssign"),
305 hir::BinOpKind::Div => Some("std::ops::DivAssign"),
306 hir::BinOpKind::Rem => Some("std::ops::RemAssign"),
307 hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"),
308 hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"),
309 hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"),
310 hir::BinOpKind::Shl => Some("std::ops::ShlAssign"),
311 hir::BinOpKind::Shr => Some("std::ops::ShrAssign"),
314 if let Some(missing_trait) = missing_trait {
315 if op.node == hir::BinOpKind::Add &&
316 self.check_str_addition(
317 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, true, op) {
318 // This has nothing here because it means we did string
319 // concatenation (e.g., "Hello " += "World!"). This means
320 // we don't want the note in the else clause to be emitted
321 } else if let ty::Param(_) = lhs_ty.sty {
322 // FIXME: point to span of param
324 "`{}` might need a bound for `{}`",
325 lhs_ty, missing_trait
327 } else if !suggested_deref {
329 "an implementation of `{}` might \
330 be missing for `{}`",
331 missing_trait, lhs_ty
338 let mut err = struct_span_err!(self.tcx.sess, op.span, E0369,
339 "binary operation `{}` cannot be applied to type `{}`",
343 let mut involves_fn = false;
344 if !lhs_expr.span.eq(&rhs_expr.span) {
345 involves_fn |= self.add_type_neq_err_label(
353 involves_fn |= self.add_type_neq_err_label(
363 let mut suggested_deref = false;
364 if let Ref(_, mut rty, _) = lhs_ty.sty {
366 self.infcx.type_is_copy_modulo_regions(self.param_env,
369 self.lookup_op_method(rty,
371 Op::Binary(op, is_assign))
374 if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
375 while let Ref(_, rty_inner, _) = rty.sty {
379 "`{}` can be used on '{}', you can \
380 dereference `{2}`: `*{2}`",
386 suggested_deref = true;
390 let missing_trait = match op.node {
391 hir::BinOpKind::Add => Some("std::ops::Add"),
392 hir::BinOpKind::Sub => Some("std::ops::Sub"),
393 hir::BinOpKind::Mul => Some("std::ops::Mul"),
394 hir::BinOpKind::Div => Some("std::ops::Div"),
395 hir::BinOpKind::Rem => Some("std::ops::Rem"),
396 hir::BinOpKind::BitAnd => Some("std::ops::BitAnd"),
397 hir::BinOpKind::BitXor => Some("std::ops::BitXor"),
398 hir::BinOpKind::BitOr => Some("std::ops::BitOr"),
399 hir::BinOpKind::Shl => Some("std::ops::Shl"),
400 hir::BinOpKind::Shr => Some("std::ops::Shr"),
402 hir::BinOpKind::Ne => Some("std::cmp::PartialEq"),
406 hir::BinOpKind::Ge => Some("std::cmp::PartialOrd"),
409 if let Some(missing_trait) = missing_trait {
410 if op.node == hir::BinOpKind::Add &&
411 self.check_str_addition(
412 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, false, op) {
413 // This has nothing here because it means we did string
414 // concatenation (e.g., "Hello " + "World!"). This means
415 // we don't want the note in the else clause to be emitted
416 } else if let ty::Param(_) = lhs_ty.sty {
417 // FIXME: point to span of param
419 "`{}` might need a bound for `{}`",
420 lhs_ty, missing_trait
422 } else if !suggested_deref && !involves_fn {
424 "an implementation of `{}` might \
425 be missing for `{}`",
426 missing_trait, lhs_ty
438 (lhs_ty, rhs_ty, return_ty)
441 /// If one of the types is an uncalled function and calling it would yield the other type,
442 /// suggest calling the function. Returns wether a suggestion was given.
443 fn add_type_neq_err_label(
445 err: &mut errors::DiagnosticBuilder<'_>,
451 ) -> bool /* did we suggest to call a function because of missing parenthesis? */ {
452 err.span_label(span, ty.to_string());
453 if let FnDef(def_id, _) = ty.sty {
454 let source_map = self.tcx.sess.source_map();
455 let hir_id = match self.tcx.hir().as_local_hir_id(def_id) {
456 Some(hir_id) => hir_id,
457 None => return false,
459 if self.tcx.has_typeck_tables(def_id) == false {
463 match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) {
464 Some(f) => f.clone(),
466 bug!("No fn-sig entry for def_id={:?}", def_id);
471 let other_ty = if let FnDef(def_id, _) = other_ty.sty {
472 let hir_id = match self.tcx.hir().as_local_hir_id(def_id) {
473 Some(hir_id) => hir_id,
474 None => return false,
476 if self.tcx.has_typeck_tables(def_id) == false {
479 match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) {
480 Some(f) => f.clone().output(),
482 bug!("No fn-sig entry for def_id={:?}", def_id);
489 if self.lookup_op_method(fn_sig.output(),
491 Op::Binary(op, is_assign))
493 let (variable_snippet, applicability) = if fn_sig.inputs().len() > 0 {
494 (format!("{}( /* arguments */ )", source_map.span_to_snippet(span).unwrap()),
495 Applicability::HasPlaceholders)
497 (format!("{}()", source_map.span_to_snippet(span).unwrap()),
498 Applicability::MaybeIncorrect)
503 "you might have forgotten to call this function",
513 /// Provide actionable suggestions when trying to add two strings with incorrect types,
514 /// like `&str + &str`, `String + String` and `&str + &String`.
516 /// If this function returns `true` it means a note was printed, so we don't need
517 /// to print the normal "implementation of `std::ops::Add` might be missing" note
518 fn check_str_addition(
520 lhs_expr: &'tcx hir::Expr,
521 rhs_expr: &'tcx hir::Expr,
524 err: &mut errors::DiagnosticBuilder<'_>,
528 let source_map = self.tcx.sess.source_map();
529 let remove_borrow_msg = "String concatenation appends the string on the right to the \
530 string on the left and may require reallocation. This \
531 requires ownership of the string on the left";
533 let msg = "`to_owned()` can be used to create an owned `String` \
534 from a string reference. String concatenation \
535 appends the string on the right to the string \
536 on the left and may require reallocation. This \
537 requires ownership of the string on the left";
539 let is_std_string = |ty| &format!("{:?}", ty) == "std::string::String";
541 match (&lhs_ty.sty, &rhs_ty.sty) {
542 (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
543 if (l_ty.sty == Str || is_std_string(l_ty)) && (
544 r_ty.sty == Str || is_std_string(r_ty) ||
545 &format!("{:?}", rhs_ty) == "&&str"
548 if !is_assign { // Do not supply this message if `&str += &str`
551 "`+` cannot be used to concatenate two `&str` strings",
553 match source_map.span_to_snippet(lhs_expr.span) {
557 if lstring.starts_with("&") {
562 if lstring.starts_with("&") {
563 // let a = String::new();
564 // let _ = &a + "bar";
565 format!("{}", &lstring[1..])
567 format!("{}.to_owned()", lstring)
569 Applicability::MachineApplicable,
577 (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
578 if (l_ty.sty == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
582 "`+` cannot be used to concatenate a `&str` with a `String`",
585 source_map.span_to_snippet(lhs_expr.span),
586 source_map.span_to_snippet(rhs_expr.span),
589 (Ok(l), Ok(r), false) => {
590 let to_string = if l.starts_with("&") {
591 // let a = String::new(); let b = String::new();
593 format!("{}", &l[1..])
595 format!("{}.to_owned()", l)
597 err.multipart_suggestion(
600 (lhs_expr.span, to_string),
601 (rhs_expr.span, format!("&{}", r)),
603 Applicability::MachineApplicable,
616 pub fn check_user_unop(
619 operand_ty: Ty<'tcx>,
622 assert!(op.is_by_value());
623 match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) {
625 self.write_method_call(ex.hir_id, method);
629 let actual = self.resolve_vars_if_possible(&operand_ty);
630 if !actual.references_error() {
631 let mut err = struct_span_err!(self.tcx.sess, ex.span, E0600,
632 "cannot apply unary operator `{}` to type `{}`",
633 op.as_str(), actual);
634 err.span_label(ex.span, format!("cannot apply unary \
635 operator `{}`", op.as_str()));
637 Uint(_) if op == hir::UnNeg => {
638 err.note("unsigned values cannot be negated");
640 Str | Never | Char | Tuple(_) | Array(_,_) => {},
641 Ref(_, ref lty, _) if lty.sty == Str => {},
643 let missing_trait = match op {
644 hir::UnNeg => "std::ops::Neg",
645 hir::UnNot => "std::ops::Not",
646 hir::UnDeref => "std::ops::UnDerf"
648 err.note(&format!("an implementation of `{}` might \
649 be missing for `{}`",
650 missing_trait, operand_ty));
660 fn lookup_op_method(&self, lhs_ty: Ty<'tcx>, other_tys: &[Ty<'tcx>], op: Op)
661 -> Result<MethodCallee<'tcx>, ()>
663 let lang = self.tcx.lang_items();
665 let span = match op {
666 Op::Binary(op, _) => op.span,
667 Op::Unary(_, span) => span
669 let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
671 hir::BinOpKind::Add => ("add_assign", lang.add_assign_trait()),
672 hir::BinOpKind::Sub => ("sub_assign", lang.sub_assign_trait()),
673 hir::BinOpKind::Mul => ("mul_assign", lang.mul_assign_trait()),
674 hir::BinOpKind::Div => ("div_assign", lang.div_assign_trait()),
675 hir::BinOpKind::Rem => ("rem_assign", lang.rem_assign_trait()),
676 hir::BinOpKind::BitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
677 hir::BinOpKind::BitAnd => ("bitand_assign", lang.bitand_assign_trait()),
678 hir::BinOpKind::BitOr => ("bitor_assign", lang.bitor_assign_trait()),
679 hir::BinOpKind::Shl => ("shl_assign", lang.shl_assign_trait()),
680 hir::BinOpKind::Shr => ("shr_assign", lang.shr_assign_trait()),
681 hir::BinOpKind::Lt | hir::BinOpKind::Le |
682 hir::BinOpKind::Ge | hir::BinOpKind::Gt |
683 hir::BinOpKind::Eq | hir::BinOpKind::Ne |
684 hir::BinOpKind::And | hir::BinOpKind::Or => {
686 "impossible assignment operation: {}=",
690 } else if let Op::Binary(op, IsAssign::No) = op {
692 hir::BinOpKind::Add => ("add", lang.add_trait()),
693 hir::BinOpKind::Sub => ("sub", lang.sub_trait()),
694 hir::BinOpKind::Mul => ("mul", lang.mul_trait()),
695 hir::BinOpKind::Div => ("div", lang.div_trait()),
696 hir::BinOpKind::Rem => ("rem", lang.rem_trait()),
697 hir::BinOpKind::BitXor => ("bitxor", lang.bitxor_trait()),
698 hir::BinOpKind::BitAnd => ("bitand", lang.bitand_trait()),
699 hir::BinOpKind::BitOr => ("bitor", lang.bitor_trait()),
700 hir::BinOpKind::Shl => ("shl", lang.shl_trait()),
701 hir::BinOpKind::Shr => ("shr", lang.shr_trait()),
702 hir::BinOpKind::Lt => ("lt", lang.partial_ord_trait()),
703 hir::BinOpKind::Le => ("le", lang.partial_ord_trait()),
704 hir::BinOpKind::Ge => ("ge", lang.partial_ord_trait()),
705 hir::BinOpKind::Gt => ("gt", lang.partial_ord_trait()),
706 hir::BinOpKind::Eq => ("eq", lang.eq_trait()),
707 hir::BinOpKind::Ne => ("ne", lang.eq_trait()),
708 hir::BinOpKind::And | hir::BinOpKind::Or => {
709 span_bug!(span, "&& and || are not overloadable")
712 } else if let Op::Unary(hir::UnNot, _) = op {
713 ("not", lang.not_trait())
714 } else if let Op::Unary(hir::UnNeg, _) = op {
715 ("neg", lang.neg_trait())
717 bug!("lookup_op_method: op not supported: {:?}", op)
720 debug!("lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
726 let method = trait_did.and_then(|trait_did| {
727 let opname = Ident::from_str(opname);
728 self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
733 let method = self.register_infer_ok_obligations(ok);
734 self.select_obligations_where_possible(false);
745 // Binary operator categories. These categories summarize the behavior
746 // with respect to the builtin operationrs supported.
748 /// &&, || -- cannot be overridden
751 /// <<, >> -- when shifting a single integer, rhs can be any
752 /// integer type. For simd, types must match.
755 /// +, -, etc -- takes equal types, produces same type as input,
756 /// applicable to ints/floats/simd
759 /// &, |, ^ -- takes equal types, produces same type as input,
760 /// applicable to ints/floats/simd/bool
763 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
764 /// which produce the input type
769 fn from(op: hir::BinOp) -> BinOpCategory {
771 hir::BinOpKind::Shl | hir::BinOpKind::Shr =>
772 BinOpCategory::Shift,
774 hir::BinOpKind::Add |
775 hir::BinOpKind::Sub |
776 hir::BinOpKind::Mul |
777 hir::BinOpKind::Div |
778 hir::BinOpKind::Rem =>
781 hir::BinOpKind::BitXor |
782 hir::BinOpKind::BitAnd |
783 hir::BinOpKind::BitOr =>
784 BinOpCategory::Bitwise,
791 hir::BinOpKind::Gt =>
792 BinOpCategory::Comparison,
794 hir::BinOpKind::And |
795 hir::BinOpKind::Or =>
796 BinOpCategory::Shortcircuit,
801 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
802 #[derive(Clone, Copy, Debug, PartialEq)]
808 #[derive(Clone, Copy, Debug)]
810 Binary(hir::BinOp, IsAssign),
811 Unary(hir::UnOp, Span),
814 /// Returns `true` if this is a built-in arithmetic operation (e.g., u32
815 /// + u32, i16x4 == i16x4) and false if these types would have to be
816 /// overloaded to be legal. There are two reasons that we distinguish
817 /// builtin operations from overloaded ones (vs trying to drive
818 /// everything uniformly through the trait system and intrinsics or
819 /// something like that):
821 /// 1. Builtin operations can trivially be evaluated in constants.
822 /// 2. For comparison operators applied to SIMD types the result is
823 /// not of type `bool`. For example, `i16x4 == i16x4` yields a
824 /// type like `i16x4`. This means that the overloaded trait
825 /// `PartialEq` is not applicable.
827 /// Reason #2 is the killer. I tried for a while to always use
828 /// overloaded logic and just check the types in constants/codegen after
829 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
830 fn is_builtin_binop(lhs: Ty<'_>, rhs: Ty<'_>, op: hir::BinOp) -> bool {
831 match BinOpCategory::from(op) {
832 BinOpCategory::Shortcircuit => {
836 BinOpCategory::Shift => {
837 lhs.references_error() || rhs.references_error() ||
838 lhs.is_integral() && rhs.is_integral()
841 BinOpCategory::Math => {
842 lhs.references_error() || rhs.references_error() ||
843 lhs.is_integral() && rhs.is_integral() ||
844 lhs.is_floating_point() && rhs.is_floating_point()
847 BinOpCategory::Bitwise => {
848 lhs.references_error() || rhs.references_error() ||
849 lhs.is_integral() && rhs.is_integral() ||
850 lhs.is_floating_point() && rhs.is_floating_point() ||
851 lhs.is_bool() && rhs.is_bool()
854 BinOpCategory::Comparison => {
855 lhs.references_error() || rhs.references_error() ||
856 lhs.is_scalar() && rhs.is_scalar()