about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs36
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs24
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs128
-rw-r--r--compiler/rustc_typeck/src/check/op.rs38
6 files changed, 214 insertions, 18 deletions
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 072e6346cde..45610fa77d3 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -368,6 +368,11 @@ pub enum ObligationCauseCode<'tcx> {
 
     /// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
     MatchImpl(ObligationCause<'tcx>, DefId),
+
+    BinOp {
+        rhs_span: Option<Span>,
+        is_lit: bool,
+    },
 }
 
 /// The 'location' at which we try to perform HIR-based wf checking.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index f26f32aabda..f5beb83729f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -501,6 +501,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             err.span_label(enclosing_scope_span, s.as_str());
                         }
 
+                        self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
                         self.suggest_dereferences(&obligation, &mut err, trait_predicate);
                         self.suggest_fn_call(&obligation, &mut err, trait_predicate);
                         self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index c4fbd25b833..96c24a5ac4c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -174,6 +174,13 @@ pub trait InferCtxtExt<'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
     );
+
+    fn suggest_floating_point_literal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
@@ -1910,8 +1917,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             | ObligationCauseCode::AwaitableExpr(_)
             | ObligationCauseCode::ForLoopIterator
             | ObligationCauseCode::QuestionMark
+            | ObligationCauseCode::CheckAssociatedTypeBounds { .. }
             | ObligationCauseCode::LetElse
-            | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {}
+            | ObligationCauseCode::BinOp { .. } => {}
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
@@ -2497,6 +2505,32 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             }
         }
     }
+
+    fn suggest_floating_point_literal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) {
+        let rhs_span = match obligation.cause.code() {
+            ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span,
+            _ => return,
+        };
+        match (
+            trait_ref.skip_binder().self_ty().kind(),
+            trait_ref.skip_binder().substs.type_at(1).kind(),
+        ) {
+            (ty::Float(_), ty::Infer(InferTy::IntVar(_))) => {
+                err.span_suggestion_verbose(
+                    rhs_span.shrink_to_hi(),
+                    "consider using a floating-point literal by writing it with `.0`",
+                    String::from(".0"),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            _ => {}
+        }
+    }
 }
 
 /// Collect all the returned expressions within the input expression.
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index d8840731263..5cb1fe8cd94 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -438,6 +438,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         )
     }
 
+    pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>(
+        &self,
+        span: Span,
+        value: T,
+        opt_input_expr: Option<&hir::Expr<'_>>,
+    ) -> InferOk<'tcx, T>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.inh.partially_normalize_associated_types_in(
+            ObligationCause::new(
+                span,
+                self.body_id,
+                traits::BinOp {
+                    rhs_span: opt_input_expr.map(|expr| expr.span),
+                    is_lit: opt_input_expr
+                        .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
+                },
+            ),
+            self.param_env,
+            value,
+        )
+    }
+
     pub fn require_type_meets(
         &self,
         ty: Ty<'tcx>,
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index 999aafbd537..7bb84581a82 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -333,15 +333,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         )
     }
 
+    pub(super) fn obligation_for_op_method(
+        &self,
+        span: Span,
+        trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        opt_input_type: Option<Ty<'tcx>>,
+        opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+    ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
+    {
+        // Construct a trait-reference `self_ty : Trait<input_tys>`
+        let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
+            match param.kind {
+                GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
+                GenericParamDefKind::Type { .. } => {
+                    if param.index == 0 {
+                        return self_ty.into();
+                    } else if let Some(input_type) = opt_input_type {
+                        return input_type.into();
+                    }
+                }
+            }
+            self.var_for_def(span, param)
+        });
+
+        let trait_ref = ty::TraitRef::new(trait_def_id, substs);
+
+        // Construct an obligation
+        let poly_trait_ref = ty::Binder::dummy(trait_ref);
+        (
+            traits::Obligation::new(
+                traits::ObligationCause::new(
+                    span,
+                    self.body_id,
+                    traits::BinOp {
+                        rhs_span: opt_input_expr.map(|expr| expr.span),
+                        is_lit: opt_input_expr
+                            .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+                    },
+                ),
+                self.param_env,
+                poly_trait_ref.without_const().to_predicate(self.tcx),
+            ),
+            substs,
+        )
+    }
+
     /// `lookup_method_in_trait` is used for overloaded operators.
     /// It does a very narrow slice of what the normal probe/confirm path does.
     /// In particular, it doesn't really do any probing: it simply constructs
     /// an obligation for a particular trait with the given self type and checks
     /// whether that trait is implemented.
-    //
-    // FIXME(#18741): it seems likely that we can consolidate some of this
-    // code with the other method-lookup code. In particular, the second half
-    // of this method is basically the same as confirmation.
     #[instrument(level = "debug", skip(self, span, opt_input_types))]
     pub(super) fn lookup_method_in_trait(
         &self,
@@ -358,7 +400,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let (obligation, substs) =
             self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
+        self.construct_obligation_for_trait(
+            span,
+            m_name,
+            trait_def_id,
+            obligation,
+            substs,
+            None,
+            false,
+        )
+    }
 
