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