From e5bb7d80d698102175dbbb4094bec03b682e8399 Mon Sep 17 00:00:00 2001 From: Will Crichton Date: Tue, 26 Apr 2022 13:59:08 -0700 Subject: [PATCH] Propagate Expectation around binop typeck code to construct more precise trait obligations for binops. --- compiler/rustc_middle/src/traits/mod.rs | 3 +- compiler/rustc_middle/src/ty/mod.rs | 18 ++++ .../src/traits/error_reporting/mod.rs | 1 + .../src/traits/error_reporting/suggestions.rs | 16 +++- compiler/rustc_typeck/src/check/expr.rs | 10 ++- .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 1 + compiler/rustc_typeck/src/check/method/mod.rs | 30 ++++++- compiler/rustc_typeck/src/check/op.rs | 85 ++++++++++++++----- .../missing-bounds.fixed | 46 ++++++++++ .../missing-bounds.rs | 2 + .../missing-bounds.stderr | 20 ++--- src/test/ui/suggestions/issue-97677.stderr | 4 +- 12 files changed, 194 insertions(+), 42 deletions(-) create mode 100644 src/test/ui/generic-associated-types/missing-bounds.fixed diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d8cc7d3feb0..945cdfbb0e9 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -12,7 +12,7 @@ pub mod util; use crate::infer::canonical::Canonical; use crate::ty::abstract_const::NotConstEvaluatable; use crate::ty::subst::SubstsRef; -use crate::ty::{self, AdtKind, Ty, TyCtxt}; +use crate::ty::{self, AdtKind, Predicate, Ty, TyCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -414,6 +414,7 @@ pub enum ObligationCauseCode<'tcx> { BinOp { rhs_span: Option, is_lit: bool, + output_pred: Option>, }, } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f0acb02933a..3536d946db2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1033,6 +1033,24 @@ impl<'tcx> Predicate<'tcx> { } } + pub fn to_opt_poly_projection_pred(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Projection(t) => Some(predicate.rebind(t)), + PredicateKind::Trait(..) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::TypeOutlives(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) + | PredicateKind::TypeWellFormedFromEnv(..) => None, + } + } + pub fn to_opt_type_outlives(self) -> Option> { let predicate = self.kind(); match predicate.skip_binder() { 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 39fce3cf769..c1af1a05972 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -665,6 +665,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.suggest_restricting_param_bound( &mut err, trait_predicate, + None, obligation.cause.body_id, ); } else if !suggested { 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 57d5e5436a6..469436ff0db 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -24,7 +24,8 @@ use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable, - ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_session::Limit; @@ -172,6 +173,7 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, + proj_pred: Option>, body_id: hir::HirId, ); @@ -457,6 +459,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, mut err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, + proj_pred: Option>, body_id: hir::HirId, ) { let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); @@ -589,9 +592,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } // Missing generic type parameter bound. let param_name = self_ty.to_string(); - let constraint = with_no_trimmed_paths!( + let mut constraint = with_no_trimmed_paths!( trait_pred.print_modifiers_and_trait_path().to_string() ); + + if let Some(proj_pred) = proj_pred { + let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder(); + let item = self.tcx.associated_item(projection_ty.item_def_id); + constraint.push_str(&format!("<{}={}>", item.name, term.to_string())); + } + if suggest_constraining_type_param( self.tcx, generics, @@ -2825,7 +2835,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { - ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span, + ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span, _ => return, }; match ( diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2400211394e..b52cb8e99d1 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -282,11 +282,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expr.kind { ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected), ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs), + ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), ExprKind::Assign(lhs, rhs, span) => { self.check_expr_assign(expr, expected, lhs, rhs, span) } - ExprKind::AssignOp(op, lhs, rhs) => self.check_binop_assign(expr, op, lhs, rhs), + ExprKind::AssignOp(op, lhs, rhs) => { + self.check_binop_assign(expr, op, lhs, rhs, expected) + } ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) @@ -404,14 +406,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::UnOp::Not => { - let result = self.check_user_unop(expr, oprnd_t, unop); + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); // If it's builtin, we can reuse the type, this helps inference. if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { oprnd_t = result; } } hir::UnOp::Neg => { - let result = self.check_user_unop(expr, oprnd_t, unop); + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); // If it's builtin, we can reuse the type, this helps inference. if !oprnd_t.is_numeric() { oprnd_t = result; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 42a9a23559c..d33b5b21403 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -413,6 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), + output_pred: None, }, ), self.param_env, diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index c0b3a23fde4..c09f63f1e8f 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -10,7 +10,7 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; -use crate::check::FnCtxt; +use crate::check::{Expectation, FnCtxt}; use crate::ObligationCause; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -20,8 +20,10 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; -use rustc_middle::ty::{DefIdTree, GenericParamDefKind}; +use rustc_middle::ty::{ + self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term, + ToPredicate, Ty, TypeVisitable, +}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; @@ -318,6 +320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_type: Option>, opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List>) { // Construct a trait-reference `self_ty : Trait` @@ -339,6 +342,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); + let opt_output_ty = + expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); + let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind( + self.tcx, + Ident::from_str("Output"), + AssocKind::Type, + trait_def_id, + ); + let output_pred = + opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| { + ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate { + projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id }, + term: Term::Ty(output_ty), + })) + .to_predicate(self.tcx) + }); + ( traits::Obligation::new( traits::ObligationCause::new( @@ -348,6 +368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred, }, ), self.param_env, @@ -397,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_type: Option>, opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, ) -> Option>> { let (obligation, substs) = self.obligation_for_op_method( span, @@ -404,6 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, opt_input_type, opt_input_expr, + expected, ); self.construct_obligation_for_trait( span, @@ -505,6 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred: None, }, ) } else { diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 0887c27ea36..d4bb3d43eff 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -2,10 +2,12 @@ use super::method::MethodCallee; use super::{has_expected_num_generic_args, FnCtxt}; +use crate::check::Expectation; use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; @@ -30,9 +32,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::BinOp, lhs: &'tcx hir::Expr<'tcx>, rhs: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes); + self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { @@ -50,6 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs), Op::Binary(op, IsAssign::Yes), + expected, ) .is_ok() { @@ -70,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::BinOp, lhs_expr: &'tcx hir::Expr<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -94,8 +99,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No); + let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( + expr, + lhs_expr, + rhs_expr, + op, + IsAssign::No, + expected, + ); // Supply type inference hints if relevant. Probably these // hints should be enforced during select as part of the @@ -176,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, + expected: Expectation<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { debug!( "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", @@ -222,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty_var), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ); // see `NB` above @@ -282,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), Err(errors) => { let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, _use_output) = match is_assign { + let (mut err, missing_trait, use_output) = match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( self.tcx.sess, @@ -406,6 +419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr, op, is_assign, + expected, ); self.add_type_neq_err_label( &mut err, @@ -415,6 +429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr, op, is_assign, + expected, ); } self.note_unmet_impls_on_type(&mut err, errors); @@ -429,6 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ) .is_ok() { @@ -490,19 +506,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ) .unwrap_err(); - let predicates = errors - .into_iter() - .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred()) - .collect::>(); - if !predicates.is_empty() { - for pred in predicates { - self.infcx.suggest_restricting_param_bound( - &mut err, - pred, - self.body_id, - ); + if !errors.is_empty() { + for error in errors { + if let Some(trait_pred) = + error.obligation.predicate.to_opt_poly_trait_pred() + { + let proj_pred = match error.obligation.cause.code() { + ObligationCauseCode::BinOp { + output_pred: Some(output_pred), + .. + } if use_output => { + output_pred.to_opt_poly_projection_pred() + } + _ => None, + }; + + self.infcx.suggest_restricting_param_bound( + &mut err, + trait_pred, + proj_pred, + self.body_id, + ); + } } } else if *ty != lhs_ty { // When we know that a missing bound is responsible, we don't show @@ -532,6 +560,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, + expected: Expectation<'tcx>, ) -> bool /* did we suggest to call a function because of missing parentheses? */ { err.span_label(span, ty.to_string()); if let FnDef(def_id, _) = *ty.kind() { @@ -561,6 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(other_ty), Some(other_expr), Op::Binary(op, is_assign), + expected, ) .is_ok() { @@ -677,9 +707,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex: &'tcx hir::Expr<'tcx>, operand_ty: Ty<'tcx>, op: hir::UnOp, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) { + match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { Ok(method) => { self.write_method_call(ex.hir_id, method); method.sig.output() @@ -712,6 +743,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.infcx.suggest_restricting_param_bound( &mut err, pred, + None, self.body_id, ); } @@ -772,6 +804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_ty: Option>, other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, op: Op, + expected: Expectation<'tcx>, ) -> Result, Vec>> { let lang = self.tcx.lang_items(); @@ -856,7 +889,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opname = Ident::with_dummy_span(opname); let method = trait_did.and_then(|trait_did| { - self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr) + self.lookup_op_method_in_trait( + span, + opname, + trait_did, + lhs_ty, + other_ty, + other_ty_expr, + expected, + ) }); match (method, trait_did) { @@ -867,8 +908,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (None, None) => Err(vec![]), (None, Some(trait_did)) => { - let (obligation, _) = - self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr); + let (obligation, _) = self.obligation_for_op_method( + span, + trait_did, + lhs_ty, + other_ty, + other_ty_expr, + expected, + ); let mut fulfill = >::new(self.tcx); fulfill.register_predicate_obligation(self, obligation); Err(fulfill.select_where_possible(&self.infcx)) diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed new file mode 100644 index 00000000000..2315810a47a --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -0,0 +1,46 @@ +// run-rustfix + +use std::ops::Add; + +struct A(B); + +impl Add for A where B: Add + Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + A(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct C(B); + +impl> Add for C { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct D(B); + +impl> Add for D { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` + } +} + +struct E(B); + +impl> Add for E where B: Add { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs index b3661ba3744..ffafff5e9f5 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.rs +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -1,3 +1,5 @@ +// run-rustfix + use std::ops::Add; struct A(B); diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr index 5323ee17226..138c642dd79 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.stderr +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -1,5 +1,5 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/missing-bounds.rs:35:33 + --> $DIR/missing-bounds.rs:37:33 | LL | impl Add for E where ::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported @@ -11,7 +11,7 @@ LL | impl Add for E where B: Add { | ~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:9:11 + --> $DIR/missing-bounds.rs:11:11 | LL | impl Add for A where B: Add { | - this type parameter @@ -24,7 +24,7 @@ LL | A(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:3:8 + --> $DIR/missing-bounds.rs:5:8 | LL | struct A(B); | ^ @@ -34,7 +34,7 @@ LL | impl Add for A where B: Add + Add { | +++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:19:14 + --> $DIR/missing-bounds.rs:21:14 | LL | impl Add for C { | - this type parameter @@ -47,7 +47,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:13:8 + --> $DIR/missing-bounds.rs:15:8 | LL | struct C(B); | ^ @@ -57,7 +57,7 @@ LL | impl> Add for C { | +++++++++++++++++ error[E0369]: cannot add `B` to `B` - --> $DIR/missing-bounds.rs:29:21 + --> $DIR/missing-bounds.rs:31:21 | LL | Self(self.0 + rhs.0) | ------ ^ ----- B @@ -66,11 +66,11 @@ LL | Self(self.0 + rhs.0) | help: consider restricting type parameter `B` | -LL | impl Add for D { - | +++++++++++++++ +LL | impl> Add for D { + | +++++++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:40:14 + --> $DIR/missing-bounds.rs:42:14 | LL | impl Add for E where ::Output = B { | - this type parameter @@ -83,7 +83,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:33:8 + --> $DIR/missing-bounds.rs:35:8 | LL | struct E(B); | ^ diff --git a/src/test/ui/suggestions/issue-97677.stderr b/src/test/ui/suggestions/issue-97677.stderr index ea563ea844d..cad6f858df1 100644 --- a/src/test/ui/suggestions/issue-97677.stderr +++ b/src/test/ui/suggestions/issue-97677.stderr @@ -8,8 +8,8 @@ LL | n + 10 | help: consider restricting type parameter `N` | -LL | fn add_ten>(n: N) -> N { - | ++++++++++++++++++++ +LL | fn add_ten>(n: N) -> N { + | ++++++++++++++++++++++++++++++ error: aborting due to previous error