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