]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_hir_typeck/src/op.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / compiler / rustc_hir_typeck / src / op.rs
CommitLineData
c34b1796
AL
1//! Code related to processing overloaded binary and unary operators.
2
7cac9316 3use super::method::MethodCallee;
17df50a5 4use super::{has_expected_num_generic_args, FnCtxt};
2b03887a 5use crate::Expectation;
29967ef6 6use rustc_ast as ast;
5e7ed085 7use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
dfeec247 8use rustc_hir as hir;
74b04a01 9use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
064997fb 10use rustc_infer::traits::ObligationCauseCode;
ba9703b0
XL
11use rustc_middle::ty::adjustment::{
12 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
13};
f2b60f7d 14use rustc_middle::ty::print::with_no_trimmed_paths;
9c376795
FG
15use rustc_middle::ty::{
16 self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
17};
2b03887a 18use rustc_session::errors::ExprParenthesesNeeded;
29967ef6 19use rustc_span::source_map::Spanned;
3dfed10e 20use rustc_span::symbol::{sym, Ident};
dfeec247 21use rustc_span::Span;
ba9703b0 22use rustc_trait_selection::infer::InferCtxtExt;
2b03887a 23use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
9c376795 24use rustc_trait_selection::traits::{self, FulfillmentError};
923072b8 25use rustc_type_ir::sty::TyKind::*;
60c5eb7d 26
dc9dc135 27impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9fa01778 28 /// Checks a `a <op>= b`
dc9dc135
XL
29 pub fn check_binop_assign(
30 &self,
dfeec247 31 expr: &'tcx hir::Expr<'tcx>,
dc9dc135 32 op: hir::BinOp,
dfeec247
XL
33 lhs: &'tcx hir::Expr<'tcx>,
34 rhs: &'tcx hir::Expr<'tcx>,
064997fb 35 expected: Expectation<'tcx>,
dc9dc135 36 ) -> Ty<'tcx> {
abe05a73 37 let (lhs_ty, rhs_ty, return_ty) =
064997fb 38 self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
a7813a04 39
dfeec247
XL
40 let ty =
41 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
5e7ed085 42 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
dfeec247
XL
43 self.tcx.mk_unit()
44 } else {
45 return_ty
46 };
47
923072b8
FG
48 self.check_lhs_assignable(lhs, "E0067", op.span, |err| {
49 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
50 if self
51 .lookup_op_method(
52 lhs_deref_ty,
9c376795 53 Some((rhs, rhs_ty)),
923072b8 54 Op::Binary(op, IsAssign::Yes),
064997fb 55 expected,
923072b8
FG
56 )
57 .is_ok()
58 {
f2b60f7d
FG
59 // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have
60 // emitted a better suggestion during error handling in check_overloaded_binop.
61 if self
62 .lookup_op_method(
63 lhs_ty,
9c376795 64 Some((rhs, rhs_ty)),
f2b60f7d
FG
65 Op::Binary(op, IsAssign::Yes),
66 expected,
67 )
68 .is_err()
69 {
70 err.downgrade_to_delayed_bug();
71 } else {
72 // Otherwise, it's valid to suggest dereferencing the LHS here.
73 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 }
923072b8
FG
80 }
81 }
82 });
c34b1796 83
9e0c209e 84 ty
c34b1796 85 }
c34b1796 86
9fa01778 87 /// Checks a potentially overloaded binary operator.
dc9dc135
XL
88 pub fn check_binop(
89 &self,
dfeec247 90 expr: &'tcx hir::Expr<'tcx>,
dc9dc135 91 op: hir::BinOp,
dfeec247
XL
92 lhs_expr: &'tcx hir::Expr<'tcx>,
93 rhs_expr: &'tcx hir::Expr<'tcx>,
064997fb 94 expected: Expectation<'tcx>,
dc9dc135 95 ) -> Ty<'tcx> {
a7813a04
XL
96 let tcx = self.tcx;
97
dfeec247
XL
98 debug!(
99 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
100 expr.hir_id, expr, op, lhs_expr, rhs_expr
101 );
a7813a04 102
a7813a04
XL
103 match BinOpCategory::from(op) {
104 BinOpCategory::Shortcircuit => {
105 // && and || are a simple case.
f035d41b 106 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
476ff2be 107 let lhs_diverges = self.diverges.get();
f035d41b 108 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
476ff2be
SL
109
110 // Depending on the LHS' value, the RHS can never execute.
111 self.diverges.set(lhs_diverges);
112
abe05a73 113 tcx.types.bool
c34b1796 114 }
a7813a04
XL
115 _ => {
116 // Otherwise, we always treat operators as if they are
117 // overloaded. This is the way to be most flexible w/r/t
118 // types that get inferred.
064997fb
FG
119 let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
120 expr,
121 lhs_expr,
122 rhs_expr,
123 op,
124 IsAssign::No,
125 expected,
126 );
a7813a04
XL
127
128 // Supply type inference hints if relevant. Probably these
129 // hints should be enforced during select as part of the
130 // `consider_unification_despite_ambiguity` routine, but this
131 // more convenient for now.
132 //
133 // The basic idea is to help type inference by taking
134 // advantage of things we know about how the impls for
135 // scalar types are arranged. This is important in a
136 // scenario like `1_u32 << 2`, because it lets us quickly
137 // deduce that the result type should be `u32`, even
138 // though we don't know yet what type 2 has and hence
139 // can't pin this down to a specific impl.
dfeec247
XL
140 if !lhs_ty.is_ty_var()
141 && !rhs_ty.is_ty_var()
142 && is_builtin_binop(lhs_ty, rhs_ty, op)
a7813a04 143 {
74b04a01 144 let builtin_return_ty = self.enforce_builtin_binop_types(
5e7ed085 145 lhs_expr.span,
74b04a01 146 lhs_ty,
5e7ed085 147 rhs_expr.span,
74b04a01
XL
148 rhs_ty,
149 op,
150 );
a7813a04
XL
151 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
152 }
c34b1796 153
9e0c209e 154 return_ty
a7813a04 155 }
c34b1796
AL
156 }
157 }
c34b1796 158
dc9dc135
XL
159 fn enforce_builtin_binop_types(
160 &self,
5e7ed085 161 lhs_span: Span,
dc9dc135 162 lhs_ty: Ty<'tcx>,
5e7ed085 163 rhs_span: Span,
dc9dc135
XL
164 rhs_ty: Ty<'tcx>,
165 op: hir::BinOp,
166 ) -> Ty<'tcx> {
a7813a04
XL
167 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
168
74b04a01
XL
169 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
170 // (See https://github.com/rust-lang/rust/issues/57447.)
171 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
172
a7813a04
XL
173 let tcx = self.tcx;
174 match BinOpCategory::from(op) {
175 BinOpCategory::Shortcircuit => {
5e7ed085
FG
176 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
177 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
f9f354fc 178 tcx.types.bool
a7813a04 179 }
c34b1796 180
a7813a04
XL
181 BinOpCategory::Shift => {
182 // result type is same as LHS always
183 lhs_ty
184 }
c34b1796 185
dfeec247 186 BinOpCategory::Math | BinOpCategory::Bitwise => {
a7813a04 187 // both LHS and RHS and result will have the same type
5e7ed085 188 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
a7813a04
XL
189 lhs_ty
190 }
c34b1796 191
a7813a04
XL
192 BinOpCategory::Comparison => {
193 // both LHS and RHS and result will have the same type
5e7ed085 194 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
f9f354fc 195 tcx.types.bool
a7813a04 196 }
c34b1796
AL
197 }
198 }
c34b1796 199
dc9dc135
XL
200 fn check_overloaded_binop(
201 &self,
dfeec247
XL
202 expr: &'tcx hir::Expr<'tcx>,
203 lhs_expr: &'tcx hir::Expr<'tcx>,
204 rhs_expr: &'tcx hir::Expr<'tcx>,
dc9dc135
XL
205 op: hir::BinOp,
206 is_assign: IsAssign,
064997fb 207 expected: Expectation<'tcx>,
dc9dc135 208 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
dfeec247
XL
209 debug!(
210 "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
211 expr.hir_id, op, is_assign
212 );
a7813a04 213
74d20737
XL
214 let lhs_ty = match is_assign {
215 IsAssign::No => {
216 // Find a suitable supertype of the LHS expression's type, by coercing to
217 // a type variable, to pass as the `Self` to the trait, avoiding invariant
218 // trait matching creating lifetime constraints that are too strict.
0731742a 219 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
74d20737 220 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
f035d41b 221 let lhs_ty = self.check_expr(lhs_expr);
dc9dc135
XL
222 let fresh_var = self.next_ty_var(TypeVariableOrigin {
223 kind: TypeVariableOriginKind::MiscVariable,
224 span: lhs_expr.span,
225 });
f035d41b 226 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
74d20737
XL
227 }
228 IsAssign::Yes => {
229 // rust-lang/rust#52126: We have to use strict
230 // equivalence on the LHS of an assign-op like `+=`;
231 // overwritten or mutably-borrowed places cannot be
232 // coerced to a supertype.
f035d41b 233 self.check_expr(lhs_expr)
74d20737 234 }
abe05a73 235 };
e74abb32 236 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
abe05a73 237
0731742a 238 // N.B., as we have not yet type-checked the RHS, we don't have the
a7813a04
XL
239 // type at hand. Make a variable to represent it. The whole reason
240 // for this indirection is so that, below, we can check the expr
241 // using this variable as the expected type, which sometimes lets
242 // us do better coercions than we would be able to do otherwise,
243 // particularly for things like `String + &String`.
dc9dc135
XL
244 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
245 kind: TypeVariableOriginKind::MiscVariable,
246 span: rhs_expr.span,
247 });
a7813a04 248
5e7ed085
FG
249 let result = self.lookup_op_method(
250 lhs_ty,
9c376795 251 Some((rhs_expr, rhs_ty_var)),
5e7ed085 252 Op::Binary(op, is_assign),
064997fb 253 expected,
5e7ed085 254 );
8bb4bdeb
XL
255
256 // see `NB` above
f035d41b 257 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
e74abb32 258 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
8bb4bdeb 259
7cac9316
XL
260 let return_ty = match result {
261 Ok(method) => {
262 let by_ref_binop = !op.node.is_by_value();
263 if is_assign == IsAssign::Yes || by_ref_binop {
1b1a35ee 264 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
487cf647 265 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
7cac9316 266 let autoref = Adjustment {
5099ac24 267 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
dfeec247 268 target: method.sig.inputs()[0],
7cac9316
XL
269 };
270 self.apply_adjustments(lhs_expr, vec![autoref]);
271 }
272 }
273 if by_ref_binop {
1b1a35ee 274 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
487cf647
FG
275 // Allow two-phase borrows for binops in initial deployment
276 // since they desugar to methods
277 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
278
7cac9316 279 let autoref = Adjustment {
5099ac24 280 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
dfeec247 281 target: method.sig.inputs()[1],
7cac9316
XL
282 };
283 // HACK(eddyb) Bypass checks due to reborrows being in
284 // some cases applied on the RHS, on top of which we need
285 // to autoref, which is not allowed by apply_adjustments.
286 // self.apply_adjustments(rhs_expr, vec![autoref]);
3dfed10e 287 self.typeck_results
3b2f2976
XL
288 .borrow_mut()
289 .adjustments_mut()
290 .entry(rhs_expr.hir_id)
b7449926 291 .or_default()
3b2f2976 292 .push(autoref);
7cac9316
XL
293 }
294 }
3b2f2976 295 self.write_method_call(expr.hir_id, method);
7cac9316
XL
296
297 method.sig.output()
298 }
f035d41b 299 // error types are considered "builtin"
c295e0f8
XL
300 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
301 Err(errors) => {
f2b60f7d
FG
302 let (_, trait_def_id) =
303 lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
304 let missing_trait = trait_def_id
305 .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
306 let (mut err, output_def_id) = match is_assign {
f035d41b
XL
307 IsAssign::Yes => {
308 let mut err = struct_span_err!(
309 self.tcx.sess,
310 expr.span,
311 E0368,
312 "binary assignment operation `{}=` cannot be applied to type `{}`",
313 op.node.as_str(),
314 lhs_ty,
315 );
316 err.span_label(
317 lhs_expr.span,
318 format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
319 );
c295e0f8 320 self.note_unmet_impls_on_type(&mut err, errors);
f2b60f7d 321 (err, None)
f035d41b
XL
322 }
323 IsAssign::No => {
f2b60f7d
FG
324 let message = match op.node {
325 hir::BinOpKind::Add => {
326 format!("cannot add `{rhs_ty}` to `{lhs_ty}`")
327 }
328 hir::BinOpKind::Sub => {
329 format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`")
330 }
331 hir::BinOpKind::Mul => {
332 format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`")
333 }
334 hir::BinOpKind::Div => {
335 format!("cannot divide `{lhs_ty}` by `{rhs_ty}`")
336 }
337 hir::BinOpKind::Rem => {
338 format!("cannot mod `{lhs_ty}` by `{rhs_ty}`")
339 }
340 hir::BinOpKind::BitAnd => {
341 format!("no implementation for `{lhs_ty} & {rhs_ty}`")
342 }
343 hir::BinOpKind::BitXor => {
344 format!("no implementation for `{lhs_ty} ^ {rhs_ty}`")
345 }
346 hir::BinOpKind::BitOr => {
347 format!("no implementation for `{lhs_ty} | {rhs_ty}`")
348 }
349 hir::BinOpKind::Shl => {
350 format!("no implementation for `{lhs_ty} << {rhs_ty}`")
351 }
352 hir::BinOpKind::Shr => {
353 format!("no implementation for `{lhs_ty} >> {rhs_ty}`")
354 }
355 _ => format!(
356 "binary operation `{}` cannot be applied to type `{}`",
357 op.node.as_str(),
358 lhs_ty
f035d41b
XL
359 ),
360 };
f2b60f7d
FG
361 let output_def_id = trait_def_id.and_then(|def_id| {
362 self.tcx
363 .associated_item_def_ids(def_id)
364 .iter()
365 .find(|item_def_id| {
366 self.tcx.associated_item(*item_def_id).name == sym::Output
367 })
368 .cloned()
369 });
064997fb 370 let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
f035d41b 371 if !lhs_expr.span.eq(&rhs_expr.span) {
f2b60f7d
FG
372 err.span_label(lhs_expr.span, lhs_ty.to_string());
373 err.span_label(rhs_expr.span, rhs_ty.to_string());
32a655c1 374 }
c295e0f8 375 self.note_unmet_impls_on_type(&mut err, errors);
f2b60f7d 376 (err, output_def_id)
f035d41b
XL
377 }
378 };
923072b8
FG
379
380 let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
381 if self
382 .lookup_op_method(
383 lhs_deref_ty,
9c376795 384 Some((rhs_expr, rhs_ty)),
923072b8 385 Op::Binary(op, is_assign),
064997fb 386 expected,
923072b8
FG
387 )
388 .is_ok()
3c0e092e 389 {
f2b60f7d
FG
390 let msg = &format!(
391 "`{}{}` can be used on `{}` if you dereference the left-hand side",
392 op.node.as_str(),
393 match is_assign {
394 IsAssign::Yes => "=",
395 IsAssign::No => "",
396 },
397 lhs_deref_ty,
398 );
399 err.span_suggestion_verbose(
400 lhs_expr.span.shrink_to_lo(),
401 msg,
402 "*",
403 rustc_errors::Applicability::MachineApplicable,
404 );
f035d41b 405 }
923072b8
FG
406 };
407
f2b60f7d
FG
408 let is_compatible = |lhs_ty, rhs_ty| {
409 self.lookup_op_method(
410 lhs_ty,
9c376795 411 Some((rhs_expr, rhs_ty)),
f2b60f7d
FG
412 Op::Binary(op, is_assign),
413 expected,
414 )
415 .is_ok()
416 };
417
923072b8
FG
418 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
419 // `a += b` => `*a += b` if a is a mut ref.
f2b60f7d
FG
420 if !op.span.can_be_used_for_suggestions() {
421 // Suppress suggestions when lhs and rhs are not in the same span as the error
422 } else if is_assign == IsAssign::Yes
423 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
424 {
425 suggest_deref_binop(lhs_deref_ty);
923072b8 426 } else if is_assign == IsAssign::No
f2b60f7d
FG
427 && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
428 {
429 if self.type_is_copy_modulo_regions(
430 self.param_env,
431 *lhs_deref_ty,
432 lhs_expr.span,
433 ) {
923072b8
FG
434 suggest_deref_binop(*lhs_deref_ty);
435 }
f2b60f7d
FG
436 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
437 is_compatible(lhs_ty, rhs_ty)
438 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
439 is_compatible(lhs_ty, rhs_ty)
440 }) || self.suggest_two_fn_call(
441 &mut err,
442 rhs_expr,
443 rhs_ty,
444 lhs_expr,
445 lhs_ty,
446 |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
447 ) {
448 // Cool
f035d41b 449 }
f035d41b 450
f2b60f7d 451 if let Some(missing_trait) = missing_trait {
f035d41b
XL
452 if op.node == hir::BinOpKind::Add
453 && self.check_str_addition(
454 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
455 )
456 {
457 // This has nothing here because it means we did string
458 // concatenation (e.g., "Hello " + "World!"). This means
459 // we don't want the note in the else clause to be emitted
2b03887a 460 } else if lhs_ty.has_non_region_param() {
04454e1e
FG
461 // Look for a TraitPredicate in the Fulfillment errors,
462 // and use it to generate a suggestion.
463 //
464 // Note that lookup_op_method must be called again but
465 // with a specific rhs_ty instead of a placeholder so
466 // the resulting predicate generates a more specific
467 // suggestion for the user.
468 let errors = self
469 .lookup_op_method(
470 lhs_ty,
9c376795 471 Some((rhs_expr, rhs_ty)),
04454e1e 472 Op::Binary(op, is_assign),
064997fb 473 expected,
04454e1e
FG
474 )
475 .unwrap_err();
064997fb
FG
476 if !errors.is_empty() {
477 for error in errors {
478 if let Some(trait_pred) =
479 error.obligation.predicate.to_opt_poly_trait_pred()
480 {
f2b60f7d
FG
481 let output_associated_item = match error.obligation.cause.code()
482 {
064997fb 483 ObligationCauseCode::BinOp {
f2b60f7d 484 output_ty: Some(output_ty),
064997fb 485 ..
f2b60f7d
FG
486 } => {
487 // Make sure that we're attaching `Output = ..` to the right trait predicate
488 if let Some(output_def_id) = output_def_id
489 && let Some(trait_def_id) = trait_def_id
490 && self.tcx.parent(output_def_id) == trait_def_id
9c376795 491 && output_ty.is_suggestable(self.tcx, false)
f2b60f7d
FG
492 {
493 Some(("Output", *output_ty))
494 } else {
495 None
496 }
064997fb
FG
497 }
498 _ => None,
499 };
500
2b03887a 501 self.err_ctxt().suggest_restricting_param_bound(
064997fb
FG
502 &mut err,
503 trait_pred,
f2b60f7d 504 output_associated_item,
064997fb
FG
505 self.body_id,
506 );
507 }
532ac7d7 508 }
f2b60f7d 509 } else {
04454e1e
FG
510 // When we know that a missing bound is responsible, we don't show
511 // this note as it is redundant.
512 err.note(&format!(
513 "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
514 ));
a7813a04 515 }
b039eaaf
SL
516 }
517 }
487cf647
FG
518 let reported = err.emit();
519 self.tcx.ty_error_with_guaranteed(reported)
c34b1796 520 }
a7813a04 521 };
c34b1796 522
abe05a73 523 (lhs_ty, rhs_ty, return_ty)
a7813a04 524 }
c34b1796 525
48663c56
XL
526 /// Provide actionable suggestions when trying to add two strings with incorrect types,
527 /// like `&str + &str`, `String + String` and `&str + &String`.
528 ///
529 /// If this function returns `true` it means a note was printed, so we don't need
530 /// to print the normal "implementation of `std::ops::Add` might be missing" note
8faf50e0
XL
531 fn check_str_addition(
532 &self,
dfeec247
XL
533 lhs_expr: &'tcx hir::Expr<'tcx>,
534 rhs_expr: &'tcx hir::Expr<'tcx>,
8faf50e0
XL
535 lhs_ty: Ty<'tcx>,
536 rhs_ty: Ty<'tcx>,
5e7ed085 537 err: &mut Diagnostic,
f035d41b 538 is_assign: IsAssign,
532ac7d7 539 op: hir::BinOp,
8faf50e0 540 ) -> bool {
5099ac24
FG
541 let str_concat_note = "string concatenation requires an owned `String` on the left";
542 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
543 let to_owned_msg = "create an owned `String` from a string reference";
48663c56 544
487cf647 545 let string_type = self.tcx.lang_items().string();
064997fb 546 let is_std_string = |ty: Ty<'tcx>| {
487cf647 547 ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type)
3dfed10e 548 };
48663c56 549
1b1a35ee 550 match (lhs_ty.kind(), rhs_ty.kind()) {
48663c56 551 (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
064997fb
FG
552 if (*l_ty.kind() == Str || is_std_string(l_ty))
553 && (*r_ty.kind() == Str
554 || is_std_string(r_ty)
555 || matches!(
556 r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str
557 )) =>
48663c56 558 {
f035d41b 559 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
5099ac24
FG
560 err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
561 err.note(str_concat_note);
562 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
563 err.span_suggestion_verbose(
564 lhs_expr.span.until(lhs_inner_expr.span),
565 rm_borrow_msg,
923072b8 566 "",
5099ac24
FG
567 Applicability::MachineApplicable
568 );
569 } else {
570 err.span_suggestion_verbose(
571 lhs_expr.span.shrink_to_hi(),
572 to_owned_msg,
923072b8 573 ".to_owned()",
5099ac24
FG
574 Applicability::MachineApplicable
575 );
576 }
8faf50e0 577 }
0531ce1d 578 true
8bb4bdeb 579 }
48663c56 580 (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
1b1a35ee 581 if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
48663c56
XL
582 {
583 err.span_label(
584 op.span,
585 "`+` cannot be used to concatenate a `&str` with a `String`",
586 );
5099ac24
FG
587 match is_assign {
588 IsAssign::No => {
589 let sugg_msg;
590 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
591 sugg_msg = "remove the borrow on the left and add one on the right";
592 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
48663c56 593 } else {
5099ac24
FG
594 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
595 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
48663c56 596 };
5099ac24
FG
597 let suggestions = vec![
598 lhs_sugg,
599 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
600 ];
601 err.multipart_suggestion_verbose(
602 sugg_msg,
603 suggestions,
0bf4aa26
XL
604 Applicability::MachineApplicable,
605 );
8faf50e0 606 }
5099ac24
FG
607 IsAssign::Yes => {
608 err.note(str_concat_note);
0531ce1d 609 }
5099ac24 610 }
0531ce1d
XL
611 true
612 }
613 _ => false,
8bb4bdeb 614 }
8bb4bdeb
XL
615 }
616
dc9dc135
XL
617 pub fn check_user_unop(
618 &self,
dfeec247 619 ex: &'tcx hir::Expr<'tcx>,
dc9dc135
XL
620 operand_ty: Ty<'tcx>,
621 op: hir::UnOp,
064997fb 622 expected: Expectation<'tcx>,
dc9dc135 623 ) -> Ty<'tcx> {
a7813a04 624 assert!(op.is_by_value());
9c376795 625 match self.lookup_op_method(operand_ty, None, Op::Unary(op, ex.span), expected) {
7cac9316 626 Ok(method) => {
3b2f2976 627 self.write_method_call(ex.hir_id, method);
7cac9316
XL
628 method.sig.output()
629 }
c295e0f8 630 Err(errors) => {
fc512014 631 let actual = self.resolve_vars_if_possible(operand_ty);
7cac9316 632 if !actual.references_error() {
dfeec247
XL
633 let mut err = struct_span_err!(
634 self.tcx.sess,
635 ex.span,
636 E0600,
637 "cannot apply unary operator `{}` to type `{}`",
638 op.as_str(),
639 actual
640 );
641 err.span_label(
642 ex.span,
f035d41b 643 format!("cannot apply unary operator `{}`", op.as_str()),
dfeec247 644 );
04454e1e 645
2b03887a 646 if operand_ty.has_non_region_param() {
f2b60f7d
FG
647 let predicates = errors.iter().filter_map(|error| {
648 error.obligation.predicate.to_opt_poly_trait_pred()
649 });
04454e1e 650 for pred in predicates {
2b03887a 651 self.err_ctxt().suggest_restricting_param_bound(
04454e1e
FG
652 &mut err,
653 pred,
064997fb 654 None,
04454e1e
FG
655 self.body_id,
656 );
657 }
5e7ed085 658 }
c295e0f8 659
9c376795 660 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
c295e0f8
XL
661 if let Some(sp) =
662 self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
663 {
664 // If the previous expression was a block expression, suggest parentheses
665 // (turning this into a binary subtraction operation instead.)
666 // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
2b03887a 667 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
c295e0f8
XL
668 } else {
669 match actual.kind() {
670 Uint(_) if op == hir::UnOp::Neg => {
671 err.note("unsigned values cannot be negated");
672
673 if let hir::ExprKind::Unary(
674 _,
675 hir::Expr {
676 kind:
677 hir::ExprKind::Lit(Spanned {
678 node: ast::LitKind::Int(1, _),
679 ..
680 }),
681 ..
682 },
683 ) = ex.kind
684 {
685 err.span_suggestion(
686 ex.span,
687 &format!(
5e7ed085 688 "you may have meant the maximum value of `{actual}`",
c295e0f8 689 ),
5e7ed085 690 format!("{actual}::MAX"),
c295e0f8
XL
691 Applicability::MaybeIncorrect,
692 );
693 }
694 }
695 Str | Never | Char | Tuple(_) | Array(_, _) => {}
696 Ref(_, lty, _) if *lty.kind() == Str => {}
697 _ => {
698 self.note_unmet_impls_on_type(&mut err, errors);
29967ef6 699 }
94b46f34
XL
700 }
701 }
702 err.emit();
7cac9316 703 }
f035d41b 704 self.tcx.ty_error()
a7813a04 705 }
c34b1796
AL
706 }
707 }
c34b1796 708
dfeec247
XL
709 fn lookup_op_method(
710 &self,
711 lhs_ty: Ty<'tcx>,
9c376795 712 opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
dfeec247 713 op: Op,
064997fb 714 expected: Expectation<'tcx>,
c295e0f8 715 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
7cac9316
XL
716 let span = match op {
717 Op::Binary(op, _) => op.span,
dfeec247 718 Op::Unary(_, span) => span,
7cac9316 719 };
f2b60f7d 720 let (opname, trait_did) = lang_item_for_op(self.tcx, op, span);
c34b1796 721
dfeec247
XL
722 debug!(
723 "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
724 lhs_ty, op, opname, trait_did
725 );
7cac9316 726
17df50a5
XL
727 // Catches cases like #83893, where a lang item is declared with the
728 // wrong number of generic arguments. Should have yielded an error
729 // elsewhere by now, but we have to catch it here so that we do not
730 // index `other_tys` out of bounds (if the lang item has too many
731 // generic arguments, `other_tys` is too short).
732 if !has_expected_num_generic_args(
733 self.tcx,
734 trait_did,
735 match op {
736 // Binary ops have a generic right-hand side, unary ops don't
737 Op::Binary(..) => 1,
738 Op::Unary(..) => 0,
739 },
740 ) {
9c376795
FG
741 self.tcx
742 .sess
743 .delay_span_bug(span, "operator didn't have the right number of generic args");
c295e0f8 744 return Err(vec![]);
17df50a5
XL
745 }
746
c295e0f8 747 let opname = Ident::with_dummy_span(opname);
9c376795
FG
748 let input_types =
749 opt_rhs.as_ref().map(|(_, ty)| std::slice::from_ref(ty)).unwrap_or_default();
750 let cause = self.cause(
751 span,
752 traits::BinOp {
753 rhs_span: opt_rhs.map(|(expr, _)| expr.span),
754 is_lit: opt_rhs
755 .map_or(false, |(expr, _)| matches!(expr.kind, hir::ExprKind::Lit(_))),
756 output_ty: expected.only_has_type(self),
757 },
758 );
759
7cac9316 760 let method = trait_did.and_then(|trait_did| {
9c376795 761 self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, Some(input_types))
7cac9316 762 });
c34b1796 763
c295e0f8
XL
764 match (method, trait_did) {
765 (Some(ok), _) => {
cc61c64b 766 let method = self.register_infer_ok_obligations(ok);
487cf647 767 self.select_obligations_where_possible(|_| {});
7cac9316 768 Ok(method)
a7813a04 769 }
c295e0f8
XL
770 (None, None) => Err(vec![]),
771 (None, Some(trait_did)) => {
9c376795
FG
772 let (obligation, _) =
773 self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types));
487cf647 774 Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation))
c295e0f8 775 }
c34b1796
AL
776 }
777 }
778}
779
f2b60f7d
FG
780fn lang_item_for_op(
781 tcx: TyCtxt<'_>,
782 op: Op,
783 span: Span,
784) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
785 let lang = tcx.lang_items();
786 if let Op::Binary(op, IsAssign::Yes) = op {
787 match op.node {
788 hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
789 hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
790 hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
791 hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
792 hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
793 hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
794 hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
795 hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
796 hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
797 hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
798 hir::BinOpKind::Lt
799 | hir::BinOpKind::Le
800 | hir::BinOpKind::Ge
801 | hir::BinOpKind::Gt
802 | hir::BinOpKind::Eq
803 | hir::BinOpKind::Ne
804 | hir::BinOpKind::And
805 | hir::BinOpKind::Or => {
806 span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
807 }
808 }
809 } else if let Op::Binary(op, IsAssign::No) = op {
810 match op.node {
811 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
812 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
813 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
814 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
815 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
816 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
817 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
818 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
819 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
820 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
821 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
822 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
823 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
824 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
825 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
826 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
827 hir::BinOpKind::And | hir::BinOpKind::Or => {
828 span_bug!(span, "&& and || are not overloadable")
829 }
830 }
831 } else if let Op::Unary(hir::UnOp::Not, _) = op {
832 (sym::not, lang.not_trait())
833 } else if let Op::Unary(hir::UnOp::Neg, _) = op {
834 (sym::neg, lang.neg_trait())
835 } else {
836 bug!("lookup_op_method: op not supported: {:?}", op)
837 }
838}
839
c34b1796 840// Binary operator categories. These categories summarize the behavior
5e7ed085 841// with respect to the builtin operations supported.
c34b1796
AL
842enum BinOpCategory {
843 /// &&, || -- cannot be overridden
844 Shortcircuit,
845
846 /// <<, >> -- when shifting a single integer, rhs can be any
847 /// integer type. For simd, types must match.
848 Shift,
849
850 /// +, -, etc -- takes equal types, produces same type as input,
851 /// applicable to ints/floats/simd
852 Math,
853
854 /// &, |, ^ -- takes equal types, produces same type as input,
855 /// applicable to ints/floats/simd/bool
856 Bitwise,
857
858 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
859 /// which produce the input type
860 Comparison,
861}
862
863impl BinOpCategory {
e9174d1e 864 fn from(op: hir::BinOp) -> BinOpCategory {
c34b1796 865 match op.node {
dfeec247
XL
866 hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
867
868 hir::BinOpKind::Add
869 | hir::BinOpKind::Sub
870 | hir::BinOpKind::Mul
871 | hir::BinOpKind::Div
872 | hir::BinOpKind::Rem => BinOpCategory::Math,
873
874 hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
875 BinOpCategory::Bitwise
876 }
877
878 hir::BinOpKind::Eq
879 | hir::BinOpKind::Ne
880 | hir::BinOpKind::Lt
881 | hir::BinOpKind::Le
882 | hir::BinOpKind::Ge
883 | hir::BinOpKind::Gt => BinOpCategory::Comparison,
884
885 hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
c34b1796
AL
886 }
887 }
888}
889
b039eaaf 890/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
7cac9316 891#[derive(Clone, Copy, Debug, PartialEq)]
b039eaaf
SL
892enum IsAssign {
893 No,
894 Yes,
895}
896
7cac9316
XL
897#[derive(Clone, Copy, Debug)]
898enum Op {
899 Binary(hir::BinOp, IsAssign),
900 Unary(hir::UnOp, Span),
901}
902
74b04a01 903/// Dereferences a single level of immutable referencing.
9c376795 904fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1b1a35ee 905 match ty.kind() {
5099ac24 906 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
74b04a01
XL
907 _ => ty,
908 }
909}
910
9fa01778 911/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
c34b1796
AL
912/// + u32, i16x4 == i16x4) and false if these types would have to be
913/// overloaded to be legal. There are two reasons that we distinguish
914/// builtin operations from overloaded ones (vs trying to drive
915/// everything uniformly through the trait system and intrinsics or
916/// something like that):
917///
918/// 1. Builtin operations can trivially be evaluated in constants.
919/// 2. For comparison operators applied to SIMD types the result is
9fa01778 920/// not of type `bool`. For example, `i16x4 == i16x4` yields a
c34b1796
AL
921/// type like `i16x4`. This means that the overloaded trait
922/// `PartialEq` is not applicable.
923///
924/// Reason #2 is the killer. I tried for a while to always use
94b46f34 925/// overloaded logic and just check the types in constants/codegen after
c34b1796 926/// the fact, and it worked fine, except for SIMD types. -nmatsakis
74b04a01
XL
927fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
928 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
929 // (See https://github.com/rust-lang/rust/issues/57447.)
930 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
931
c34b1796 932 match BinOpCategory::from(op) {
dfeec247 933 BinOpCategory::Shortcircuit => true,
c34b1796
AL
934
935 BinOpCategory::Shift => {
dfeec247
XL
936 lhs.references_error()
937 || rhs.references_error()
938 || lhs.is_integral() && rhs.is_integral()
c34b1796
AL
939 }
940
941 BinOpCategory::Math => {
dfeec247
XL
942 lhs.references_error()
943 || rhs.references_error()
944 || lhs.is_integral() && rhs.is_integral()
945 || lhs.is_floating_point() && rhs.is_floating_point()
c34b1796
AL
946 }
947
948 BinOpCategory::Bitwise => {
dfeec247
XL
949 lhs.references_error()
950 || rhs.references_error()
951 || lhs.is_integral() && rhs.is_integral()
952 || lhs.is_floating_point() && rhs.is_floating_point()
953 || lhs.is_bool() && rhs.is_bool()
c34b1796
AL
954 }
955
956 BinOpCategory::Comparison => {
dfeec247 957 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
c34b1796
AL
958 }
959 }
960}
74b04a01 961
f035d41b
XL
962struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
963
a2a8927a 964impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
f035d41b
XL
965 fn tcx(&self) -> TyCtxt<'tcx> {
966 self.0.tcx
967 }
968
969 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1b1a35ee 970 match ty.kind() {
f035d41b
XL
971 ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
972 kind: TypeVariableOriginKind::MiscVariable,
973 span: self.1,
974 }),
975 _ => ty.super_fold_with(self),
976 }
977 }
978}