1use 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 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 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 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 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 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 self.diverges.set(lhs_diverges);
117
118 tcx.types.bool
119 }
120 _ => {
121 let (lhs_ty, rhs_ty, return_ty) =
125 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
126
127 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 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 BinOpCategory::Shift => lhs_ty,
184
185 BinOpCategory::Math | BinOpCategory::Bitwise => {
186 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
188 lhs_ty
189 }
190
191 BinOpCategory::Comparison => {
192 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 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 self.check_expr(lhs_expr)
226 }
227 };
228 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
229
230 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 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 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 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 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 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 let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
453
454 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 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 } 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 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 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 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 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 } 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 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 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 &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 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 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 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 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 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 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 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1130 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 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
1216pub(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#[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 Shortcircuit,
1233
1234 Shift,
1237
1238 Math,
1241
1242 Bitwise,
1245
1246 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#[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
1305fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1307 match ty.kind() {
1308 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1309 _ => ty,
1310 }
1311}
1312
1313fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1330 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}