Skip to main content

rustc_hir_typeck/
op.rs

1//! Code related to processing overloaded binary and unary operators.
2
3use rustc_ast::{self as ast, AssignOp, BinOp};
4use rustc_data_structures::packed::Pu128;
5use rustc_errors::codes::*;
6use rustc_errors::{Applicability, Diag, struct_span_code_err};
7use rustc_hir::def_id::DefId;
8use rustc_hir::{self as hir, AssignOpKind, BinOpKind, Expr, ExprKind};
9use rustc_infer::traits::ObligationCauseCode;
10use rustc_middle::bug;
11use rustc_middle::ty::adjustment::{
12    Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
13};
14use rustc_middle::ty::print::with_no_trimmed_paths;
15use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
16use rustc_session::errors::ExprParenthesesNeeded;
17use rustc_span::{Span, Spanned, Symbol, sym};
18use rustc_trait_selection::infer::InferCtxtExt;
19use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
20use tracing::debug;
21
22use super::FnCtxt;
23use super::method::MethodCallee;
24use crate::method::TreatNotYetDefinedOpaques;
25use crate::{Expectation, errors};
26
27impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28    /// Checks a `a <op>= b`
29    pub(crate) fn check_expr_assign_op(
30        &self,
31        expr: &'tcx Expr<'tcx>,
32        op: hir::AssignOp,
33        lhs: &'tcx Expr<'tcx>,
34        rhs: &'tcx Expr<'tcx>,
35        expected: Expectation<'tcx>,
36    ) -> Ty<'tcx> {
37        let (lhs_ty, rhs_ty, return_ty) =
38            self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
39
40        let category = BinOpCategory::from(op.node);
41        let ty = if !lhs_ty.is_ty_var()
42            && !rhs_ty.is_ty_var()
43            && is_builtin_binop(lhs_ty, rhs_ty, category)
44        {
45            self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
46            self.tcx.types.unit
47        } else {
48            return_ty
49        };
50
51        self.check_lhs_assignable(lhs, E0067, op.span, |err| {
52            if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
53                if self
54                    .lookup_op_method(
55                        (lhs, lhs_deref_ty),
56                        Some((rhs, rhs_ty)),
57                        lang_item_for_binop(self.tcx, Op::AssignOp(op)),
58                        op.span,
59                        expected,
60                    )
61                    .is_ok()
62                {
63                    // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have
64                    // emitted a better suggestion during error handling in check_overloaded_binop.
65                    if self
66                        .lookup_op_method(
67                            (lhs, lhs_ty),
68                            Some((rhs, rhs_ty)),
69                            lang_item_for_binop(self.tcx, Op::AssignOp(op)),
70                            op.span,
71                            expected,
72                        )
73                        .is_err()
74                    {
75                        err.downgrade_to_delayed_bug();
76                    } else {
77                        // Otherwise, it's valid to suggest dereferencing the LHS here.
78                        err.span_suggestion_verbose(
79                            lhs.span.shrink_to_lo(),
80                            "consider dereferencing the left-hand side of this operation",
81                            "*",
82                            Applicability::MaybeIncorrect,
83                        );
84                    }
85                }
86            }
87        });
88
89        ty
90    }
91
92    /// Checks a potentially overloaded binary operator.
93    pub(crate) fn check_expr_binop(
94        &self,
95        expr: &'tcx Expr<'tcx>,
96        op: hir::BinOp,
97        lhs_expr: &'tcx Expr<'tcx>,
98        rhs_expr: &'tcx Expr<'tcx>,
99        expected: Expectation<'tcx>,
100    ) -> Ty<'tcx> {
101        let tcx = self.tcx;
102
103        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:103",
                        "rustc_hir_typeck::op", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
                        ::tracing_core::__macro_support::Option::Some(103u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("check_binop(expr.hir_id={0}, expr={1:?}, op={2:?}, lhs_expr={3:?}, rhs_expr={4:?})",
                                                    expr.hir_id, expr, op, lhs_expr, rhs_expr) as &dyn Value))])
            });
    } else { ; }
};debug!(
104            "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
105            expr.hir_id, expr, op, lhs_expr, rhs_expr
106        );
107
108        match BinOpCategory::from(op.node) {
109            BinOpCategory::Shortcircuit => {
110                // && and || are a simple case.
111                self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
112                let lhs_diverges = self.diverges.get();
113                self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
114
115                // Depending on the LHS' value, the RHS can never execute.
116                self.diverges.set(lhs_diverges);
117
118                tcx.types.bool
119            }
120            _ => {
121                // Otherwise, we always treat operators as if they are
122                // overloaded. This is the way to be most flexible w/r/t
123                // types that get inferred.
124                let (lhs_ty, rhs_ty, return_ty) =
125                    self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
126
127                // Supply type inference hints if relevant. Probably these
128                // hints should be enforced during select as part of the
129                // `consider_unification_despite_ambiguity` routine, but this
130                // more convenient for now.
131                //
132                // The basic idea is to help type inference by taking
133                // advantage of things we know about how the impls for
134                // scalar types are arranged. This is important in a
135                // scenario like `1_u32 << 2`, because it lets us quickly
136                // deduce that the result type should be `u32`, even
137                // though we don't know yet what type 2 has and hence
138                // can't pin this down to a specific impl.
139                let category = BinOpCategory::from(op.node);
140                if !lhs_ty.is_ty_var()
141                    && !rhs_ty.is_ty_var()
142                    && is_builtin_binop(lhs_ty, rhs_ty, category)
143                {
144                    let builtin_return_ty = self.enforce_builtin_binop_types(
145                        lhs_expr.span,
146                        lhs_ty,
147                        rhs_expr.span,
148                        rhs_ty,
149                        category,
150                    );
151                    self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
152                    builtin_return_ty
153                } else {
154                    return_ty
155                }
156            }
157        }
158    }
159
160    fn enforce_builtin_binop_types(
161        &self,
162        lhs_span: Span,
163        lhs_ty: Ty<'tcx>,
164        rhs_span: Span,
165        rhs_ty: Ty<'tcx>,
166        category: BinOpCategory,
167    ) -> Ty<'tcx> {
168        if true {
    if !is_builtin_binop(lhs_ty, rhs_ty, category) {
        ::core::panicking::panic("assertion failed: is_builtin_binop(lhs_ty, rhs_ty, category)")
    };
};debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
169
170        // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
171        // (See https://github.com/rust-lang/rust/issues/57447.)
172        let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
173
174        let tcx = self.tcx;
175        match category {
176            BinOpCategory::Shortcircuit => {
177                self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
178                self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
179                tcx.types.bool
180            }
181
182            // result type is same as LHS always
183            BinOpCategory::Shift => lhs_ty,
184
185            BinOpCategory::Math | BinOpCategory::Bitwise => {
186                // both LHS and RHS and result will have the same type
187                self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
188                lhs_ty
189            }
190
191            BinOpCategory::Comparison => {
192                // both LHS and RHS and result will have the same type
193                self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
194                tcx.types.bool
195            }
196        }
197    }
198
199    fn check_overloaded_binop(
200        &self,
201        expr: &'tcx Expr<'tcx>,
202        lhs_expr: &'tcx Expr<'tcx>,
203        rhs_expr: &'tcx Expr<'tcx>,
204        op: Op,
205        expected: Expectation<'tcx>,
206    ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
207        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:207",
                        "rustc_hir_typeck::op", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
                        ::tracing_core::__macro_support::Option::Some(207u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("check_overloaded_binop(expr.hir_id={0}, op={1:?})",
                                                    expr.hir_id, op) as &dyn Value))])
            });
    } else { ; }
};debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
208
209        let lhs_ty = match op {
210            Op::BinOp(_) => {
211                // Find a suitable supertype of the LHS expression's type, by coercing to
212                // a type variable, to pass as the `Self` to the trait, avoiding invariant
213                // trait matching creating lifetime constraints that are too strict.
214                // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
215                // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
216                let lhs_ty = self.check_expr(lhs_expr);
217                let fresh_var = self.next_ty_var(lhs_expr.span);
218                self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
219            }
220            Op::AssignOp(_) => {
221                // rust-lang/rust#52126: We have to use strict
222                // equivalence on the LHS of an assign-op like `+=`;
223                // overwritten or mutably-borrowed places cannot be
224                // coerced to a supertype.
225                self.check_expr(lhs_expr)
226            }
227        };
228        let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
229
230        // N.B., as we have not yet type-checked the RHS, we don't have the
231        // type at hand. Make a variable to represent it. The whole reason
232        // for this indirection is so that, below, we can check the expr
233        // using this variable as the expected type, which sometimes lets
234        // us do better coercions than we would be able to do otherwise,
235        // particularly for things like `String + &String`.
236        let rhs_ty_var = self.next_ty_var(rhs_expr.span);
237        let result = self.lookup_op_method(
238            (lhs_expr, lhs_ty),
239            Some((rhs_expr, rhs_ty_var)),
240            lang_item_for_binop(self.tcx, op),
241            op.span(),
242            expected,
243        );
244
245        // see `NB` above
246        let rhs_ty = self.check_expr_coercible_to_type_or_error(
247            rhs_expr,
248            rhs_ty_var,
249            Some(lhs_expr),
250            |err, ty| {
251                self.err_ctxt().note_field_shadowed_by_private_candidate(
252                    err,
253                    rhs_expr.hir_id,
254                    self.param_env,
255                );
256                if let Op::BinOp(binop) = op
257                    && binop.node == hir::BinOpKind::Eq
258                {
259                    self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
260                }
261            },
262        );
263        let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
264
265        let return_ty = self.overloaded_binop_ret_ty(
266            expr, lhs_expr, rhs_expr, op, expected, lhs_ty, result, rhs_ty,
267        );
268
269        (lhs_ty, rhs_ty, return_ty)
270    }
271
272    fn overloaded_binop_ret_ty(
273        &self,
274        expr: &'tcx Expr<'tcx>,
275        lhs_expr: &'tcx Expr<'tcx>,
276        rhs_expr: &'tcx Expr<'tcx>,
277        op: Op,
278        expected: Expectation<'tcx>,
279        lhs_ty: Ty<'tcx>,
280        result: Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>>,
281        rhs_ty: Ty<'tcx>,
282    ) -> Ty<'tcx> {
283        match result {
284            Ok(method) => {
285                let by_ref_binop = !op.is_by_value();
286
287                if #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::AssignOp(_) => true,
    _ => false,
}matches!(op, Op::AssignOp(_)) || by_ref_binop {
288                    if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
289                        let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
290                        let autoref = Adjustment {
291                            kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
292                            target: method.sig.inputs()[0],
293                        };
294                        self.apply_adjustments(lhs_expr, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [autoref]))vec![autoref]);
295                    }
296
297                    if by_ref_binop {
298                        if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
299                            // Allow two-phase borrows for binops in initial deployment
300                            // since they desugar to methods
301                            let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
302                            let autoref = Adjustment {
303                                kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
304                                target: method.sig.inputs()[1],
305                            };
306                            // HACK(eddyb) Bypass checks due to reborrows being in
307                            // some cases applied on the RHS, on top of which we need
308                            // to autoref, which is not allowed by apply_adjustments.
309                            // self.apply_adjustments(rhs_expr, vec![autoref]);
310                            self.typeck_results
311                                .borrow_mut()
312                                .adjustments_mut()
313                                .entry(rhs_expr.hir_id)
314                                .or_default()
315                                .push(autoref);
316                        }
317                    }
318                }
319
320                self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
321                method.sig.output()
322            }
323            // error types are considered "builtin"
324            Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
325                Ty::new_misc_error(self.tcx)
326            }
327            Err(errors) => self.report_binop_fulfillment_errors(
328                expr, lhs_expr, rhs_expr, op, expected, lhs_ty, rhs_ty, errors,
329            ),
330        }
331    }
332
333    fn report_binop_fulfillment_errors(
334        &self,
335        expr: &'tcx Expr<'tcx>,
336        lhs_expr: &'tcx Expr<'tcx>,
337        rhs_expr: &'tcx Expr<'tcx>,
338        op: Op,
339        expected: Expectation<'tcx>,
340        lhs_ty: Ty<'tcx>,
341        rhs_ty: Ty<'tcx>,
342        errors: Vec<FulfillmentError<'tcx>>,
343    ) -> Ty<'tcx> {
344        let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
345
346        let mut path = None;
347        let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
348        let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
349
350        let (mut err, output_def_id) = match op {
351            // Try and detect when `+=` was incorrectly
352            // used instead of `==` in a let-chain
353            Op::AssignOp(assign_op) => {
354                if let Err(e) =
355                    errors::maybe_emit_plus_equals_diagnostic(&self, assign_op, lhs_expr)
356                {
357                    (e, None)
358                } else {
359                    let s = assign_op.node.as_str();
360                    let mut err = {
    self.dcx().struct_span_err(expr.span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("binary assignment operation `{0}` cannot be applied to type `{1}`",
                            s, lhs_ty_str))
                })).with_code(E0368)
}struct_span_code_err!(
361                        self.dcx(),
362                        expr.span,
363                        E0368,
364                        "binary assignment operation `{}` cannot be applied to type `{}`",
365                        s,
366                        lhs_ty_str,
367                    );
368                    err.span_label(
369                        lhs_expr.span,
370                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot use `{0}` on type `{1}`", s,
                lhs_ty_str))
    })format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
