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