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 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, category)
42 {
43 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
44 }
45
46 self.check_lhs_assignable(lhs, E0067, op.span, |err| {
47 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
48 if self
49 .lookup_op_method(
50 (lhs, lhs_deref_ty),
51 Some((rhs, rhs_ty)),
52 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
53 op.span,
54 expected,
55 )
56 .is_ok()
57 {
58 if self
61 .lookup_op_method(
62 (lhs, lhs_ty),
63 Some((rhs, rhs_ty)),
64 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
65 op.span,
66 expected,
67 )
68 .is_err()
69 {
70 err.downgrade_to_delayed_bug();
71 } else {
72 err.span_suggestion_verbose(
74 lhs.span.shrink_to_lo(),
75 "consider dereferencing the left-hand side of this operation",
76 "*",
77 Applicability::MaybeIncorrect,
78 );
79 }
80 }
81 }
82 });
83
84 self.tcx.types.unit
85 }
86
87 pub(crate) fn check_expr_binop(
89 &self,
90 expr: &'tcx Expr<'tcx>,
91 op: hir::BinOp,
92 lhs_expr: &'tcx Expr<'tcx>,
93 rhs_expr: &'tcx Expr<'tcx>,
94 expected: Expectation<'tcx>,
95 ) -> Ty<'tcx> {
96 let tcx = self.tcx;
97
98 {
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:98",
"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(98u32),
::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!(
99 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
100 expr.hir_id, expr, op, lhs_expr, rhs_expr
101 );
102
103 match BinOpCategory::from(op.node) {
104 BinOpCategory::Shortcircuit => {
105 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
107 let lhs_diverges = self.diverges.get();
108 self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
109
110 self.diverges.set(lhs_diverges);
112
113 tcx.types.bool
114 }
115 _ => {
116 let (lhs_ty, rhs_ty, return_ty) =
120 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
121
122 let category = BinOpCategory::from(op.node);
135 if !lhs_ty.is_ty_var()
136 && !rhs_ty.is_ty_var()
137 && is_builtin_binop(lhs_ty, rhs_ty, category)
138 {
139 let builtin_return_ty = self.enforce_builtin_binop_types(
140 lhs_expr.span,
141 lhs_ty,
142 rhs_expr.span,
143 rhs_ty,
144 category,
145 );
146 self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
147 builtin_return_ty
148 } else {
149 return_ty
150 }
151 }
152 }
153 }
154
155 fn enforce_builtin_binop_types(
156 &self,
157 lhs_span: Span,
158 lhs_ty: Ty<'tcx>,
159 rhs_span: Span,
160 rhs_ty: Ty<'tcx>,
161 category: BinOpCategory,
162 ) -> Ty<'tcx> {
163 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));
164
165 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
168
169 let tcx = self.tcx;
170 match category {
171 BinOpCategory::Shortcircuit => {
172 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
173 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
174 tcx.types.bool
175 }
176
177 BinOpCategory::Shift => lhs_ty,
179
180 BinOpCategory::Math | BinOpCategory::Bitwise => {
181 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
183 lhs_ty
184 }
185
186 BinOpCategory::Comparison => {
187 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
189 tcx.types.bool
190 }
191 }
192 }
193
194 fn check_overloaded_binop(
195 &self,
196 expr: &'tcx Expr<'tcx>,
197 lhs_expr: &'tcx Expr<'tcx>,
198 rhs_expr: &'tcx Expr<'tcx>,
199 op: Op,
200 expected: Expectation<'tcx>,
201 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
202 {
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:202",
"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(202u32),
::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);
203
204 let lhs_ty = match op {
205 Op::BinOp(_) => {
206 let lhs_ty = self.check_expr(lhs_expr);
212 let fresh_var = self.next_ty_var(lhs_expr.span);
213 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
214 }
215 Op::AssignOp(_) => {
216 self.check_expr(lhs_expr)
221 }
222 };
223 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
224
225 let rhs_ty_var = self.next_ty_var(rhs_expr.span);
232 let result = self.lookup_op_method(
233 (lhs_expr, lhs_ty),
234 Some((rhs_expr, rhs_ty_var)),
235 lang_item_for_binop(self.tcx, op),
236 op.span(),
237 expected,
238 );
239
240 let rhs_ty = self.check_expr_coercible_to_type_or_error(
242 rhs_expr,
243 rhs_ty_var,
244 Some(lhs_expr),
245 |err, ty| {
246 self.err_ctxt().note_field_shadowed_by_private_candidate(
247 err,
248 rhs_expr.hir_id,
249 self.param_env,
250 );
251 if let Op::BinOp(binop) = op
252 && binop.node == hir::BinOpKind::Eq
253 {
254 self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
255 }
256 },
257 );
258 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
259
260 let return_ty = self.overloaded_binop_ret_ty(
261 expr, lhs_expr, rhs_expr, op, expected, lhs_ty, result, rhs_ty,
262 );
263
264 (lhs_ty, rhs_ty, return_ty)
265 }
266
267 fn overloaded_binop_ret_ty(
268 &self,
269 expr: &'tcx Expr<'tcx>,
270 lhs_expr: &'tcx Expr<'tcx>,
271 rhs_expr: &'tcx Expr<'tcx>,
272 op: Op,
273 expected: Expectation<'tcx>,
274 lhs_ty: Ty<'tcx>,
275 result: Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>>,
276 rhs_ty: Ty<'tcx>,
277 ) -> Ty<'tcx> {
278 match result {
279 Ok(method) => {
280 let by_ref_binop = !op.is_by_value();
281
282 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_)) || by_ref_binop {
283 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
284 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
285 let autoref = Adjustment {
286 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
287 target: method.sig.inputs()[0],
288 };
289 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]);
290 }
291
292 if by_ref_binop {
293 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
294 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
297 let autoref = Adjustment {
298 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
299 target: method.sig.inputs()[1],
300 };
301 self.typeck_results
306 .borrow_mut()
307 .adjustments_mut()
308 .entry(rhs_expr.hir_id)
309 .or_default()
310 .push(autoref);
311 }
312 }
313 }
314
315 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
316 method.sig.output()
317 }
318 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
320 Ty::new_misc_error(self.tcx)
321 }
322 Err(errors) => self.report_binop_fulfillment_errors(
323 expr, lhs_expr, rhs_expr, op, expected, lhs_ty, rhs_ty, errors,
324 ),
325 }
326 }
327
328 fn report_binop_fulfillment_errors(
329 &self,
330 expr: &'tcx Expr<'tcx>,
331 lhs_expr: &'tcx Expr<'tcx>,
332 rhs_expr: &'tcx Expr<'tcx>,
333 op: Op,
334 expected: Expectation<'tcx>,
335 lhs_ty: Ty<'tcx>,
336 rhs_ty: Ty<'tcx>,
337 errors: Vec<FulfillmentError<'tcx>>,
338 ) -> Ty<'tcx> {
339 let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
340
341 let mut path = None;
342 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
343 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
344
345 let (mut err, output_def_id) = match op {
346 Op::AssignOp(assign_op) => {
349 if let Err(e) =
350 errors::maybe_emit_plus_equals_diagnostic(&self, assign_op, lhs_expr)
351 {
352 (e, None)
353 } else {
354 let s = assign_op.node.as_str();
355 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!(
356 self.dcx(),
357 expr.span,
358 E0368,
359 "binary assignment operation `{}` cannot be applied to type `{}`",
360 s,
361 lhs_ty_str,
362 );
363 err.span_label(
364 lhs_expr.span,
365 ::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),
366 );
367 let err_ctxt = self.err_ctxt();
368 err_ctxt.note_field_shadowed_by_private_candidate(
369 &mut err,
370 lhs_expr.hir_id,
371 self.param_env,
372 );
373 err_ctxt.note_field_shadowed_by_private_candidate(
374 &mut err,
375 rhs_expr.hir_id,
376 self.param_env,
377 );
378 self.note_unmet_impls_on_type(&mut err, &errors, false);
379 (err, None)
380 }
381 }
382 Op::BinOp(bin_op) => {
383 use hir::BinOpKind;
384 let message = match bin_op.node {
385 BinOpKind::Add => {
386 ::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}`")
387 }
388 BinOpKind::Sub => {
389 ::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}`")
390 }
391 BinOpKind::Mul => {
392 ::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}`")
393 }
394 BinOpKind::Div => {
395 ::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}`")
396 }
397 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!(
398 "cannot calculate the remainder of `{lhs_ty_str}` divided by `{rhs_ty_str}`"
399 ),
400 BinOpKind::BitAnd
401 | BinOpKind::BitXor
402 | BinOpKind::BitOr
403 | BinOpKind::Shl
404 | 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!(
405 "no implementation for `{lhs_ty_str} {} {rhs_ty_str}`",
406 bin_op.node.as_str()
407 ),
408 _ => ::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!(
409 "binary operation `{}` cannot be applied to type `{lhs_ty_str}`",
410 bin_op.node.as_str()
411 ),
412 };
413
414 let output_def_id = trait_def_id.and_then(|def_id| {
415 self.tcx
416 .associated_item_def_ids(def_id)
417 .iter()
418 .find(|&&item_def_id| {
419 self.tcx.associated_item(item_def_id).name() == sym::Output
420 })
421 .cloned()
422 });
423 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}");
424 if !lhs_expr.span.eq(&rhs_expr.span) {
425 err.span_label(lhs_expr.span, lhs_ty_str.clone());
426 err.span_label(rhs_expr.span, rhs_ty_str);
427 }
428 let err_ctxt = self.err_ctxt();
429 err_ctxt.note_field_shadowed_by_private_candidate(
430 &mut err,
431 lhs_expr.hir_id,
432 self.param_env,
433 );
434 err_ctxt.note_field_shadowed_by_private_candidate(
435 &mut err,
436 rhs_expr.hir_id,
437 self.param_env,
438 );
439 let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
440 self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive);
441 (err, output_def_id)
442 }
443 };
444 *err.long_ty_path() = path;
445
446 let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
448
449 if maybe_missing_semi && self.is_lhs_of_assign_stmt(expr) {
453 err.downgrade_to_delayed_bug();
454 }
455
456 let is_compatible_after_call = |lhs_ty, rhs_ty| {
457 let op_ok = self
458 .lookup_op_method(
459 (lhs_expr, lhs_ty),
460 Some((rhs_expr, rhs_ty)),
461 lang_item_for_binop(self.tcx, op),
462 op.span(),
463 expected,
464 )
465 .is_ok();
466
467 op_ok || self.can_eq(self.param_env, lhs_ty, rhs_ty)
468 };
469
470 self.suggest_deref_or_call_for_binop_error(
473 lhs_expr,
474 rhs_expr,
475 op,
476 expected,
477 lhs_ty,
478 rhs_ty,
479 &mut err,
480 is_compatible_after_call,
481 );
482
483 if let Some(missing_trait) =
484 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)))
485 {
486 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(BinOp { node: BinOpKind::Add, .. }) |
Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => true,
_ => false,
}matches!(
487 op,
488 Op::BinOp(BinOp { node: BinOpKind::Add, .. })
489 | Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. })
490 ) && self.check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
491 {
492 } else if lhs_ty.has_non_region_param() {
496 if !errors.is_empty() {
497 for error in errors {
498 if let Some(trait_pred) = error.obligation.predicate.as_trait_clause() {
499 let output_associated_item = if let ObligationCauseCode::BinOp {
500 output_ty: Some(output_ty),
501 ..
502 } = error.obligation.cause.code()
503 {
504 output_def_id
505 .zip(trait_def_id)
506 .filter(|(output_def_id, trait_def_id)| {
507 self.tcx.parent(*output_def_id) == *trait_def_id
508 })
509 .and_then(|_| output_ty.make_suggestable(self.tcx, false, None))
510 .map(|output_ty| ("Output", output_ty))
511 } else {
512 None
513 };
514
515 self.err_ctxt().suggest_restricting_param_bound(
516 &mut err,
517 trait_pred,
518 output_associated_item,
519 self.body_id,
520 );
521 }
522 }
523 } else {
524 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!(
527 "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
528 ));
529 }
530 }
531 }
532
533 self.suggest_raw_ptr_binop_arithmetic(lhs_expr, rhs_expr, op, lhs_ty, rhs_ty, &mut err);
536
537 let lhs_name_str = match lhs_expr.kind {
538 ExprKind::Path(hir::QPath::Resolved(_, path)) => {
539 path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
540 }
541 _ => self
542 .tcx
543 .sess
544 .source_map()
545 .span_to_snippet(lhs_expr.span)
546 .unwrap_or_else(|_| "_".to_string()),
547 };
548
549 self.suggest_raw_ptr_assign_arithmetic(
550 lhs_expr,
551 rhs_expr,
552 op,
553 lhs_ty,
554 rhs_ty,
555 &lhs_name_str,
556 &mut err,
557 );
558
559 Ty::new_error(self.tcx, err.emit())
560 }
561
562 fn suggest_deref_or_call_for_binop_error(
563 &self,
564 lhs_expr: &'tcx Expr<'tcx>,
565 rhs_expr: &'tcx Expr<'tcx>,
566 op: Op,
567 expected: Expectation<'tcx>,
568 lhs_ty: Ty<'tcx>,
569 rhs_ty: Ty<'tcx>,
570 err: &mut Diag<'_>,
571 is_compatible_after_call: impl Fn(Ty<'tcx>, Ty<'tcx>) -> bool,
572 ) {
573 if !op.span().can_be_used_for_suggestions() {
575 return;
576 }
577
578 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
579 && #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_))
580 {
581 self.suggest_deref_binop(lhs_expr, rhs_expr, op, expected, rhs_ty, err, lhs_deref_ty);
582 } else if let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
583 && #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(_) => true,
_ => false,
}matches!(op, Op::BinOp(_))
584 {
585 if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
586 self.suggest_deref_binop(
587 lhs_expr,
588 rhs_expr,
589 op,
590 expected,
591 rhs_ty,
592 err,
593 *lhs_deref_ty,
594 );
595 } else {
596 let lhs_inv_mutbl = mutbl.invert();
597 let lhs_inv_mutbl_ty = Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
598
599 self.suggest_different_borrow(
600 lhs_expr,
601 rhs_expr,
602 op,
603 expected,
604 err,
605 lhs_inv_mutbl_ty,
606 Some(lhs_inv_mutbl),
607 rhs_ty,
608 None,
609 );
610
611 if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
612 let rhs_inv_mutbl = mutbl.invert();
613 let rhs_inv_mutbl_ty =
614 Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
615
616 self.suggest_different_borrow(
617 lhs_expr,
618 rhs_expr,
619 op,
620 expected,
621 err,
622 lhs_ty,
623 None,
624 rhs_inv_mutbl_ty,
625 Some(rhs_inv_mutbl),
626 );
627 self.suggest_different_borrow(
628 lhs_expr,
629 rhs_expr,
630 op,
631 expected,
632 err,
633 lhs_inv_mutbl_ty,
634 Some(lhs_inv_mutbl),
635 rhs_inv_mutbl_ty,
636 Some(rhs_inv_mutbl),
637 );
638 }
639 }
640 } else {
641 let suggested = self.suggest_fn_call(err, lhs_expr, lhs_ty, |lhs_ty| {
642 is_compatible_after_call(lhs_ty, rhs_ty)
643 }) || self.suggest_fn_call(err, rhs_expr, rhs_ty, |rhs_ty| {
644 is_compatible_after_call(lhs_ty, rhs_ty)
645 });
646
647 if !suggested {
648 self.suggest_two_fn_call(
649 err,
650 rhs_expr,
651 rhs_ty,
652 lhs_expr,
653 lhs_ty,
654 is_compatible_after_call,
655 );
656 }
657 }
658 }
659
660 fn suggest_raw_ptr_binop_arithmetic(
661 &self,
662 lhs_expr: &'tcx Expr<'tcx>,
663 rhs_expr: &'tcx Expr<'tcx>,
664 op: Op,
665 lhs_ty: Ty<'tcx>,
666 rhs_ty: Ty<'tcx>,
667 err: &mut Diag<'_>,
668 ) {
669 if !op.span().can_be_used_for_suggestions() {
670 return;
671 }
672
673 match op {
674 Op::BinOp(BinOp { node: BinOpKind::Add, .. })
675 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
676 {
677 err.multipart_suggestion(
678 "consider using `wrapping_add` or `add` for pointer + {integer}",
679 ::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![
680 (lhs_expr.span.between(rhs_expr.span), ".wrapping_add(".to_owned()),
681 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
682 ],
683 Applicability::MaybeIncorrect,
684 );
685 }
686 Op::BinOp(BinOp { node: BinOpKind::Sub, .. }) => {
687 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
688 err.multipart_suggestion(
689 "consider using `wrapping_sub` or `sub` for pointer - {integer}",
690 ::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![
691 (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
692 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
693 ],
694 Applicability::MaybeIncorrect,
695 );
696 }
697 if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
698 err.multipart_suggestion(
699 "consider using `offset_from` for pointer - pointer if the \
700 pointers point to the same allocation",
701 ::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![
702 (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
703 (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()),
704 (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
705 ],
706 Applicability::MaybeIncorrect,
707 );
708 }
709 }
710 _ => {}
711 }
712 }
713
714 fn suggest_raw_ptr_assign_arithmetic(
715 &self,
716 lhs_expr: &'tcx Expr<'tcx>,
717 rhs_expr: &'tcx Expr<'tcx>,
718 op: Op,
719 lhs_ty: Ty<'tcx>,
720 rhs_ty: Ty<'tcx>,
721 lhs_name_str: &str,
722 err: &mut Diag<'_>,
723 ) {
724 if !op.span().can_be_used_for_suggestions()
725 || !#[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_))
726 || !lhs_ty.is_raw_ptr()
727 || !rhs_ty.is_integral()
728 {
729 return;
730 }
731
732 let (msg, method) = match op {
733 Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => {
734 ("consider using `add` or `wrapping_add` to do pointer arithmetic", "wrapping_add")
735 }
736 Op::AssignOp(AssignOp { node: AssignOpKind::SubAssign, .. }) => {
737 ("consider using `sub` or `wrapping_sub` to do pointer arithmetic", "wrapping_sub")
738 }
739 _ => return,
740 };
741
742 err.multipart_suggestion(
743 msg,
744 ::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![
745 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
746 (lhs_expr.span.between(rhs_expr.span), format!(".{method}(")),
747 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
748 ],
749 Applicability::MaybeIncorrect,
750 );
751 }
752
753 fn suggest_different_borrow(
754 &self,
755 lhs_expr: &'tcx Expr<'tcx>,
756 rhs_expr: &'tcx Expr<'tcx>,
757 op: Op,
758 expected: Expectation<'tcx>,
759 err: &mut Diag<'_>,
760 lhs_adjusted_ty: Ty<'tcx>,
761 lhs_new_mutbl: Option<ty::Mutability>,
762 rhs_adjusted_ty: Ty<'tcx>,
763 rhs_new_mutbl: Option<ty::Mutability>,
764 ) {
765 if self
766 .lookup_op_method(
767 (lhs_expr, lhs_adjusted_ty),
768 Some((rhs_expr, rhs_adjusted_ty)),
769 lang_item_for_binop(self.tcx, op),
770 op.span(),
771 expected,
772 )
773 .is_ok()
774 {
775 let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
776 let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
777 let op = op.as_str();
778 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"));
779
780 if lhs_new_mutbl.is_some_and(|lhs_mutbl| lhs_mutbl.is_not())
781 && rhs_new_mutbl.is_some_and(|rhs_mutbl| rhs_mutbl.is_not())
782 {
783 err.multipart_suggestion(
784 "consider reborrowing both sides",
785 ::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![
786 (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
787 (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
788 ],
789 rustc_errors::Applicability::MachineApplicable,
790 );
791 } else {
792 let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| {
793 if new_mutbl.is_not() {
795 err.span_suggestion_verbose(
796 sp.shrink_to_lo(),
797 "consider reborrowing this side",
798 "&*",
799 rustc_errors::Applicability::MachineApplicable,
800 );
801 } else {
803 err.span_help(sp, "consider making this expression a mutable borrow");
804 }
805 };
806
807 if let Some(lhs_new_mutbl) = lhs_new_mutbl {
808 suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
809 }
810 if let Some(rhs_new_mutbl) = rhs_new_mutbl {
811 suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
812 }
813 }
814 }
815 }
816
817 fn is_lhs_of_assign_stmt(&self, expr: &Expr<'_>) -> bool {
818 let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id) else { return false };
819 let ExprKind::Assign(lhs, _, _) = parent.kind else { return false };
820 let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id) else { return false };
821 #[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(_))
822 && lhs.hir_id == expr.hir_id
823 }
824
825 fn suggest_deref_binop(
826 &self,
827 lhs_expr: &'tcx Expr<'tcx>,
828 rhs_expr: &'tcx Expr<'tcx>,
829 op: Op,
830 expected: Expectation<'tcx>,
831 rhs_ty: Ty<'tcx>,
832 err: &mut Diag<'_>,
833 lhs_deref_ty: Ty<'tcx>,
834 ) {
835 if self
836 .lookup_op_method(
837 (lhs_expr, lhs_deref_ty),
838 Some((rhs_expr, rhs_ty)),
839 lang_item_for_binop(self.tcx, op),
840 op.span(),
841 expected,
842 )
843 .is_ok()
844 {
845 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!(
846 "`{}` can be used on `{}` if you dereference the left-hand side",
847 op.as_str(),
848 self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
849 );
850 err.span_suggestion_verbose(
851 lhs_expr.span.shrink_to_lo(),
852 msg,
853 "*",
854 rustc_errors::Applicability::MachineApplicable,
855 );
856 }
857 }
858
859 fn check_str_addition(
865 &self,
866 lhs_expr: &'tcx Expr<'tcx>,
867 rhs_expr: &'tcx Expr<'tcx>,
868 lhs_ty: Ty<'tcx>,
869 rhs_ty: Ty<'tcx>,
870 err: &mut Diag<'_>,
871 op: Op,
872 ) -> bool {
873 let str_concat_note = "string concatenation requires an owned `String` on the left";
874 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
875 let to_owned_msg = "create an owned `String` from a string reference";
876
877 let string_type = self.tcx.lang_items().string();
878 let is_std_string =
879 |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|def| Some(def.did()) == string_type);
880 let is_str_like = |ty: Ty<'tcx>| *ty.kind() == ty::Str || is_std_string(ty);
881
882 let lhs_owned_sugg = |lhs_expr: &Expr<'_>| {
884 if let ExprKind::AddrOf(_, _, inner) = lhs_expr.kind {
885 (lhs_expr.span.until(inner.span), None)
886 } else {
887 (lhs_expr.span.shrink_to_hi(), Some(".to_owned()".to_owned()))
888 }
889 };
890
891 let (&ty::Ref(_, l_ty, _), rhs_kind) = (lhs_ty.kind(), rhs_ty.kind()) else {
892 return false;
893 };
894 if !is_str_like(l_ty) {
895 return false;
896 }
897
898 match rhs_kind {
899 &ty::Ref(_, r_ty, _)
901 if is_str_like(r_ty)
902 || #[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) =>
903 {
904 if let Op::BinOp(_) = op {
906 err.span_label(
907 op.span(),
908 "`+` cannot be used to concatenate two `&str` strings",
909 );
910 err.note(str_concat_note);
911 let (span, replacement) = lhs_owned_sugg(lhs_expr);
912 let (msg, replacement) = match replacement {
913 None => (rm_borrow_msg, "".to_owned()),
914 Some(r) => (to_owned_msg, r),
915 };
916 err.span_suggestion_verbose(
917 span,
918 msg,
919 replacement,
920 Applicability::MachineApplicable,
921 );
922 }
923 true
924 }
925 ty::Adt(..) if is_std_string(rhs_ty) => {
927 err.span_label(
928 op.span(),
929 "`+` cannot be used to concatenate a `&str` with a `String`",
930 );
931 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(_) => true,
_ => false,
}matches!(op, Op::BinOp(_)) {
932 let (lhs_span, lhs_replacement) = lhs_owned_sugg(lhs_expr);
933 let (sugg_msg, lhs_replacement) = match lhs_replacement {
934 None => (
935 "remove the borrow on the left and add one on the right",
936 "".to_owned(),
937 ),
938 Some(r) => (
939 "create an owned `String` on the left and add a borrow on the right",
940 r,
941 ),
942 };
943 err.multipart_suggestion(
944 sugg_msg,
945 ::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![
946 (lhs_span, lhs_replacement),
947 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
948 ],
949 Applicability::MachineApplicable,
950 );
951 } else if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_)) {
952 err.note(str_concat_note);
953 }
954 true
955 }
956 _ => false,
957 }
958 }
959
960 pub(crate) fn check_user_unop(
961 &self,
962 ex: &'tcx Expr<'tcx>,
963 operand_ty: Ty<'tcx>,
964 op: hir::UnOp,
965 expected: Expectation<'tcx>,
966 ) -> Ty<'tcx> {
967 if !op.is_by_value() {
::core::panicking::panic("assertion failed: op.is_by_value()")
};assert!(op.is_by_value());
968 match self.lookup_op_method(
969 (ex, operand_ty),
970 None,
971 lang_item_for_unop(self.tcx, op),
972 ex.span,
973 expected,
974 ) {
975 Ok(method) => {
976 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
977 method.sig.output()
978 }
979 Err(errors) => {
980 let actual = self.resolve_vars_if_possible(operand_ty);
981 let guar = actual.error_reported().err().unwrap_or_else(|| {
982 let mut file = None;
983 let ty_str = self.tcx.short_string(actual, &mut file);
984 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!(
985 self.dcx(),
986 ex.span,
987 E0600,
988 "cannot apply unary operator `{}` to type `{ty_str}`",
989 op.as_str(),
990 );
991 *err.long_ty_path() = file;
992 err.span_label(
993 ex.span,
994 ::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()),
995 );
996
997 if operand_ty.has_non_region_param() {
998 let predicates = errors
999 .iter()
1000 .filter_map(|error| error.obligation.predicate.as_trait_clause());
1001 for pred in predicates {
1002 self.err_ctxt().suggest_restricting_param_bound(
1003 &mut err,
1004 pred,
1005 None,
1006 self.body_id,
1007 );
1008 }
1009 }
1010
1011 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
1012 if let Some(sp) =
1013 self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
1014 {
1015 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1019 } else {
1020 match actual.kind() {
1021 ty::Uint(_) if op == hir::UnOp::Neg => {
1022 err.note("unsigned values cannot be negated");
1023
1024 if let ExprKind::Unary(
1025 _,
1026 Expr {
1027 kind:
1028 ExprKind::Lit(Spanned {
1029 node: ast::LitKind::Int(Pu128(1), _),
1030 ..
1031 }),
1032 ..
1033 },
1034 ) = ex.kind
1035 {
1036 let span = if let hir::Node::Expr(parent) =
1037 self.tcx.parent_hir_node(ex.hir_id)
1038 && let ExprKind::Cast(..) = parent.kind
1039 {
1040 parent.span
1042 } else {
1043 ex.span
1044 };
1045 err.span_suggestion_verbose(
1046 span,
1047 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you may have meant the maximum value of `{0}`",
actual))
})format!(
1048 "you may have meant the maximum value of `{actual}`",
1049 ),
1050 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::MAX", actual))
})format!("{actual}::MAX"),
1051 Applicability::MaybeIncorrect,
1052 );
1053 }
1054 }
1055 ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
1056 ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
1057 _ => {
1058 self.note_unmet_impls_on_type(&mut err, &errors, true);
1059 }
1060 }
1061 }
1062 err.emit()
1063 });
1064 Ty::new_error(self.tcx, guar)
1065 }
1066 }
1067 }
1068
1069 fn lookup_op_method(
1070 &self,
1071 (lhs_expr, lhs_ty): (&'tcx Expr<'tcx>, Ty<'tcx>),
1072 opt_rhs: Option<(&'tcx Expr<'tcx>, Ty<'tcx>)>,
1073 (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
1074 span: Span,
1075 expected: Expectation<'tcx>,
1076 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
1077 let Some(trait_did) = trait_did else {
1078 return Err(::alloc::vec::Vec::new()vec![]);
1080 };
1081
1082 {
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:1082",
"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(1082u32),
::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!(
1083 "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
1084 lhs_ty, opname, trait_did
1085 );
1086
1087 let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
1088 let cause = self.cause(
1089 span,
1090 match opt_rhs_expr {
1091 Some(rhs) => ObligationCauseCode::BinOp {
1092 lhs_hir_id: lhs_expr.hir_id,
1093 rhs_hir_id: rhs.hir_id,
1094 rhs_span: rhs.span,
1095 rhs_is_lit: #[allow(non_exhaustive_omitted_patterns)] match rhs.kind {
ExprKind::Lit(_) => true,
_ => false,
}matches!(rhs.kind, ExprKind::Lit(_)),
1096 output_ty: expected.only_has_type(self),
1097 },
1098 None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
1099 },
1100 );
1101
1102 let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
1106 let method = self.lookup_method_for_operator(
1107 cause.clone(),
1108 opname,
1109 trait_did,
1110 lhs_ty,
1111 opt_rhs_ty,
1112 treat_opaques,
1113 );
1114 match method {
1115 Some(ok) => {
1116 let method = self.register_infer_ok_obligations(ok);
1117 self.select_obligations_where_possible(|_| {});
1118 Ok(method)
1119 }
1120 None => {
1121 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1125 if let Some((rhs_expr, rhs_ty)) = opt_rhs
1129 && rhs_ty.is_ty_var()
1130 {
1131 self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
1132 }
1133
1134 let args =
1136 ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
1137 ty::GenericParamDefKind::Lifetime
1138 | ty::GenericParamDefKind::Const { .. } => {
1139 {
::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")
1140 }
1141 ty::GenericParamDefKind::Type { .. } => {
1142 if param.index == 0 {
1143 lhs_ty.into()
1144 } else {
1145 opt_rhs_ty.expect("expected RHS for binop").into()
1146 }
1147 }
1148 });
1149 let obligation = Obligation::new(
1150 self.tcx,
1151 cause,
1152 self.param_env,
1153 ty::TraitRef::new_from_args(self.tcx, trait_did, args),
1154 );
1155 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
1156 ocx.register_obligation(obligation);
1157 Err(ocx.evaluate_obligations_error_on_ambiguity())
1158 }
1159 }
1160 }
1161}
1162
1163fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<DefId>) {
1164 let lang = tcx.lang_items();
1165 match op {
1166 Op::AssignOp(op) => match op.node {
1167 AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
1168 AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
1169 AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
1170 AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
1171 AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
1172 AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
1173 AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
1174 AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
1175 AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
1176 AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
1177 },
1178 Op::BinOp(op) => match op.node {
1179 BinOpKind::Add => (sym::add, lang.add_trait()),
1180 BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1181 BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1182 BinOpKind::Div => (sym::div, lang.div_trait()),
1183 BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1184 BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1185 BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1186 BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1187 BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1188 BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1189 BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1190 BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1191 BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1192 BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1193 BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1194 BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1195 BinOpKind::And | BinOpKind::Or => {
1196 ::rustc_middle::util::bug::bug_fmt(format_args!("&& and || are not overloadable"))bug!("&& and || are not overloadable")
1197 }
1198 },
1199 }
1200}
1201
1202fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
1203 let lang = tcx.lang_items();
1204 match op {
1205 hir::UnOp::Not => (sym::not, lang.not_trait()),
1206 hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
1207 hir::UnOp::Deref => ::rustc_middle::util::bug::bug_fmt(format_args!("Deref is not overloadable"))bug!("Deref is not overloadable"),
1208 }
1209}
1210
1211pub(crate) fn contains_let_in_chain(expr: &Expr<'_>) -> bool {
1213 match &expr.kind {
1214 ExprKind::Let(..) => true,
1215 ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, left, right) => {
1216 contains_let_in_chain(left) || contains_let_in_chain(right)
1217 }
1218 _ => false,
1219 }
1220}
1221
1222#[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)]
1225enum BinOpCategory {
1226 Shortcircuit,
1228
1229 Shift,
1232
1233 Math,
1236
1237 Bitwise,
1240
1241 Comparison,
1244}
1245
1246impl From<BinOpKind> for BinOpCategory {
1247 fn from(op: BinOpKind) -> BinOpCategory {
1248 use hir::BinOpKind::*;
1249 match op {
1250 Shl | Shr => BinOpCategory::Shift,
1251 Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
1252 BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
1253 Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
1254 And | Or => BinOpCategory::Shortcircuit,
1255 }
1256 }
1257}
1258
1259impl From<AssignOpKind> for BinOpCategory {
1260 fn from(op: AssignOpKind) -> BinOpCategory {
1261 use hir::AssignOpKind::*;
1262 match op {
1263 ShlAssign | ShrAssign => BinOpCategory::Shift,
1264 AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
1265 BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
1266 }
1267 }
1268}
1269
1270#[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)]
1272enum Op {
1273 BinOp(hir::BinOp),
1274 AssignOp(hir::AssignOp),
1275}
1276
1277impl Op {
1278 fn span(&self) -> Span {
1279 match self {
1280 Op::BinOp(op) => op.span,
1281 Op::AssignOp(op) => op.span,
1282 }
1283 }
1284
1285 fn as_str(&self) -> &'static str {
1286 match self {
1287 Op::BinOp(op) => op.node.as_str(),
1288 Op::AssignOp(op) => op.node.as_str(),
1289 }
1290 }
1291
1292 fn is_by_value(&self) -> bool {
1293 match self {
1294 Op::BinOp(op) => op.node.is_by_value(),
1295 Op::AssignOp(op) => op.node.is_by_value(),
1296 }
1297 }
1298}
1299
1300fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1302 match ty.kind() {
1303 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1304 _ => ty,
1305 }
1306}
1307
1308fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1320 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1323
1324 match category {
1325 BinOpCategory::Shortcircuit => true,
1326 BinOpCategory::Shift => {
1327 lhs.references_error()
1328 || rhs.references_error()
1329 || lhs.is_integral() && rhs.is_integral()
1330 }
1331 BinOpCategory::Math => {
1332 lhs.references_error()
1333 || rhs.references_error()
1334 || lhs.is_integral() && rhs.is_integral()
1335 || lhs.is_floating_point() && rhs.is_floating_point()
1336 }
1337 BinOpCategory::Bitwise => {
1338 lhs.references_error()
1339 || rhs.references_error()
1340 || lhs.is_integral() && rhs.is_integral()
1341 || lhs.is_floating_point() && rhs.is_floating_point()
1342 || lhs.is_bool() && rhs.is_bool()
1343 }
1344 BinOpCategory::Comparison => {
1345 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1346 }
1347 }
1348}