371                    );
372                    let err_ctxt = self.err_ctxt();
373                    err_ctxt.note_field_shadowed_by_private_candidate(
374                        &mut err,
375                        lhs_expr.hir_id,
376                        self.param_env,
377                    );
378                    err_ctxt.note_field_shadowed_by_private_candidate(
379                        &mut err,
380                        rhs_expr.hir_id,
381                        self.param_env,
382                    );
383                    self.note_unmet_impls_on_type(&mut err, &errors, false);
384                    (err, None)
385                }
386            }
387            Op::BinOp(bin_op) => {
388                use hir::BinOpKind;
389                let message = match bin_op.node {
390                    BinOpKind::Add => {
391                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot add `{0}` to `{1}`",
                rhs_ty_str, lhs_ty_str))
    })format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
392                    }
393                    BinOpKind::Sub => {
394                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot subtract `{0}` from `{1}`",
                rhs_ty_str, lhs_ty_str))
    })format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`")
395                    }
396                    BinOpKind::Mul => {
397                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot multiply `{0}` by `{1}`",
                lhs_ty_str, rhs_ty_str))
    })format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`")
398                    }
399                    BinOpKind::Div => {
400                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot divide `{0}` by `{1}`",
                lhs_ty_str, rhs_ty_str))
    })format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`")
401                    }
402                    BinOpKind::Rem => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot calculate the remainder of `{0}` divided by `{1}`",
                lhs_ty_str, rhs_ty_str))
    })format!(
403                        "cannot calculate the remainder of `{lhs_ty_str}` divided by `{rhs_ty_str}`"
404                    ),
405                    BinOpKind::BitAnd
406                    | BinOpKind::BitXor
407                    | BinOpKind::BitOr
408                    | BinOpKind::Shl
409                    | BinOpKind::Shr => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("no implementation for `{1} {0} {2}`",
                bin_op.node.as_str(), lhs_ty_str, rhs_ty_str))
    })format!(
410                        "no implementation for `{lhs_ty_str} {} {rhs_ty_str}`",
411                        bin_op.node.as_str()
412                    ),
413                    _ => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("binary operation `{0}` cannot be applied to type `{1}`",
                bin_op.node.as_str(), lhs_ty_str))
    })format!(
414                        "binary operation `{}` cannot be applied to type `{lhs_ty_str}`",
415                        bin_op.node.as_str()
416                    ),
417                };
418
419                let output_def_id = trait_def_id.and_then(|def_id| {
420                    self.tcx
421                        .associated_item_def_ids(def_id)
422                        .iter()
423                        .find(|&&item_def_id| {
424                            self.tcx.associated_item(item_def_id).name() == sym::Output
425                        })
426                        .cloned()
427                });
428                let mut err = {
    self.dcx().struct_span_err(bin_op.span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("{0}", message))
                })).with_code(E0369)
}struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
429                if !lhs_expr.span.eq(&rhs_expr.span) {
430                    err.span_label(lhs_expr.span, lhs_ty_str.clone());
431                    err.span_label(rhs_expr.span, rhs_ty_str);
432                }
433                let err_ctxt = self.err_ctxt();
434                err_ctxt.note_field_shadowed_by_private_candidate(
435                    &mut err,
436                    lhs_expr.hir_id,
437                    self.param_env,
438                );
439                err_ctxt.note_field_shadowed_by_private_candidate(
440                    &mut err,
441                    rhs_expr.hir_id,
442                    self.param_env,
443                );
444                let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
445                self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive);
446                (err, output_def_id)
447            }
448        };
449        *err.long_ty_path() = path;
450
451        // Try to suggest a semicolon if it's `A \n *B` where `B` is a place expr
452        let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
453
454        // We defer to the later error produced by `check_lhs_assignable`.
455        // We only downgrade this if it's the LHS, though, and if this is a
456        // valid assignment statement.
457        if maybe_missing_semi && self.is_lhs_of_assign_stmt(expr) {
458            err.downgrade_to_delayed_bug();
459        }
460
461        let is_compatible_after_call = |lhs_ty, rhs_ty| {
462            let op_ok = self
463                .lookup_op_method(
464                    (lhs_expr, lhs_ty),
465                    Some((rhs_expr, rhs_ty)),
466                    lang_item_for_binop(self.tcx, op),
467                    op.span(),
468                    expected,
469                )
470                .is_ok();
471
472            op_ok || self.can_eq(self.param_env, lhs_ty, rhs_ty)
473        };
474
475        // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
476        // `a += b` => `*a += b` if a is a mut ref.
477        self.suggest_deref_or_call_for_binop_error(
478            lhs_expr,
479            rhs_expr,
480            op,
481            expected,
482            lhs_ty,
483            rhs_ty,
484            &mut err,
485            is_compatible_after_call,
486        );
487
488        if let Some(missing_trait) =
489            trait_def_id.map(|def_id| { let _guard = NoTrimmedGuard::new(); self.tcx.def_path_str(def_id) }with_no_trimmed_paths!(self.tcx.def_path_str(def_id)))
490        {
491            if #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::BinOp(BinOp { node: BinOpKind::Add, .. }) |
        Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => true,
    _ => false,
}matches!(
492                op,
493                Op::BinOp(BinOp { node: BinOpKind::Add, .. })
494                    | Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. })
495            ) && self.check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
496            {
497                // This has nothing here because it means we did string
498                // concatenation (e.g., "Hello " + "World!"). This means
499                // we don't want the note in the else clause to be emitted
500            } else if lhs_ty.has_non_region_param() {
501                if !errors.is_empty() {
502                    for error in errors {
503                        if let Some(trait_pred) = error.obligation.predicate.as_trait_clause() {
504                            let output_associated_item = if let ObligationCauseCode::BinOp {
505                                output_ty: Some(output_ty),
506                                ..
507                            } = error.obligation.cause.code()
508                            {
509                                output_def_id
510                                    .zip(trait_def_id)
511                                    .filter(|(output_def_id, trait_def_id)| {
512                                        self.tcx.parent(*output_def_id) == *trait_def_id
513                                    })
514                                    .and_then(|_| output_ty.make_suggestable(self.tcx, false, None))
515                                    .map(|output_ty| ("Output", output_ty))
516                            } else {
517                                None
518                            };
519
520                            self.err_ctxt().suggest_restricting_param_bound(
521                                &mut err,
522                                trait_pred,
523                                output_associated_item,
524                                self.body_id,
525                            );
526                        }
527                    }
528                } else {
529                    // When we know that a missing bound is responsible, we don't show
530                    // this note as it is redundant.
531                    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the trait `{0}` is not implemented for `{1}`",
                missing_trait, lhs_ty_str))
    })format!(
532                        "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
533                    ));
534                }
535            }
536        }
537
538        // Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
539        // pointer + {integer} or pointer - pointer.
540        self.suggest_raw_ptr_binop_arithmetic(lhs_expr, rhs_expr, op, lhs_ty, rhs_ty, &mut err);
541
542        let lhs_name_str = match lhs_expr.kind {
543            ExprKind::Path(hir::QPath::Resolved(_, path)) => {
544                path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
545            }
546            _ => self
547                .tcx
548                .sess
549                .source_map()
550                .span_to_snippet(lhs_expr.span)
551                .unwrap_or_else(|_| "_".to_string()),
552        };
553
554        self.suggest_raw_ptr_assign_arithmetic(
555            lhs_expr,
556            rhs_expr,
557            op,
558            lhs_ty,
559            rhs_ty,
560            &lhs_name_str,
561            &mut err,
562        );
563
564        Ty::new_error(self.tcx, err.emit())
565    }
566
567    fn suggest_deref_or_call_for_binop_error(
568        &self,
569        lhs_expr: &'tcx Expr<'tcx>,
570        rhs_expr: &'tcx Expr<'tcx>,
571        op: Op,
572        expected: Expectation<'tcx>,
573        lhs_ty: Ty<'tcx>,
574        rhs_ty: Ty<'tcx>,
575        err: &mut Diag<'_>,
576        is_compatible_after_call: impl Fn(Ty<'tcx>, Ty<'tcx>) -> bool,
577    ) {
578        // Suppress suggestions when lhs and rhs are not in the same span as the error
579        if !op.span().can_be_used_for_suggestions() {
580            return;
581        }
582
583        if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
584            && #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::AssignOp(_) => true,
    _ => false,
}matches!(op, Op::AssignOp(_))
585        {
586            self.suggest_deref_binop(lhs_expr, rhs_expr, op, expected, rhs_ty, err, lhs_deref_ty);
587        } else if let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
588            && #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::BinOp(_) => true,
    _ => false,
}matches!(op, Op::BinOp(_))
589        {
590            if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
591                self.suggest_deref_binop(
592                    lhs_expr,
593                    rhs_expr,
594                    op,
595                    expected,
596                    rhs_ty,
597                    err,
598                    *lhs_deref_ty,
599                );
600            } else {
601                let lhs_inv_mutbl = mutbl.invert();
602                let lhs_inv_mutbl_ty = Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
603
604                self.suggest_different_borrow(
605                    lhs_expr,
606                    rhs_expr,
607                    op,
608                    expected,
609                    err,
610                    lhs_inv_mutbl_ty,
611                    Some(lhs_inv_mutbl),
612                    rhs_ty,
613                    None,
614                );
615
616                if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
617                    let rhs_inv_mutbl = mutbl.invert();
618                    let rhs_inv_mutbl_ty =
619                        Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
620
621                    self.suggest_different_borrow(
622                        lhs_expr,
623                        rhs_expr,
624                        op,
625                        expected,
626                        err,
627                        lhs_ty,
628                        None,
629                        rhs_inv_mutbl_ty,
630                        Some(rhs_inv_mutbl),
631                    );
632                    self.suggest_different_borrow(
633                        lhs_expr,
634                        rhs_expr,
635                        op,
636                        expected,
637                        err,
638                        lhs_inv_mutbl_ty,
639                        Some(lhs_inv_mutbl),
640                        rhs_inv_mutbl_ty,
641                        Some(rhs_inv_mutbl),
642                    );
643                }
644            }
645        } else {
646            let suggested = self.suggest_fn_call(err, lhs_expr, lhs_ty, |lhs_ty| {
647                is_compatible_after_call(lhs_ty, rhs_ty)
648            }) || self.suggest_fn_call(err, rhs_expr, rhs_ty, |rhs_ty| {
649                is_compatible_after_call(lhs_ty, rhs_ty)
650            });
651
652            if !suggested {
653                self.suggest_two_fn_call(
654                    err,
655                    rhs_expr,
656                    rhs_ty,
657                    lhs_expr,
658                    lhs_ty,
659                    is_compatible_after_call,
660                );
661            }
662        }
663    }
664
665    fn suggest_raw_ptr_binop_arithmetic(
666        &self,
667        lhs_expr: &'tcx Expr<'tcx>,
668        rhs_expr: &'tcx Expr<'tcx>,
669        op: Op,
670        lhs_ty: Ty<'tcx>,
671        rhs_ty: Ty<'tcx>,
672        err: &mut Diag<'_>,
673    ) {
674        if !op.span().can_be_used_for_suggestions() {
675            return;
676        }
677
678        match op {
679            Op::BinOp(BinOp { node: BinOpKind::Add, .. })
680                if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
681            {
682                err.multipart_suggestion(
683                    "consider using `wrapping_add` or `add` for pointer + {integer}",
684                    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_expr.span.between(rhs_expr.span), ".wrapping_add(".to_owned()),
                (rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
685                        (lhs_expr.span.between(rhs_expr.span), ".wrapping_add(".to_owned()),
686                        (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
687                    ],
688                    Applicability::MaybeIncorrect,
689                );
690            }
691            Op::BinOp(BinOp { node: BinOpKind::Sub, .. }) => {
692                if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
693                    err.multipart_suggestion(
694                        "consider using `wrapping_sub` or `sub` for pointer - {integer}",
695                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
                (rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
696                            (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
697                            (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
698                        ],
699                        Applicability::MaybeIncorrect,
700                    );
701                }
702                if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
703                    err.multipart_suggestion(
704                        "consider using `offset_from` for pointer - pointer if the \
705                     pointers point to the same allocation",
706                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
                (lhs_expr.span.between(rhs_expr.span),
                    ".offset_from(".to_owned()),
                (rhs_expr.span.shrink_to_hi(), ") }".to_owned())]))vec![
707                            (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
708                            (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()),
709                            (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
710                        ],
711                        Applicability::MaybeIncorrect,
712                    );
713                }
714            }
715            _ => {}
716        }
717    }
718
719    fn suggest_raw_ptr_assign_arithmetic(
720        &self,
721        lhs_expr: &'tcx Expr<'tcx>,
722        rhs_expr: &'tcx Expr<'tcx>,
723        op: Op,
724        lhs_ty: Ty<'tcx>,
725        rhs_ty: Ty<'tcx>,
726        lhs_name_str: &str,
727        err: &mut Diag<'_>,
728    ) {
729        if !op.span().can_be_used_for_suggestions()
730            || !#[allow(non_exhaustive_omitted_patterns)] match op {
    Op::AssignOp(_) => true,
    _ => false,
}matches!(op, Op::AssignOp(_))
731            || !lhs_ty.is_raw_ptr()
732            || !rhs_ty.is_integral()
733        {
734            return;
735        }
736
737        let (msg, method) = match op {
738            Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => {
739                ("consider using `add` or `wrapping_add` to do pointer arithmetic", "wrapping_add")
740            }
741            Op::AssignOp(AssignOp { node: AssignOpKind::SubAssign, .. }) => {
742                ("consider using `sub` or `wrapping_sub` to do pointer arithmetic", "wrapping_sub")
743            }
744            _ => return,
745        };
746
747        err.multipart_suggestion(
748            msg,
749            ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_expr.span.shrink_to_lo(),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("{0} = ", lhs_name_str))
                        })),
                (lhs_expr.span.between(rhs_expr.span),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!(".{0}(", method))
                        })), (rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
750                (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
751                (lhs_expr.span.between(rhs_expr.span), format!(".{method}(")),
752                (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
753            ],
754            Applicability::MaybeIncorrect,
755        );
756    }
757
758    fn suggest_different_borrow(
759        &self,
760        lhs_expr: &'tcx Expr<'tcx>,
761        rhs_expr: &'tcx Expr<'tcx>,
762        op: Op,
763        expected: Expectation<'tcx>,
764        err: &mut Diag<'_>,
765        lhs_adjusted_ty: Ty<'tcx>,
766        lhs_new_mutbl: Option<ty::Mutability>,
767        rhs_adjusted_ty: Ty<'tcx>,
768        rhs_new_mutbl: Option<ty::Mutability>,
769    ) {
770        if self
771            .lookup_op_method(
772                (lhs_expr, lhs_adjusted_ty),
773                Some((rhs_expr, rhs_adjusted_ty)),
774                lang_item_for_binop(self.tcx, op),
775                op.span(),
776                expected,
777            )
778            .is_ok()
779        {
780            let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
781            let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
782            let op = op.as_str();
783            err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("an implementation for `{0} {1} {2}` exists",
                lhs, op, rhs))
    })format!("an implementation for `{lhs} {op} {rhs}` exists"));
784
785            if lhs_new_mutbl.is_some_and(|lhs_mutbl| lhs_mutbl.is_not())
786                && rhs_new_mutbl.is_some_and(|rhs_mutbl| rhs_mutbl.is_not())
787            {
788                err.multipart_suggestion(
789                    "consider reborrowing both sides",
790                    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_expr.span.shrink_to_lo(), "&*".to_string()),
                (rhs_expr.span.shrink_to_lo(), "&*".to_string())]))vec![
791                        (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
792                        (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
793                    ],
794                    rustc_errors::Applicability::MachineApplicable,
795                );
796            } else {
797                let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| {
798                    // Can reborrow (&mut -> &)
799                    if new_mutbl.is_not() {
800                        err.span_suggestion_verbose(
801                            sp.shrink_to_lo(),
802                            "consider reborrowing this side",
803                            "&*",
804                            rustc_errors::Applicability::MachineApplicable,
805                        );
806                    // Works on &mut but have &
807                    } else {
808                        err.span_help(sp, "consider making this expression a mutable borrow");
809                    }
810                };
811
812                if let Some(lhs_new_mutbl) = lhs_new_mutbl {
813                    suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
814                }
815                if let Some(rhs_new_mutbl) = rhs_new_mutbl {
816                    suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
817                }
818            }
819        }
820    }
821
822    fn is_lhs_of_assign_stmt(&self, expr: &Expr<'_>) -> bool {
823        let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id) else { return false };
824        let ExprKind::Assign(lhs, _, _) = parent.kind else { return false };
825        let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id) else { return false };
826        #[allow(non_exhaustive_omitted_patterns)] match stmt.kind {
    hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) => true,
    _ => false,
}matches!(stmt.kind, hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_))
827            && lhs.hir_id == expr.hir_id
828    }
829
830    fn suggest_deref_binop(
831        &self,
832        lhs_expr: &'tcx Expr<'tcx>,
833        rhs_expr: &'tcx Expr<'tcx>,
834        op: Op,
835        expected: Expectation<'tcx>,
836        rhs_ty: Ty<'tcx>,
837        err: &mut Diag<'_>,
838        lhs_deref_ty: Ty<'tcx>,
839    ) {
840        if self
841            .lookup_op_method(
842                (lhs_expr, lhs_deref_ty),
843                Some((rhs_expr, rhs_ty)),
844                lang_item_for_binop(self.tcx, op),
845                op.span(),
846                expected,
847            )
848            .is_ok()
849        {
850            let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` can be used on `{1}` if you dereference the left-hand side",
                op.as_str(),
                self.tcx.short_string(lhs_deref_ty, err.long_ty_path())))
    })format!(
851                "`{}` can be used on `{}` if you dereference the left-hand side",
852                op.as_str(),
853                self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
854            );
855            err.span_suggestion_verbose(
856                lhs_expr.span.shrink_to_lo(),
857                msg,
858                "*",
859                rustc_errors::Applicability::MachineApplicable,
860            );
861        }
862    }
863
864    /// Provide actionable suggestions when trying to add two strings with incorrect types,
865    /// like `&str + &str`, `String + String` and `&str + &String`.
866    ///
867    /// If this function returns `true` it means a note was printed, so we don't need
868    /// to print the normal "implementation of `std::ops::Add` might be missing" note
869    fn check_str_addition(
870        &self,
871        lhs_expr: &'tcx Expr<'tcx>,
872        rhs_expr: &'tcx Expr<'tcx>,
873        lhs_ty: Ty<'tcx>,
874        rhs_ty: Ty<'tcx>,
875        err: &mut Diag<'_>,
876        op: Op,
877    ) -> bool {
878        let str_concat_note = "string concatenation requires an owned `String` on the left";
879        let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
880        let to_owned_msg = "create an owned `String` from a string reference";
881
882        let string_type = self.tcx.lang_items().string();
883        let is_std_string =
884            |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|def| Some(def.did()) == string_type);
885        let is_str_like = |ty: Ty<'tcx>| *ty.kind() == ty::Str || is_std_string(ty);
886
887        // Returns (suggestion_span, Some(replacement)) or (span, None) if lhs is a borrow to remove.
888        let lhs_owned_sugg = |lhs_expr: &Expr<'_>| {
889            if let ExprKind::AddrOf(_, _, inner) = lhs_expr.kind {
890                (lhs_expr.span.until(inner.span), None)
891            } else {
892                (lhs_expr.span.shrink_to_hi(), Some(".to_owned()".to_owned()))
893            }
894        };
895
896        let (&ty::Ref(_, l_ty, _), rhs_kind) = (lhs_ty.kind(), rhs_ty.kind()) else {
897            return false;
898        };
899        if !is_str_like(l_ty) {
900            return false;
901        }
902
903        match rhs_kind {
904            // &str or &String + &str, &String, or &&str
905            &ty::Ref(_, r_ty, _)
906                if is_str_like(r_ty)
907                    || #[allow(non_exhaustive_omitted_patterns)] match r_ty.kind() {
    ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
    _ => false,
}matches!(r_ty.kind(), ty::Ref(_, inner, _) if *inner.kind() == ty::Str) =>
908            {
909                // Do not supply this message if `&str += &str`
910                if let Op::BinOp(_) = op {
911                    err.span_label(
912                        op.span(),
913                        "`+` cannot be used to concatenate two `&str` strings",
914                    );
915                    err.note(str_concat_note);
916                    let (span, replacement) = lhs_owned_sugg(lhs_expr);
917                    let (msg, replacement) = match replacement {
918                        None => (rm_borrow_msg, "".to_owned()),
919                        Some(r) => (to_owned_msg, r),
920                    };
921                    err.span_suggestion_verbose(
922                        span,
923                        msg,
924                        replacement,
925                        Applicability::MachineApplicable,
926                    );
927                }
928                true
929            }
930            // &str or &String + String
931            ty::Adt(..) if is_std_string(rhs_ty) => {
932                err.span_label(
933                    op.span(),
934                    "`+` cannot be used to concatenate a `&str` with a `String`",
935                );
936                if #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::BinOp(_) => true,
    _ => false,
}matches!(op, Op::BinOp(_)) {
937                    let (lhs_span, lhs_replacement) = lhs_owned_sugg(lhs_expr);
938                    let (sugg_msg, lhs_replacement) = match lhs_replacement {
939                        None => (
940                            "remove the borrow on the left and add one on the right",
941                            "".to_owned(),
942                        ),
943                        Some(r) => (
944                            "create an owned `String` on the left and add a borrow on the right",
945                            r,
946                        ),
947                    };
948                    err.multipart_suggestion(
949                        sugg_msg,
950                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(lhs_span, lhs_replacement),
                (rhs_expr.span.shrink_to_lo(), "&".to_owned())]))vec![
951                            (lhs_span, lhs_replacement),
952                            (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
953                        ],
954                        Applicability::MachineApplicable,
955                    );
956                } else if #[allow(non_exhaustive_omitted_patterns)] match op {
    Op::AssignOp(_) => true,
    _ => false,
}matches!(op, Op::AssignOp(_)) {
957                    err.note(str_concat_note);
958                }
959                true
960            }
961            _ => false,
962        }
963    }
964
965    pub(crate) fn check_user_unop(
966        &self,
967        ex: &'tcx Expr<'tcx>,
968        operand_ty: Ty<'tcx>,
969        op: hir::UnOp,
970        expected: Expectation<'tcx>,
971    ) -> Ty<'tcx> {
972        if !op.is_by_value() {
    ::core::panicking::panic("assertion failed: op.is_by_value()")
};assert!(op.is_by_value());
973        match self.lookup_op_method(
974            (ex, operand_ty),
975            None,
976            lang_item_for_unop(self.tcx, op),
977            ex.span,
978            expected,
979        ) {
980            Ok(method) => {
981                self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
982                method.sig.output()
983            }
984            Err(errors) => {
985                let actual = self.resolve_vars_if_possible(operand_ty);
986                let guar = actual.error_reported().err().unwrap_or_else(|| {
987                    let mut file = None;
988                    let ty_str = self.tcx.short_string(actual, &mut file);
989                    let mut err = {
    self.dcx().struct_span_err(ex.span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("cannot apply unary operator `{0}` to type `{1}`",
                            op.as_str(), ty_str))
                })).with_code(E0600)
}struct_span_code_err!(
990                        self.dcx(),
991                        ex.span,
992                        E0600,
993                        "cannot apply unary operator `{}` to type `{ty_str}`",
994                        op.as_str(),
995                    );
996                    *err.long_ty_path() = file;
997                    err.span_label(
998                        ex.span,
999                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot apply unary operator `{0}`",
                op.as_str()))
    })format!("cannot apply unary operator `{}`", op.as_str()),