+    pub(super) fn lookup_op_method_in_trait(
+        &self,
+        span: Span,
+        m_name: Ident,
+        trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        opt_input_type: Option<Ty<'tcx>>,
+        opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+        let (obligation, substs) = self.obligation_for_op_method(
+            span,
+            trait_def_id,
+            self_ty,
+            opt_input_type,
+            opt_input_expr,
+        );
+        self.construct_obligation_for_trait(
+            span,
+            m_name,
+            trait_def_id,
+            obligation,
+            substs,
+            opt_input_expr,
+            true,
+        )
+    }
+
+    // FIXME(#18741): it seems likely that we can consolidate some of this
+    // code with the other method-lookup code. In particular, the second half
+    // of this method is basically the same as confirmation.
+    fn construct_obligation_for_trait(
+        &self,
+        span: Span,
+        m_name: Ident,
+        trait_def_id: DefId,
+        obligation: traits::PredicateObligation<'tcx>,
+        substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
+        opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+        is_op: bool,
+    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
         debug!(?obligation);
 
         // Now we want to know if this can be matched
@@ -395,8 +487,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
         let fn_sig = fn_sig.subst(self.tcx, substs);
 
-        let InferOk { value, obligations: o } =
-            self.normalize_associated_types_in_as_infer_ok(span, fn_sig);
+        let InferOk { value, obligations: o } = if is_op {
+            self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
+        } else {
+            self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
+        };
         let fn_sig = {
             obligations.extend(o);
             value
@@ -412,8 +507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // any late-bound regions appearing in its bounds.
         let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
 
-        let InferOk { value, obligations: o } =
-            self.normalize_associated_types_in_as_infer_ok(span, bounds);
+        let InferOk { value, obligations: o } = if is_op {
+            self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
+        } else {
+            self.normalize_associated_types_in_as_infer_ok(span, bounds)
+        };
         let bounds = {
             obligations.extend(o);
             value
@@ -421,7 +519,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         assert!(!bounds.has_escaping_bound_vars());
 
-        let cause = traits::ObligationCause::misc(span, self.body_id);
+        let cause = if is_op {
+            ObligationCause::new(
+                span,
+                self.body_id,
+                traits::BinOp {
+                    rhs_span: opt_input_expr.map(|expr| expr.span),
+                    is_lit: opt_input_expr
+                        .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+                },
+            )
+        } else {
+            traits::ObligationCause::misc(span, self.body_id)
+        };
         obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
 
         // Also add an obligation for the method type being well-formed.
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 5dd5e71656d..cd77e6d0384 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -201,7 +201,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             span: rhs_expr.span,
         });
 
-        let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
+        let result = self.lookup_op_method(
+            lhs_ty,
+            Some(rhs_ty_var),
+            Some(rhs_expr),
+            Op::Binary(op, is_assign),
+        );
 
         // see `NB` above
         let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
@@ -382,6 +387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 lhs_expr.span,
                                 lhs_ty,
                                 rhs_ty,
+                                rhs_expr,
                                 op,
                                 is_assign,
                             );
@@ -390,6 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 rhs_expr.span,
                                 rhs_ty,
                                 lhs_ty,
+                                lhs_expr,
                                 op,
                                 is_assign,
                             );
@@ -400,7 +407,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 };
                 if let Ref(_, rty, _) = lhs_ty.kind() {
                     if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
-                        && self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok()
+                        && self
+                            .lookup_op_method(
+                                *rty,
+                                Some(rhs_ty),
+                                Some(rhs_expr),
+                                Op::Binary(op, is_assign),
+                            )
+                            .is_ok()
                     {
                         if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
                             let msg = &format!(
@@ -443,7 +457,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             let needs_bound = self
                                 .lookup_op_method(
                                     eraser.fold_ty(lhs_ty),
-                                    &[eraser.fold_ty(rhs_ty)],
+                                    Some(eraser.fold_ty(rhs_ty)),
+                                    Some(rhs_expr),
                                     Op::Binary(op, is_assign),
                                 )
                                 .is_ok();
@@ -487,6 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Span,
         ty: Ty<'tcx>,
         other_ty: Ty<'tcx>,
+        other_expr: &'tcx hir::Expr<'tcx>,
         op: hir::BinOp,
         is_assign: IsAssign,
     ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
@@ -513,7 +529,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
             if self
-                .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign))
+                .lookup_op_method(
+                    fn_sig.output(),
+                    Some(other_ty),
+                    Some(other_expr),
+                    Op::Binary(op, is_assign),
+                )
                 .is_ok()
             {
                 let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
@@ -631,7 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         op: hir::UnOp,
     ) -> Ty<'tcx> {
         assert!(op.is_by_value());
-        match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) {
+        match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) {
             Ok(method) => {
                 self.write_method_call(ex.hir_id, method);
                 method.sig.output()
@@ -705,7 +726,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn lookup_op_method(
         &self,
         lhs_ty: Ty<'tcx>,
-        other_tys: &[Ty<'tcx>],
+        other_ty: Option<Ty<'tcx>>,
+        other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         op: Op,
     ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
         let lang = self.tcx.lang_items();
@@ -791,7 +813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let opname = Ident::with_dummy_span(opname);
         let method = trait_did.and_then(|trait_did| {
-            self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
+            self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr)
         });
 
         match (method, trait_did) {
@@ -803,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             (None, None) => Err(vec![]),
             (None, Some(trait_did)) => {
                 let (obligation, _) =
-                    self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys));
+                    self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr);
                 let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
                 fulfill.register_predicate_obligation(self, obligation);
                 Err(fulfill.select_where_possible(&self.infcx))