1 //! Code related to processing overloaded binary and unary operators.
3 use super::method::MethodCallee;
4 use super::{has_expected_num_generic_args, FnCtxt};
5 use crate::check::Expectation;
7 use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
9 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
10 use rustc_infer::traits::ObligationCauseCode;
11 use rustc_middle::ty::adjustment::{
12 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
14 use rustc_middle::ty::{
15 self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
17 use rustc_span::source_map::Spanned;
18 use rustc_span::symbol::{sym, Ident};
20 use rustc_trait_selection::infer::InferCtxtExt;
21 use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
22 use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
23 use rustc_type_ir::sty::TyKind::*;
25 use std::ops::ControlFlow;
27 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28 /// Checks a `a <op>= b`
29 pub fn check_binop_assign(
31 expr: &'tcx hir::Expr<'tcx>,
33 lhs: &'tcx hir::Expr<'tcx>,
34 rhs: &'tcx hir::Expr<'tcx>,
35 expected: Expectation<'tcx>,
37 let (lhs_ty, rhs_ty, return_ty) =
38 self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
41 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
42 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
48 self.check_lhs_assignable(lhs, "E0067", op.span, |err| {
49 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
55 Op::Binary(op, IsAssign::Yes),
60 // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have
61 // emitted a better suggestion during error handling in check_overloaded_binop.
67 Op::Binary(op, IsAssign::Yes),
72 err.downgrade_to_delayed_bug();
74 // Otherwise, it's valid to suggest dereferencing the LHS here.
75 err.span_suggestion_verbose(
76 lhs.span.shrink_to_lo(),
77 "consider dereferencing the left-hand side of this operation",
79 Applicability::MaybeIncorrect,
89 /// Checks a potentially overloaded binary operator.
92 expr: &'tcx hir::Expr<'tcx>,
94 lhs_expr: &'tcx hir::Expr<'tcx>,
95 rhs_expr: &'tcx hir::Expr<'tcx>,
96 expected: Expectation<'tcx>,
101 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
102 expr.hir_id, expr, op, lhs_expr, rhs_expr
105 match BinOpCategory::from(op) {
106 BinOpCategory::Shortcircuit => {
107 // && and || are a simple case.
108 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
109 let lhs_diverges = self.diverges.get();
110 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
112 // Depending on the LHS' value, the RHS can never execute.
113 self.diverges.set(lhs_diverges);
118 // Otherwise, we always treat operators as if they are
119 // overloaded. This is the way to be most flexible w/r/t
120 // types that get inferred.
121 let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
130 // Supply type inference hints if relevant. Probably these
131 // hints should be enforced during select as part of the
132 // `consider_unification_despite_ambiguity` routine, but this
133 // more convenient for now.
135 // The basic idea is to help type inference by taking
136 // advantage of things we know about how the impls for
137 // scalar types are arranged. This is important in a
138 // scenario like `1_u32 << 2`, because it lets us quickly
139 // deduce that the result type should be `u32`, even
140 // though we don't know yet what type 2 has and hence
141 // can't pin this down to a specific impl.
142 if !lhs_ty.is_ty_var()
143 && !rhs_ty.is_ty_var()
144 && is_builtin_binop(lhs_ty, rhs_ty, op)
146 let builtin_return_ty = self.enforce_builtin_binop_types(
153 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
161 fn enforce_builtin_binop_types(
169 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
171 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
172 // (See https://github.com/rust-lang/rust/issues/57447.)
173 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
176 match BinOpCategory::from(op) {
177 BinOpCategory::Shortcircuit => {
178 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
179 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
183 BinOpCategory::Shift => {
184 // result type is same as LHS always
188 BinOpCategory::Math | BinOpCategory::Bitwise => {
189 // both LHS and RHS and result will have the same type
190 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
194 BinOpCategory::Comparison => {
195 // both LHS and RHS and result will have the same type
196 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
202 fn check_overloaded_binop(
204 expr: &'tcx hir::Expr<'tcx>,
205 lhs_expr: &'tcx hir::Expr<'tcx>,
206 rhs_expr: &'tcx hir::Expr<'tcx>,
209 expected: Expectation<'tcx>,
210 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
212 "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
213 expr.hir_id, op, is_assign
216 let lhs_ty = match is_assign {
218 // Find a suitable supertype of the LHS expression's type, by coercing to
219 // a type variable, to pass as the `Self` to the trait, avoiding invariant
220 // trait matching creating lifetime constraints that are too strict.
221 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
222 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
223 let lhs_ty = self.check_expr(lhs_expr);
224 let fresh_var = self.next_ty_var(TypeVariableOrigin {
225 kind: TypeVariableOriginKind::MiscVariable,
228 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
231 // rust-lang/rust#52126: We have to use strict
232 // equivalence on the LHS of an assign-op like `+=`;
233 // overwritten or mutably-borrowed places cannot be
234 // coerced to a supertype.
235 self.check_expr(lhs_expr)
238 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
240 // N.B., as we have not yet type-checked the RHS, we don't have the
241 // type at hand. Make a variable to represent it. The whole reason
242 // for this indirection is so that, below, we can check the expr
243 // using this variable as the expected type, which sometimes lets
244 // us do better coercions than we would be able to do otherwise,
245 // particularly for things like `String + &String`.
246 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
247 kind: TypeVariableOriginKind::MiscVariable,
251 let result = self.lookup_op_method(
255 Op::Binary(op, is_assign),
260 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
261 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
263 let return_ty = match result {
265 let by_ref_binop = !op.node.is_by_value();
266 if is_assign == IsAssign::Yes || by_ref_binop {
267 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
268 let mutbl = match mutbl {
269 hir::Mutability::Not => AutoBorrowMutability::Not,
270 hir::Mutability::Mut => AutoBorrowMutability::Mut {
271 // Allow two-phase borrows for binops in initial deployment
272 // since they desugar to methods
273 allow_two_phase_borrow: AllowTwoPhase::Yes,
276 let autoref = Adjustment {
277 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
278 target: method.sig.inputs()[0],
280 self.apply_adjustments(lhs_expr, vec![autoref]);
284 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
285 let mutbl = match mutbl {
286 hir::Mutability::Not => AutoBorrowMutability::Not,
287 hir::Mutability::Mut => AutoBorrowMutability::Mut {
288 // Allow two-phase borrows for binops in initial deployment
289 // since they desugar to methods
290 allow_two_phase_borrow: AllowTwoPhase::Yes,
293 let autoref = Adjustment {
294 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
295 target: method.sig.inputs()[1],
297 // HACK(eddyb) Bypass checks due to reborrows being in
298 // some cases applied on the RHS, on top of which we need
299 // to autoref, which is not allowed by apply_adjustments.
300 // self.apply_adjustments(rhs_expr, vec![autoref]);
304 .entry(rhs_expr.hir_id)
309 self.write_method_call(expr.hir_id, method);
313 // error types are considered "builtin"
314 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
316 let source_map = self.tcx.sess.source_map();
317 let (mut err, missing_trait, use_output) = match is_assign {
319 let mut err = struct_span_err!(
323 "binary assignment operation `{}=` cannot be applied to type `{}`",
329 format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
331 let missing_trait = match op.node {
332 hir::BinOpKind::Add => Some("std::ops::AddAssign"),
333 hir::BinOpKind::Sub => Some("std::ops::SubAssign"),
334 hir::BinOpKind::Mul => Some("std::ops::MulAssign"),
335 hir::BinOpKind::Div => Some("std::ops::DivAssign"),
336 hir::BinOpKind::Rem => Some("std::ops::RemAssign"),
337 hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"),
338 hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"),
339 hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"),
340 hir::BinOpKind::Shl => Some("std::ops::ShlAssign"),
341 hir::BinOpKind::Shr => Some("std::ops::ShrAssign"),
344 self.note_unmet_impls_on_type(&mut err, errors);
345 (err, missing_trait, false)
348 let (message, missing_trait, use_output) = match op.node {
349 hir::BinOpKind::Add => (
350 format!("cannot add `{rhs_ty}` to `{lhs_ty}`"),
351 Some("std::ops::Add"),
354 hir::BinOpKind::Sub => (
355 format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"),
356 Some("std::ops::Sub"),
359 hir::BinOpKind::Mul => (
360 format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"),
361 Some("std::ops::Mul"),
364 hir::BinOpKind::Div => (
365 format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"),
366 Some("std::ops::Div"),
369 hir::BinOpKind::Rem => (
370 format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"),
371 Some("std::ops::Rem"),
374 hir::BinOpKind::BitAnd => (
375 format!("no implementation for `{lhs_ty} & {rhs_ty}`"),
376 Some("std::ops::BitAnd"),
379 hir::BinOpKind::BitXor => (
380 format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"),
381 Some("std::ops::BitXor"),
384 hir::BinOpKind::BitOr => (
385 format!("no implementation for `{lhs_ty} | {rhs_ty}`"),
386 Some("std::ops::BitOr"),
389 hir::BinOpKind::Shl => (
390 format!("no implementation for `{lhs_ty} << {rhs_ty}`"),
391 Some("std::ops::Shl"),
394 hir::BinOpKind::Shr => (
395 format!("no implementation for `{lhs_ty} >> {rhs_ty}`"),
396 Some("std::ops::Shr"),
399 hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
401 "binary operation `{}` cannot be applied to type `{}`",
405 Some("std::cmp::PartialEq"),
411 | hir::BinOpKind::Ge => (
413 "binary operation `{}` cannot be applied to type `{}`",
417 Some("std::cmp::PartialOrd"),
422 "binary operation `{}` cannot be applied to type `{}`",
430 let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
431 if !lhs_expr.span.eq(&rhs_expr.span) {
432 err.span_label(lhs_expr.span, lhs_ty.to_string());
433 err.span_label(rhs_expr.span, rhs_ty.to_string());
435 self.note_unmet_impls_on_type(&mut err, errors);
436 (err, missing_trait, use_output)
440 let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
446 Op::Binary(op, is_assign),
451 if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
453 "`{}{}` can be used on `{}`, you can dereference `{}`",
456 IsAssign::Yes => "=",
459 lhs_deref_ty.peel_refs(),
462 err.span_suggestion_verbose(
463 lhs_expr.span.shrink_to_lo(),
466 rustc_errors::Applicability::MachineApplicable,
472 let is_compatible = |lhs_ty, rhs_ty| {
473 self.lookup_op_method(
477 Op::Binary(op, is_assign),
483 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
484 // `a += b` => `*a += b` if a is a mut ref.
485 if !op.span.can_be_used_for_suggestions() {
486 // Suppress suggestions when lhs and rhs are not in the same span as the error
487 } else if is_assign == IsAssign::Yes
488 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
490 suggest_deref_binop(lhs_deref_ty);
491 } else if is_assign == IsAssign::No
492 && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
494 if self.type_is_copy_modulo_regions(
499 suggest_deref_binop(*lhs_deref_ty);
501 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
502 is_compatible(lhs_ty, rhs_ty)
503 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
504 is_compatible(lhs_ty, rhs_ty)
505 }) || self.suggest_two_fn_call(
511 |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
516 if let Some(missing_trait) = missing_trait {
517 let mut visitor = TypeParamVisitor(vec![]);
518 visitor.visit_ty(lhs_ty);
520 if op.node == hir::BinOpKind::Add
521 && self.check_str_addition(
522 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
525 // This has nothing here because it means we did string
526 // concatenation (e.g., "Hello " + "World!"). This means
527 // we don't want the note in the else clause to be emitted
528 } else if let [ty] = &visitor.0[..] {
529 // Look for a TraitPredicate in the Fulfillment errors,
530 // and use it to generate a suggestion.
532 // Note that lookup_op_method must be called again but
533 // with a specific rhs_ty instead of a placeholder so
534 // the resulting predicate generates a more specific
535 // suggestion for the user.
541 Op::Binary(op, is_assign),
545 if !errors.is_empty() {
546 for error in errors {
547 if let Some(trait_pred) =
548 error.obligation.predicate.to_opt_poly_trait_pred()
550 let proj_pred = match error.obligation.cause.code() {
551 ObligationCauseCode::BinOp {
552 output_pred: Some(output_pred),
555 output_pred.to_opt_poly_projection_pred()
560 self.suggest_restricting_param_bound(
568 } else if *ty != lhs_ty {
569 // When we know that a missing bound is responsible, we don't show
570 // this note as it is redundant.
572 "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
582 (lhs_ty, rhs_ty, return_ty)
585 /// Provide actionable suggestions when trying to add two strings with incorrect types,
586 /// like `&str + &str`, `String + String` and `&str + &String`.
588 /// If this function returns `true` it means a note was printed, so we don't need
589 /// to print the normal "implementation of `std::ops::Add` might be missing" note
590 fn check_str_addition(
592 lhs_expr: &'tcx hir::Expr<'tcx>,
593 rhs_expr: &'tcx hir::Expr<'tcx>,
596 err: &mut Diagnostic,
600 let str_concat_note = "string concatenation requires an owned `String` on the left";
601 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
602 let to_owned_msg = "create an owned `String` from a string reference";
604 let is_std_string = |ty: Ty<'tcx>| {
606 .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
609 match (lhs_ty.kind(), rhs_ty.kind()) {
610 (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
611 if (*l_ty.kind() == Str || is_std_string(l_ty))
612 && (*r_ty.kind() == Str
613 || is_std_string(r_ty)
615 r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str
618 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
619 err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
620 err.note(str_concat_note);
621 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
622 err.span_suggestion_verbose(
623 lhs_expr.span.until(lhs_inner_expr.span),
626 Applicability::MachineApplicable
629 err.span_suggestion_verbose(
630 lhs_expr.span.shrink_to_hi(),
633 Applicability::MachineApplicable
639 (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
640 if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
644 "`+` cannot be used to concatenate a `&str` with a `String`",
649 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
650 sugg_msg = "remove the borrow on the left and add one on the right";
651 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
653 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
654 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
656 let suggestions = vec![
658 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
660 err.multipart_suggestion_verbose(
663 Applicability::MachineApplicable,
667 err.note(str_concat_note);
676 pub fn check_user_unop(
678 ex: &'tcx hir::Expr<'tcx>,
679 operand_ty: Ty<'tcx>,
681 expected: Expectation<'tcx>,
683 assert!(op.is_by_value());
684 match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) {
686 self.write_method_call(ex.hir_id, method);
690 let actual = self.resolve_vars_if_possible(operand_ty);
691 if !actual.references_error() {
692 let mut err = struct_span_err!(
696 "cannot apply unary operator `{}` to type `{}`",
702 format!("cannot apply unary operator `{}`", op.as_str()),
705 let mut visitor = TypeParamVisitor(vec![]);
706 visitor.visit_ty(operand_ty);
707 if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() {
708 let predicates = errors
710 .filter_map(|error| {
711 error.obligation.predicate.to_opt_poly_trait_pred()
713 for pred in predicates {
714 self.suggest_restricting_param_bound(
723 let sp = self.tcx.sess.source_map().start_point(ex.span);
725 self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
727 // If the previous expression was a block expression, suggest parentheses
728 // (turning this into a binary subtraction operation instead.)
729 // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
730 self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp);
732 match actual.kind() {
733 Uint(_) if op == hir::UnOp::Neg => {
734 err.note("unsigned values cannot be negated");
736 if let hir::ExprKind::Unary(
740 hir::ExprKind::Lit(Spanned {
741 node: ast::LitKind::Int(1, _),
751 "you may have meant the maximum value of `{actual}`",
753 format!("{actual}::MAX"),
754 Applicability::MaybeIncorrect,
758 Str | Never | Char | Tuple(_) | Array(_, _) => {}
759 Ref(_, lty, _) if *lty.kind() == Str => {}
761 self.note_unmet_impls_on_type(&mut err, errors);
775 other_ty: Option<Ty<'tcx>>,
776 other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
778 expected: Expectation<'tcx>,
779 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
780 let lang = self.tcx.lang_items();
782 let span = match op {
783 Op::Binary(op, _) => op.span,
784 Op::Unary(_, span) => span,
786 let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
788 hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
789 hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
790 hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
791 hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
792 hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
793 hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
794 hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
795 hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
796 hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
797 hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
804 | hir::BinOpKind::And
805 | hir::BinOpKind::Or => {
806 span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
809 } else if let Op::Binary(op, IsAssign::No) = op {
811 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
812 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
813 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
814 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
815 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
816 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
817 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
818 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
819 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
820 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
821 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
822 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
823 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
824 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
825 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
826 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
827 hir::BinOpKind::And | hir::BinOpKind::Or => {
828 span_bug!(span, "&& and || are not overloadable")
831 } else if let Op::Unary(hir::UnOp::Not, _) = op {
832 (sym::not, lang.not_trait())
833 } else if let Op::Unary(hir::UnOp::Neg, _) = op {
834 (sym::neg, lang.neg_trait())
836 bug!("lookup_op_method: op not supported: {:?}", op)
840 "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
841 lhs_ty, op, opname, trait_did
844 // Catches cases like #83893, where a lang item is declared with the
845 // wrong number of generic arguments. Should have yielded an error
846 // elsewhere by now, but we have to catch it here so that we do not
847 // index `other_tys` out of bounds (if the lang item has too many
848 // generic arguments, `other_tys` is too short).
849 if !has_expected_num_generic_args(
853 // Binary ops have a generic right-hand side, unary ops don't
861 let opname = Ident::with_dummy_span(opname);
862 let method = trait_did.and_then(|trait_did| {
863 self.lookup_op_method_in_trait(
874 match (method, trait_did) {
876 let method = self.register_infer_ok_obligations(ok);
877 self.select_obligations_where_possible(false, |_| {});
880 (None, None) => Err(vec![]),
881 (None, Some(trait_did)) => {
882 let (obligation, _) = self.obligation_for_op_method(
890 let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
891 fulfill.register_predicate_obligation(self, obligation);
892 Err(fulfill.select_where_possible(&self.infcx))
898 // Binary operator categories. These categories summarize the behavior
899 // with respect to the builtin operations supported.
901 /// &&, || -- cannot be overridden
904 /// <<, >> -- when shifting a single integer, rhs can be any
905 /// integer type. For simd, types must match.
908 /// +, -, etc -- takes equal types, produces same type as input,
909 /// applicable to ints/floats/simd
912 /// &, |, ^ -- takes equal types, produces same type as input,
913 /// applicable to ints/floats/simd/bool
916 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
917 /// which produce the input type
922 fn from(op: hir::BinOp) -> BinOpCategory {
924 hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
927 | hir::BinOpKind::Sub
928 | hir::BinOpKind::Mul
929 | hir::BinOpKind::Div
930 | hir::BinOpKind::Rem => BinOpCategory::Math,
932 hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
933 BinOpCategory::Bitwise
941 | hir::BinOpKind::Gt => BinOpCategory::Comparison,
943 hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
948 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
949 #[derive(Clone, Copy, Debug, PartialEq)]
955 #[derive(Clone, Copy, Debug)]
957 Binary(hir::BinOp, IsAssign),
958 Unary(hir::UnOp, Span),
961 /// Dereferences a single level of immutable referencing.
962 fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
964 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
969 /// Returns `true` if this is a built-in arithmetic operation (e.g., u32
970 /// + u32, i16x4 == i16x4) and false if these types would have to be
971 /// overloaded to be legal. There are two reasons that we distinguish
972 /// builtin operations from overloaded ones (vs trying to drive
973 /// everything uniformly through the trait system and intrinsics or
974 /// something like that):
976 /// 1. Builtin operations can trivially be evaluated in constants.
977 /// 2. For comparison operators applied to SIMD types the result is
978 /// not of type `bool`. For example, `i16x4 == i16x4` yields a
979 /// type like `i16x4`. This means that the overloaded trait
980 /// `PartialEq` is not applicable.
982 /// Reason #2 is the killer. I tried for a while to always use
983 /// overloaded logic and just check the types in constants/codegen after
984 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
985 fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
986 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
987 // (See https://github.com/rust-lang/rust/issues/57447.)
988 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
990 match BinOpCategory::from(op) {
991 BinOpCategory::Shortcircuit => true,
993 BinOpCategory::Shift => {
994 lhs.references_error()
995 || rhs.references_error()
996 || lhs.is_integral() && rhs.is_integral()
999 BinOpCategory::Math => {
1000 lhs.references_error()
1001 || rhs.references_error()
1002 || lhs.is_integral() && rhs.is_integral()
1003 || lhs.is_floating_point() && rhs.is_floating_point()
1006 BinOpCategory::Bitwise => {
1007 lhs.references_error()
1008 || rhs.references_error()
1009 || lhs.is_integral() && rhs.is_integral()
1010 || lhs.is_floating_point() && rhs.is_floating_point()
1011 || lhs.is_bool() && rhs.is_bool()
1014 BinOpCategory::Comparison => {
1015 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1020 struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
1022 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
1023 fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1024 if let ty::Param(_) = ty.kind() {
1027 ty.super_visit_with(self)
1031 struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
1033 impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
1034 fn tcx(&self) -> TyCtxt<'tcx> {
1038 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1040 ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
1041 kind: TypeVariableOriginKind::MiscVariable,
1044 _ => ty.super_fold_with(self),