1000                    );
1001
1002                    if operand_ty.has_non_region_param() {
1003                        let predicates = errors
1004                            .iter()
1005                            .filter_map(|error| error.obligation.predicate.as_trait_clause());
1006                        for pred in predicates {
1007                            self.err_ctxt().suggest_restricting_param_bound(
1008                                &mut err,
1009                                pred,
1010                                None,
1011                                self.body_id,
1012                            );
1013                        }
1014                    }
1015
1016                    let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
1017                    if let Some(sp) =
1018                        self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
1019                    {
1020                        // If the previous expression was a block expression, suggest parentheses
1021                        // (turning this into a binary subtraction operation instead.)
1022                        // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
1023                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1024                    } else {
1025                        match actual.kind() {
1026                            ty::Uint(_) if op == hir::UnOp::Neg => {
1027                                err.note("unsigned values cannot be negated");
1028
1029                                if let ExprKind::Unary(
1030                                    _,
1031                                    Expr {
1032                                        kind:
1033                                            ExprKind::Lit(Spanned {
1034                                                node: ast::LitKind::Int(Pu128(1), _),
1035                                                ..
1036                                            }),
1037                                        ..
1038                                    },
1039                                ) = ex.kind
1040                                {
1041                                    let span = if let hir::Node::Expr(parent) =
1042                                        self.tcx.parent_hir_node(ex.hir_id)
1043                                        && let ExprKind::Cast(..) = parent.kind
1044                                    {
1045                                        // `-1 as usize` -> `usize::MAX`
1046                                        parent.span
1047                                    } else {
1048                                        ex.span
1049                                    };
1050                                    err.span_suggestion_verbose(
1051                                        span,
1052                                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("you may have meant the maximum value of `{0}`",
                actual))
    })format!(
1053                                            "you may have meant the maximum value of `{actual}`",
1054                                        ),
1055                                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::MAX", actual))
    })format!("{actual}::MAX"),
1056                                        Applicability::MaybeIncorrect,
1057                                    );
1058                                }
1059                            }
1060                            ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
1061                            ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
1062                            _ => {
1063                                self.note_unmet_impls_on_type(&mut err, &errors, true);
1064                            }
1065                        }
1066                    }
1067                    err.emit()
1068                });
1069                Ty::new_error(self.tcx, guar)
1070            }
1071        }
1072    }
1073
1074    fn lookup_op_method(
1075        &self,
1076        (lhs_expr, lhs_ty): (&'tcx Expr<'tcx>, Ty<'tcx>),
1077        opt_rhs: Option<(&'tcx Expr<'tcx>, Ty<'tcx>)>,
1078        (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
1079        span: Span,
1080        expected: Expectation<'tcx>,
1081    ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
1082        let Some(trait_did) = trait_did else {
1083            // Bail if the operator trait is not defined.
1084            return Err(::alloc::vec::Vec::new()vec![]);
1085        };
1086
1087        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:1087",
                        "rustc_hir_typeck::op", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
                        ::tracing_core::__macro_support::Option::Some(1087u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("lookup_op_method(lhs_ty={0:?}, opname={1:?}, trait_did={2:?})",
                                                    lhs_ty, opname, trait_did) as &dyn Value))])
            });
    } else { ; }
};debug!(
1088            "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
1089            lhs_ty, opname, trait_did
1090        );
1091
1092        let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
1093        let cause = self.cause(
1094            span,
1095            match opt_rhs_expr {
1096                Some(rhs) => ObligationCauseCode::BinOp {
1097                    lhs_hir_id: lhs_expr.hir_id,
1098                    rhs_hir_id: rhs.hir_id,
1099                    rhs_span: rhs.span,
1100                    rhs_is_lit: #[allow(non_exhaustive_omitted_patterns)] match rhs.kind {
    ExprKind::Lit(_) => true,
    _ => false,
}matches!(rhs.kind, ExprKind::Lit(_)),
1101                    output_ty: expected.only_has_type(self),
1102                },
1103                None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
1104            },
1105        );
1106
1107        // We don't consider any other candidates if this lookup fails
1108        // so we can freely treat opaque types as inference variables here
1109        // to allow more code to compile.
1110        let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
1111        let method = self.lookup_method_for_operator(
1112            cause.clone(),
1113            opname,
1114            trait_did,
1115            lhs_ty,
1116            opt_rhs_ty,
1117            treat_opaques,
1118        );
1119        match method {
1120            Some(ok) => {
1121                let method = self.register_infer_ok_obligations(ok);
1122                self.select_obligations_where_possible(|_| {});
1123                Ok(method)
1124            }
1125            None => {
1126                // This path may do some inference, so make sure we've really
1127                // doomed compilation so as to not accidentally stabilize new
1128                // inference or something here...
1129                self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1130                // Guide inference for the RHS expression if it's provided --
1131                // this will allow us to better error reporting, at the expense
1132                // of making some error messages a bit more specific.
1133                if let Some((rhs_expr, rhs_ty)) = opt_rhs
1134                    && rhs_ty.is_ty_var()
1135                {
1136                    self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
1137                }
1138
1139                // Construct an obligation `self_ty : Trait<input_tys>`
1140                let args =
1141                    ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
1142                        ty::GenericParamDefKind::Lifetime
1143                        | ty::GenericParamDefKind::Const { .. } => {
1144                            {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("did not expect operand trait to have lifetime/const args")));
}unreachable!("did not expect operand trait to have lifetime/const args")
1145                        }
1146                        ty::GenericParamDefKind::Type { .. } => {
1147                            if param.index == 0 {
1148                                lhs_ty.into()
1149                            } else {
1150                                opt_rhs_ty.expect("expected RHS for binop").into()
1151                            }
1152                        }
1153                    });
1154                let obligation = Obligation::new(
1155                    self.tcx,
1156                    cause,
1157                    self.param_env,
1158                    ty::TraitRef::new_from_args(self.tcx, trait_did, args),
1159                );
1160                let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
1161                ocx.register_obligation(obligation);
1162                Err(ocx.evaluate_obligations_error_on_ambiguity())
1163            }
1164        }
1165    }
1166}
1167
1168fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<DefId>) {
1169    let lang = tcx.lang_items();
1170    match op {
1171        Op::AssignOp(op) => match op.node {
1172            AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
1173            AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
1174            AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
1175            AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
1176            AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
1177            AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
1178            AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
1179            AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
1180            AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
1181            AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
1182        },
1183        Op::BinOp(op) => match op.node {
1184            BinOpKind::Add => (sym::add, lang.add_trait()),
1185            BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1186            BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1187            BinOpKind::Div => (sym::div, lang.div_trait()),
1188            BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1189            BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1190            BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1191            BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1192            BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1193            BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1194            BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1195            BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1196            BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1197            BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1198            BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1199            BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1200            BinOpKind::And | BinOpKind::Or => {
1201                ::rustc_middle::util::bug::bug_fmt(format_args!("&& and || are not overloadable"))bug!("&& and || are not overloadable")
1202            }
1203        },
1204    }
1205}
1206
1207fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
1208    let lang = tcx.lang_items();
1209    match op {
1210        hir::UnOp::Not => (sym::not, lang.not_trait()),
1211        hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
1212        hir::UnOp::Deref => ::rustc_middle::util::bug::bug_fmt(format_args!("Deref is not overloadable"))bug!("Deref is not overloadable"),
1213    }
1214}
1215
1216/// Check if `expr` contains a `let` or `&&`, indicating presence of a let-chain
1217pub(crate) fn contains_let_in_chain(expr: &Expr<'_>) -> bool {
1218    match &expr.kind {
1219        ExprKind::Let(..) => true,
1220        ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, left, right) => {
1221            contains_let_in_chain(left) || contains_let_in_chain(right)
1222        }
1223        _ => false,
1224    }
1225}
1226
1227// Binary operator categories. These categories summarize the behavior
1228// with respect to the builtin operations supported.
1229#[derive(#[automatically_derived]
impl ::core::clone::Clone for BinOpCategory {
    #[inline]
    fn clone(&self) -> BinOpCategory { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BinOpCategory { }Copy)]
1230enum BinOpCategory {
1231    /// &&, || -- cannot be overridden
1232    Shortcircuit,
1233
1234    /// <<, >> -- when shifting a single integer, rhs can be any
1235    /// integer type. For simd, types must match.
1236    Shift,
1237
1238    /// +, -, etc -- takes equal types, produces same type as input,
1239    /// applicable to ints/floats/simd
1240    Math,
1241
1242    /// &, |, ^ -- takes equal types, produces same type as input,
1243    /// applicable to ints/floats/simd/bool
1244    Bitwise,
1245
1246    /// ==, !=, etc -- takes equal types, produces bools, except for simd,
1247    /// which produce the input type
1248    Comparison,
1249}
1250
1251impl From<BinOpKind> for BinOpCategory {
1252    fn from(op: BinOpKind) -> BinOpCategory {
1253        use hir::BinOpKind::*;
1254        match op {
1255            Shl | Shr => BinOpCategory::Shift,
1256            Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
1257            BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
1258            Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
1259            And | Or => BinOpCategory::Shortcircuit,
1260        }
1261    }
1262}
1263
1264impl From<AssignOpKind> for BinOpCategory {
1265    fn from(op: AssignOpKind) -> BinOpCategory {
1266        use hir::AssignOpKind::*;
1267        match op {
1268            ShlAssign | ShrAssign => BinOpCategory::Shift,
1269            AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
1270            BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
1271        }
1272    }
1273}
1274
1275/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`).
1276#[derive(#[automatically_derived]
impl ::core::clone::Clone for Op {
    #[inline]
    fn clone(&self) -> Op {
        let _: ::core::clone::AssertParamIsClone<hir::BinOp>;
        let _: ::core::clone::AssertParamIsClone<hir::AssignOp>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Op { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Op {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Op::BinOp(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "BinOp",
                    &__self_0),
            Op::AssignOp(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "AssignOp", &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for Op {
    #[inline]
    fn eq(&self, other: &Op) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Op::BinOp(__self_0), Op::BinOp(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Op::AssignOp(__self_0), Op::AssignOp(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq)]
1277enum Op {
1278    BinOp(hir::BinOp),
1279    AssignOp(hir::AssignOp),
1280}
1281
1282impl Op {
1283    fn span(&self) -> Span {
1284        match self {
1285            Op::BinOp(op) => op.span,
1286            Op::AssignOp(op) => op.span,
1287        }
1288    }
1289
1290    fn as_str(&self) -> &'static str {
1291        match self {
1292            Op::BinOp(op) => op.node.as_str(),
1293            Op::AssignOp(op) => op.node.as_str(),
1294        }
1295    }
1296
1297    fn is_by_value(&self) -> bool {
1298        match self {
1299            Op::BinOp(op) => op.node.is_by_value(),
1300            Op::AssignOp(op) => op.node.is_by_value(),
1301        }
1302    }
1303}
1304
1305/// Dereferences a single level of immutable referencing.
1306fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1307    match ty.kind() {
1308        ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1309        _ => ty,
1310    }
1311}
1312
1313/// Returns `true` if this is a built-in arithmetic operation (e.g.,
1314/// u32 + u32, i16x4 == i16x4) and false if these types would have to be
1315/// overloaded to be legal. There are two reasons that we distinguish
1316/// builtin operations from overloaded ones (vs trying to drive
1317/// everything uniformly through the trait system and intrinsics or
1318/// something like that):
1319///
1320/// 1. Builtin operations can trivially be evaluated in constants.
1321/// 2. For comparison operators applied to SIMD types the result is
1322///    not of type `bool`. For example, `i16x4 == i16x4` yields a
1323///    type like `i16x4`. This means that the overloaded trait
1324///    `PartialEq` is not applicable.
1325///
1326/// Reason #2 is the killer. I tried for a while to always use
1327/// overloaded logic and just check the types in constants/codegen after
1328/// the fact, and it worked fine, except for SIMD types. -nmatsakis
1329fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1330    // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
1331    // (See https://github.com/rust-lang/rust/issues/57447.)
1332    let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1333
1334    match category {
1335        BinOpCategory::Shortcircuit => true,
1336        BinOpCategory::Shift => {
1337            lhs.references_error()
1338                || rhs.references_error()
1339                || lhs.is_integral() && rhs.is_integral()
1340        }
1341        BinOpCategory::Math => {
1342            lhs.references_error()
1343                || rhs.references_error()
1344                || lhs.is_integral() && rhs.is_integral()
1345                || lhs.is_floating_point() && rhs.is_floating_point()
1346        }
1347        BinOpCategory::Bitwise => {
1348            lhs.references_error()
1349                || rhs.references_error()
1350                || lhs.is_integral() && rhs.is_integral()
1351                || lhs.is_floating_point() && rhs.is_floating_point()
1352                || lhs.is_bool() && rhs.is_bool()
1353        }
1354        BinOpCategory::Comparison => {
1355            lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1356        }
1357    }
1358}