mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Rollup merge of #70908 - estebank:suggest-add, r=nikomatsakis
Provide suggestions for type parameters missing bounds for associated types When implementing the binary operator traits it is easy to forget to restrict the `Output` associated type. `rustc` now accounts for different cases to lead users in the right direction to add the necessary restrictions. The structured suggestions in the following output are new: ``` error: equality constraints are not yet supported in `where` clauses --> $DIR/missing-bounds.rs:37:33 | LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported | = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information help: if `Output` is an associated type you're trying to set, use the associated type binding syntax | LL | impl<B: Add> Add for E<B> where B: Add<Output = B> { | ^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types --> $DIR/missing-bounds.rs:11:11 | 7 | impl<B> Add for A<B> where B: Add { | - this type parameter ... 11 | A(self.0 + rhs.0) | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type | = note: expected type parameter `B` found associated type `<B as std::ops::Add>::Output` help: consider further restricting this bound | 7 | impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0369]: cannot add `B` to `B` --> $DIR/missing-bounds.rs:31:21 | 31 | Self(self.0 + rhs.0) | ------ ^ ----- B | | | B | help: consider restricting type parameter `B` | 27 | impl<B: std::ops::Add<Output = B>> Add for D<B> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` That output is given for the following cases: ```rust struct A<B>(B); impl<B> Add for A<B> where B: Add { type Output = Self; fn add(self, rhs: Self) -> Self { A(self.0 + rhs.0) //~ ERROR mismatched types } } struct D<B>(B); impl<B> Add for D<B> { type Output = Self; fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` } } struct E<B>(B); impl<B: Add> Add for E<B> where <B as Add>::Output = B { type Output = Self; fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) } } ```
This commit is contained in:
commit
ce14d6db5a
@ -23,6 +23,7 @@ use rustc_session::Session;
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::Span;
|
||||
use std::mem;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
const MORE_EXTERN: &str =
|
||||
"for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
|
||||
@ -1113,17 +1114,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|
||||
for predicate in &generics.where_clause.predicates {
|
||||
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
|
||||
self.err_handler()
|
||||
.struct_span_err(
|
||||
predicate.span,
|
||||
"equality constraints are not yet supported in `where` clauses",
|
||||
)
|
||||
.span_label(predicate.span, "not supported")
|
||||
.note(
|
||||
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \
|
||||
for more information",
|
||||
)
|
||||
.emit();
|
||||
deny_equality_constraints(self, predicate, generics);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1300,6 +1291,89 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
|
||||
/// like it's setting an associated type, provide an appropriate suggestion.
|
||||
fn deny_equality_constraints(
|
||||
this: &mut AstValidator<'_>,
|
||||
predicate: &WhereEqPredicate,
|
||||
generics: &Generics,
|
||||
) {
|
||||
let mut err = this.err_handler().struct_span_err(
|
||||
predicate.span,
|
||||
"equality constraints are not yet supported in `where` clauses",
|
||||
);
|
||||
err.span_label(predicate.span, "not supported");
|
||||
|
||||
// Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
|
||||
if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
|
||||
if let TyKind::Path(None, path) = &qself.ty.kind {
|
||||
match &path.segments[..] {
|
||||
[PathSegment { ident, args: None, .. }] => {
|
||||
for param in &generics.params {
|
||||
if param.ident == *ident {
|
||||
let param = ident;
|
||||
match &full_path.segments[qself.position..] {
|
||||
[PathSegment { ident, .. }] => {
|
||||
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
|
||||
let mut assoc_path = full_path.clone();
|
||||
// Remove `Bar` from `Foo::Bar`.
|
||||
assoc_path.segments.pop();
|
||||
let len = assoc_path.segments.len() - 1;
|
||||
// Build `<Bar = RhsTy>`.
|
||||
let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
|
||||
id: rustc_ast::node_id::DUMMY_NODE_ID,
|
||||
ident: *ident,
|
||||
kind: AssocTyConstraintKind::Equality {
|
||||
ty: predicate.rhs_ty.clone(),
|
||||
},
|
||||
span: ident.span,
|
||||
});
|
||||
// Add `<Bar = RhsTy>` to `Foo`.
|
||||
match &mut assoc_path.segments[len].args {
|
||||
Some(args) => match args.deref_mut() {
|
||||
GenericArgs::Parenthesized(_) => continue,
|
||||
GenericArgs::AngleBracketed(args) => {
|
||||
args.args.push(arg);
|
||||
}
|
||||
},
|
||||
empty_args => {
|
||||
*empty_args = AngleBracketedArgs {
|
||||
span: ident.span,
|
||||
args: vec![arg],
|
||||
}
|
||||
.into();
|
||||
}
|
||||
}
|
||||
err.span_suggestion_verbose(
|
||||
predicate.span,
|
||||
&format!(
|
||||
"if `{}` is an associated type you're trying to set, \
|
||||
use the associated type binding syntax",
|
||||
ident
|
||||
),
|
||||
format!(
|
||||
"{}: {}",
|
||||
param,
|
||||
pprust::path_to_string(&assoc_path)
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
err.note(
|
||||
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
|
||||
let mut validator = AstValidator {
|
||||
session,
|
||||
|
@ -2626,8 +2626,42 @@ impl Node<'_> {
|
||||
match self {
|
||||
Node::TraitItem(TraitItem { generics, .. })
|
||||
| Node::ImplItem(ImplItem { generics, .. })
|
||||
| Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics),
|
||||
| Node::Item(Item {
|
||||
kind:
|
||||
ItemKind::Trait(_, _, generics, ..)
|
||||
| ItemKind::Impl { generics, .. }
|
||||
| ItemKind::Fn(_, generics, _),
|
||||
..
|
||||
}) => Some(generics),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hir_id(&self) -> Option<HirId> {
|
||||
match self {
|
||||
Node::Item(Item { hir_id, .. })
|
||||
| Node::ForeignItem(ForeignItem { hir_id, .. })
|
||||
| Node::TraitItem(TraitItem { hir_id, .. })
|
||||
| Node::ImplItem(ImplItem { hir_id, .. })
|
||||
| Node::Field(StructField { hir_id, .. })
|
||||
| Node::AnonConst(AnonConst { hir_id, .. })
|
||||
| Node::Expr(Expr { hir_id, .. })
|
||||
| Node::Stmt(Stmt { hir_id, .. })
|
||||
| Node::Ty(Ty { hir_id, .. })
|
||||
| Node::Binding(Pat { hir_id, .. })
|
||||
| Node::Pat(Pat { hir_id, .. })
|
||||
| Node::Arm(Arm { hir_id, .. })
|
||||
| Node::Block(Block { hir_id, .. })
|
||||
| Node::Local(Local { hir_id, .. })
|
||||
| Node::MacroDef(MacroDef { hir_id, .. })
|
||||
| Node::Lifetime(Lifetime { hir_id, .. })
|
||||
| Node::Param(Param { hir_id, .. })
|
||||
| Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id),
|
||||
Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id),
|
||||
Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id,
|
||||
Node::Variant(Variant { id, .. }) => Some(*id),
|
||||
Node::Ctor(variant) => variant.ctor_hir_id(),
|
||||
Node::Crate(_) | Node::Visibility(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,12 @@
|
||||
|
||||
use crate::ty::sty::InferTy;
|
||||
use crate::ty::TyKind::*;
|
||||
use crate::ty::TyS;
|
||||
use crate::ty::{TyCtxt, TyS};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
impl<'tcx> TyS<'tcx> {
|
||||
/// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
|
||||
@ -67,3 +72,180 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggest restricting a type param with a new bound.
|
||||
pub fn suggest_constraining_type_param(
|
||||
tcx: TyCtxt<'_>,
|
||||
generics: &hir::Generics<'_>,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
param_name: &str,
|
||||
constraint: &str,
|
||||
def_id: Option<DefId>,
|
||||
) -> bool {
|
||||
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
||||
|
||||
let param = if let Some(param) = param {
|
||||
param
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
|
||||
let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
|
||||
let msg_restrict_type_further =
|
||||
format!("consider further restricting type parameter `{}`", param_name);
|
||||
|
||||
if def_id == tcx.lang_items().sized_trait() {
|
||||
// Type parameters are already `Sized` by default.
|
||||
err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
|
||||
return true;
|
||||
}
|
||||
let mut suggest_restrict = |span| {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
MSG_RESTRICT_BOUND_FURTHER,
|
||||
format!(" + {}", constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
|
||||
if param_name.starts_with("impl ") {
|
||||
// If there's an `impl Trait` used in argument position, suggest
|
||||
// restricting it:
|
||||
//
|
||||
// fn foo(t: impl Foo) { ... }
|
||||
// --------
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion for tools in this case is:
|
||||
//
|
||||
// fn foo(t: impl Foo) { ... }
|
||||
// --------
|
||||
// |
|
||||
// replace with: `impl Foo + Bar`
|
||||
|
||||
suggest_restrict(param.span.shrink_to_hi());
|
||||
return true;
|
||||
}
|
||||
|
||||
if generics.where_clause.predicates.is_empty()
|
||||
// Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
|
||||
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
|
||||
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
|
||||
{
|
||||
if let Some(bounds_span) = param.bounds_span() {
|
||||
// If user has provided some bounds, suggest restricting them:
|
||||
//
|
||||
// fn foo<T: Foo>(t: T) { ... }
|
||||
// ---
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion for tools in this case is:
|
||||
//
|
||||
// fn foo<T: Foo>(t: T) { ... }
|
||||
// --
|
||||
// |
|
||||
// replace with: `T: Bar +`
|
||||
suggest_restrict(bounds_span.shrink_to_hi());
|
||||
} else {
|
||||
// If user hasn't provided any bounds, suggest adding a new one:
|
||||
//
|
||||
// fn foo<T>(t: T) { ... }
|
||||
// - help: consider restricting this type parameter with `T: Foo`
|
||||
err.span_suggestion_verbose(
|
||||
param.span.shrink_to_hi(),
|
||||
&msg_restrict_type,
|
||||
format!(": {}", constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
// This part is a bit tricky, because using the `where` clause user can
|
||||
// provide zero, one or many bounds for the same type parameter, so we
|
||||
// have following cases to consider:
|
||||
//
|
||||
// 1) When the type parameter has been provided zero bounds
|
||||
//
|
||||
// Message:
|
||||
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
||||
// - help: consider restricting this type parameter with `where X: Bar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
||||
// - insert: `, X: Bar`
|
||||
//
|
||||
//
|
||||
// 2) When the type parameter has been provided one bound
|
||||
//
|
||||
// Message:
|
||||
// fn foo<T>(t: T) where T: Foo { ... }
|
||||
// ^^^^^^
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<T>(t: T) where T: Foo { ... }
|
||||
// ^^
|
||||
// |
|
||||
// replace with: `T: Bar +`
|
||||
//
|
||||
//
|
||||
// 3) When the type parameter has been provided many bounds
|
||||
//
|
||||
// Message:
|
||||
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
|
||||
// - help: consider further restricting this type parameter with `where T: Zar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
|
||||
// - insert: `, T: Zar`
|
||||
|
||||
let mut param_spans = Vec::new();
|
||||
|
||||
for predicate in generics.where_clause.predicates {
|
||||
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
|
||||
span, bounded_ty, ..
|
||||
}) = predicate
|
||||
{
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
|
||||
if let Some(segment) = path.segments.first() {
|
||||
if segment.ident.to_string() == param_name {
|
||||
param_spans.push(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
|
||||
// Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
|
||||
let mut trailing_comma = false;
|
||||
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
|
||||
trailing_comma = snippet.ends_with(',');
|
||||
}
|
||||
let where_clause_span = if trailing_comma {
|
||||
let hi = where_clause_span.hi();
|
||||
Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
|
||||
} else {
|
||||
where_clause_span.shrink_to_hi()
|
||||
};
|
||||
|
||||
match ¶m_spans[..] {
|
||||
&[¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
|
||||
_ => {
|
||||
err.span_suggestion_verbose(
|
||||
where_clause_span,
|
||||
&msg_restrict_type_further,
|
||||
format!(", {}: {}", param_name, constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||
use crate::ty::diagnostics::suggest_constraining_type_param;
|
||||
use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt};
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
|
||||
@ -401,8 +402,46 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
(ty::Projection(_), ty::Projection(_)) => {
|
||||
db.note("an associated type was expected, but a different one was found");
|
||||
}
|
||||
(ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => {
|
||||
db.note("you might be missing a type parameter or trait bound");
|
||||
(ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => {
|
||||
let generics = self.generics_of(body_owner_def_id);
|
||||
let p_span = self.def_span(generics.type_param(p, self).def_id);
|
||||
if !sp.contains(p_span) {
|
||||
db.span_label(p_span, "this type parameter");
|
||||
}
|
||||
let hir = self.hir();
|
||||
let mut note = true;
|
||||
if let Some(generics) = generics
|
||||
.type_param(p, self)
|
||||
.def_id
|
||||
.as_local()
|
||||
.map(|id| hir.as_local_hir_id(id))
|
||||
.and_then(|id| self.hir().find(self.hir().get_parent_node(id)))
|
||||
.as_ref()
|
||||
.and_then(|node| node.generics())
|
||||
{
|
||||
// Synthesize the associated type restriction `Add<Output = Expected>`.
|
||||
// FIXME: extract this logic for use in other diagnostics.
|
||||
let trait_ref = proj.trait_ref(self);
|
||||
let path =
|
||||
self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
|
||||
let item_name = self.item_name(proj.item_def_id);
|
||||
let path = if path.ends_with('>') {
|
||||
format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p)
|
||||
} else {
|
||||
format!("{}<{} = {}>", path, item_name, p)
|
||||
};
|
||||
note = !suggest_constraining_type_param(
|
||||
self,
|
||||
generics,
|
||||
db,
|
||||
&format!("{}", proj.self_ty()),
|
||||
&path,
|
||||
None,
|
||||
);
|
||||
}
|
||||
if note {
|
||||
db.note("you might be missing a type parameter or trait bound");
|
||||
}
|
||||
}
|
||||
(ty::Param(p), ty::Dynamic(..) | ty::Opaque(..))
|
||||
| (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => {
|
||||
|
@ -10,10 +10,9 @@ use rustc_middle::mir::{
|
||||
FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
|
||||
ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
|
||||
use rustc_span::source_map::DesugaringKind;
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param;
|
||||
|
||||
use crate::dataflow::drop_flag_effects;
|
||||
use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
|
||||
|
@ -15,17 +15,16 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate};
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::fast_reject;
|
||||
use rustc_middle::ty::fold::TypeFolder;
|
||||
use rustc_middle::ty::SubtypePredicate;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
|
||||
self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
|
||||
TypeFoldable, WithConstness,
|
||||
};
|
||||
use rustc_session::DiagnosticMessageId;
|
||||
use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP};
|
||||
use rustc_span::{ExpnKind, Span, DUMMY_SP};
|
||||
use std::fmt;
|
||||
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
@ -1700,180 +1699,3 @@ impl ArgKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggest restricting a type param with a new bound.
|
||||
pub fn suggest_constraining_type_param(
|
||||
tcx: TyCtxt<'_>,
|
||||
generics: &hir::Generics<'_>,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
param_name: &str,
|
||||
constraint: &str,
|
||||
def_id: Option<DefId>,
|
||||
) -> bool {
|
||||
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
||||
|
||||
let param = if let Some(param) = param {
|
||||
param
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
|
||||
let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
|
||||
let msg_restrict_type_further =
|
||||
format!("consider further restricting type parameter `{}`", param_name);
|
||||
|
||||
if def_id == tcx.lang_items().sized_trait() {
|
||||
// Type parameters are already `Sized` by default.
|
||||
err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
|
||||
return true;
|
||||
}
|
||||
let mut suggest_restrict = |span| {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
MSG_RESTRICT_BOUND_FURTHER,
|
||||
format!(" + {}", constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
|
||||
if param_name.starts_with("impl ") {
|
||||
// If there's an `impl Trait` used in argument position, suggest
|
||||
// restricting it:
|
||||
//
|
||||
// fn foo(t: impl Foo) { ... }
|
||||
// --------
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion for tools in this case is:
|
||||
//
|
||||
// fn foo(t: impl Foo) { ... }
|
||||
// --------
|
||||
// |
|
||||
// replace with: `impl Foo + Bar`
|
||||
|
||||
suggest_restrict(param.span.shrink_to_hi());
|
||||
return true;
|
||||
}
|
||||
|
||||
if generics.where_clause.predicates.is_empty()
|
||||
// Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
|
||||
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
|
||||
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
|
||||
{
|
||||
if let Some(bounds_span) = param.bounds_span() {
|
||||
// If user has provided some bounds, suggest restricting them:
|
||||
//
|
||||
// fn foo<T: Foo>(t: T) { ... }
|
||||
// ---
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion for tools in this case is:
|
||||
//
|
||||
// fn foo<T: Foo>(t: T) { ... }
|
||||
// --
|
||||
// |
|
||||
// replace with: `T: Bar +`
|
||||
suggest_restrict(bounds_span.shrink_to_hi());
|
||||
} else {
|
||||
// If user hasn't provided any bounds, suggest adding a new one:
|
||||
//
|
||||
// fn foo<T>(t: T) { ... }
|
||||
// - help: consider restricting this type parameter with `T: Foo`
|
||||
err.span_suggestion_verbose(
|
||||
param.span.shrink_to_hi(),
|
||||
&msg_restrict_type,
|
||||
format!(": {}", constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
// This part is a bit tricky, because using the `where` clause user can
|
||||
// provide zero, one or many bounds for the same type parameter, so we
|
||||
// have following cases to consider:
|
||||
//
|
||||
// 1) When the type parameter has been provided zero bounds
|
||||
//
|
||||
// Message:
|
||||
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
||||
// - help: consider restricting this type parameter with `where X: Bar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
||||
// - insert: `, X: Bar`
|
||||
//
|
||||
//
|
||||
// 2) When the type parameter has been provided one bound
|
||||
//
|
||||
// Message:
|
||||
// fn foo<T>(t: T) where T: Foo { ... }
|
||||
// ^^^^^^
|
||||
// |
|
||||
// help: consider further restricting this bound with `+ Bar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<T>(t: T) where T: Foo { ... }
|
||||
// ^^
|
||||
// |
|
||||
// replace with: `T: Bar +`
|
||||
//
|
||||
//
|
||||
// 3) When the type parameter has been provided many bounds
|
||||
//
|
||||
// Message:
|
||||
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
|
||||
// - help: consider further restricting this type parameter with `where T: Zar`
|
||||
//
|
||||
// Suggestion:
|
||||
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
|
||||
// - insert: `, T: Zar`
|
||||
|
||||
let mut param_spans = Vec::new();
|
||||
|
||||
for predicate in generics.where_clause.predicates {
|
||||
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
|
||||
span, bounded_ty, ..
|
||||
}) = predicate
|
||||
{
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
|
||||
if let Some(segment) = path.segments.first() {
|
||||
if segment.ident.to_string() == param_name {
|
||||
param_spans.push(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
|
||||
// Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
|
||||
let mut trailing_comma = false;
|
||||
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
|
||||
trailing_comma = snippet.ends_with(',');
|
||||
}
|
||||
let where_clause_span = if trailing_comma {
|
||||
let hi = where_clause_span.hi();
|
||||
Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
|
||||
} else {
|
||||
where_clause_span.shrink_to_hi()
|
||||
};
|
||||
|
||||
match ¶m_spans[..] {
|
||||
&[¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
|
||||
_ => {
|
||||
err.span_suggestion_verbose(
|
||||
where_clause_span,
|
||||
&msg_restrict_type_further,
|
||||
format!(", {}: {}", param_name, constraint),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ use super::{
|
||||
};
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::error_reporting::suggest_constraining_type_param;
|
||||
|
||||
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
|
||||
use rustc_hir as hir;
|
||||
@ -13,7 +12,8 @@ use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
|
||||
use rustc_middle::ty::TypeckTables;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
|
||||
self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty,
|
||||
TyCtxt, TypeFoldable, WithConstness,
|
||||
};
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use rustc_span::{MultiSpan, Span, DUMMY_SP};
|
||||
|
@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
|
||||
use rustc_middle::ty::{self, Ty, TypeFoldable};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
|
||||
@ -253,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// error types are considered "builtin"
|
||||
if !lhs_ty.references_error() && !rhs_ty.references_error() {
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
|
||||
match is_assign {
|
||||
IsAssign::Yes => {
|
||||
let mut err = struct_span_err!(
|
||||
@ -317,12 +318,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// This has nothing here because it means we did string
|
||||
// concatenation (e.g., "Hello " += "World!"). This means
|
||||
// we don't want the note in the else clause to be emitted
|
||||
} else if let ty::Param(_) = lhs_ty.kind {
|
||||
// FIXME: point to span of param
|
||||
err.note(&format!(
|
||||
"`{}` might need a bound for `{}`",
|
||||
lhs_ty, missing_trait
|
||||
));
|
||||
} else if let ty::Param(p) = lhs_ty.kind {
|
||||
suggest_constraining_param(
|
||||
self.tcx,
|
||||
self.body_id,
|
||||
&mut err,
|
||||
lhs_ty,
|
||||
rhs_ty,
|
||||
missing_trait,
|
||||
p,
|
||||
false,
|
||||
);
|
||||
} else if !suggested_deref {
|
||||
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
||||
}
|
||||
@ -330,46 +336,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.emit();
|
||||
}
|
||||
IsAssign::No => {
|
||||
let (message, missing_trait) = match op.node {
|
||||
let (message, missing_trait, use_output) = match op.node {
|
||||
hir::BinOpKind::Add => (
|
||||
format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty),
|
||||
Some("std::ops::Add"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Sub => (
|
||||
format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
|
||||
Some("std::ops::Sub"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Mul => (
|
||||
format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty),
|
||||
Some("std::ops::Mul"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Div => (
|
||||
format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::Div"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Rem => (
|
||||
format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::Rem"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::BitAnd => (
|
||||
format!("no implementation for `{} & {}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::BitAnd"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::BitXor => (
|
||||
format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::BitXor"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::BitOr => (
|
||||
format!("no implementation for `{} | {}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::BitOr"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Shl => (
|
||||
format!("no implementation for `{} << {}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::Shl"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Shr => (
|
||||
format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty),
|
||||
Some("std::ops::Shr"),
|
||||
true,
|
||||
),
|
||||
hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
|
||||
format!(
|
||||
@ -378,6 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
lhs_ty
|
||||
),
|
||||
Some("std::cmp::PartialEq"),
|
||||
false,
|
||||
),
|
||||
hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
@ -389,6 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
lhs_ty
|
||||
),
|
||||
Some("std::cmp::PartialOrd"),
|
||||
false,
|
||||
),
|
||||
_ => (
|
||||
format!(
|
||||
@ -397,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
lhs_ty
|
||||
),
|
||||
None,
|
||||
false,
|
||||
),
|
||||
};
|
||||
let mut err = struct_span_err!(
|
||||
@ -459,12 +478,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// This has nothing here because it means we did string
|
||||
// concatenation (e.g., "Hello " + "World!"). This means
|
||||
// we don't want the note in the else clause to be emitted
|
||||
} else if let ty::Param(_) = lhs_ty.kind {
|
||||
// FIXME: point to span of param
|
||||
err.note(&format!(
|
||||
"`{}` might need a bound for `{}`",
|
||||
lhs_ty, missing_trait
|
||||
));
|
||||
} else if let ty::Param(p) = lhs_ty.kind {
|
||||
suggest_constraining_param(
|
||||
self.tcx,
|
||||
self.body_id,
|
||||
&mut err,
|
||||
lhs_ty,
|
||||
rhs_ty,
|
||||
missing_trait,
|
||||
p,
|
||||
use_output,
|
||||
);
|
||||
} else if !suggested_deref && !involves_fn {
|
||||
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
||||
}
|
||||
@ -911,3 +935,43 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_constraining_param(
|
||||
tcx: TyCtxt<'_>,
|
||||
body_id: hir::HirId,
|
||||
mut err: &mut DiagnosticBuilder<'_>,
|
||||
lhs_ty: Ty<'_>,
|
||||
rhs_ty: Ty<'_>,
|
||||
missing_trait: &str,
|
||||
p: ty::ParamTy,
|
||||
set_output: bool,
|
||||
) {
|
||||
let hir = tcx.hir();
|
||||
let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait);
|
||||
// Try to find the def-id and details for the parameter p. We have only the index,
|
||||
// so we have to find the enclosing function's def-id, then look through its declared
|
||||
// generic parameters to get the declaration.
|
||||
let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
|
||||
let generics = tcx.generics_of(def_id);
|
||||
let param_def_id = generics.type_param(&p, tcx).def_id;
|
||||
if let Some(generics) = param_def_id
|
||||
.as_local()
|
||||
.map(|id| hir.as_local_hir_id(id))
|
||||
.and_then(|id| hir.find(hir.get_parent_item(id)))
|
||||
.as_ref()
|
||||
.and_then(|node| node.generics())
|
||||
{
|
||||
let output = if set_output { format!("<Output = {}>", rhs_ty) } else { String::new() };
|
||||
suggest_constraining_type_param(
|
||||
tcx,
|
||||
generics,
|
||||
&mut err,
|
||||
&format!("{}", lhs_ty),
|
||||
&format!("{}{}", missing_trait, output),
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
let span = tcx.def_span(param_def_id);
|
||||
err.span_label(span, msg);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,16 @@ error[E0271]: type mismatch resolving `for<'a> <<T as Baz>::Baa<'a> as std::ops:
|
||||
--> $DIR/construct_with_other_type.rs:19:9
|
||||
|
|
||||
LL | impl<T> Baz for T where T: Foo {
|
||||
| ^^^ expected type parameter `T`, found associated type
|
||||
| - ^^^ expected type parameter `T`, found associated type
|
||||
| |
|
||||
| this type parameter
|
||||
|
|
||||
= note: expected associated type `<T as Foo>::Bar<'_, 'static>`
|
||||
found associated type `<<T as Baz>::Quux<'_> as Foo>::Bar<'_, 'static>`
|
||||
= note: you might be missing a type parameter or trait bound
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | impl<T> Baz for T where T: Foo + Baz<Quux = T> {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
46
src/test/ui/generic-associated-types/missing-bounds.fixed
Normal file
46
src/test/ui/generic-associated-types/missing-bounds.fixed
Normal file
@ -0,0 +1,46 @@
|
||||
// run-rustfix
|
||||
|
||||
use std::ops::Add;
|
||||
|
||||
struct A<B>(B);
|
||||
|
||||
impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
A(self.0 + rhs.0) //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
struct C<B>(B);
|
||||
|
||||
impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0) //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
struct D<B>(B);
|
||||
|
||||
impl<B: std::ops::Add<Output = B>> Add for D<B> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
|
||||
}
|
||||
}
|
||||
|
||||
struct E<B>(B);
|
||||
|
||||
impl<B: Add> Add for E<B> where B: Add<Output = B>, B: std::ops::Add<Output = B> {
|
||||
//~^ 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() {}
|
46
src/test/ui/generic-associated-types/missing-bounds.rs
Normal file
46
src/test/ui/generic-associated-types/missing-bounds.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// run-rustfix
|
||||
|
||||
use std::ops::Add;
|
||||
|
||||
struct A<B>(B);
|
||||
|
||||
impl<B> Add for A<B> where B: Add {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
A(self.0 + rhs.0) //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
struct C<B>(B);
|
||||
|
||||
impl<B: Add> Add for C<B> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0) //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
struct D<B>(B);
|
||||
|
||||
impl<B> Add for D<B> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
|
||||
}
|
||||
}
|
||||
|
||||
struct E<B>(B);
|
||||
|
||||
impl<B: Add> Add for E<B> where <B as Add>::Output = B {
|
||||
//~^ 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() {}
|
77
src/test/ui/generic-associated-types/missing-bounds.stderr
Normal file
77
src/test/ui/generic-associated-types/missing-bounds.stderr
Normal file
@ -0,0 +1,77 @@
|
||||
error: equality constraints are not yet supported in `where` clauses
|
||||
--> $DIR/missing-bounds.rs:37:33
|
||||
|
|
||||
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
|
||||
|
|
||||
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
|
||||
help: if `Output` is an associated type you're trying to set, use the associated type binding syntax
|
||||
|
|
||||
LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-bounds.rs:11:11
|
||||
|
|
||||
LL | impl<B> Add for A<B> where B: Add {
|
||||
| - this type parameter
|
||||
...
|
||||
LL | A(self.0 + rhs.0)
|
||||
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
||||
|
|
||||
= note: expected type parameter `B`
|
||||
found associated type `<B as std::ops::Add>::Output`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-bounds.rs:21:14
|
||||
|
|
||||
LL | impl<B: Add> Add for C<B> {
|
||||
| - this type parameter
|
||||
...
|
||||
LL | Self(self.0 + rhs.0)
|
||||
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
||||
|
|
||||
= note: expected type parameter `B`
|
||||
found associated type `<B as std::ops::Add>::Output`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0369]: cannot add `B` to `B`
|
||||
--> $DIR/missing-bounds.rs:31:21
|
||||
|
|
||||
LL | Self(self.0 + rhs.0)
|
||||
| ------ ^ ----- B
|
||||
| |
|
||||
| B
|
||||
|
|
||||
help: consider restricting type parameter `B`
|
||||
|
|
||||
LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-bounds.rs:42:14
|
||||
|
|
||||
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
|
||||
| - this type parameter
|
||||
...
|
||||
LL | Self(self.0 + rhs.0)
|
||||
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
||||
|
|
||||
= note: expected type parameter `B`
|
||||
found associated type `<B as std::ops::Add>::Output`
|
||||
help: consider further restricting type parameter `B`
|
||||
|
|
||||
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: std::ops::Add<Output = B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0308, E0369.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
@ -5,12 +5,18 @@ LL | trait From<Src> {
|
||||
| --- required by this bound in `From`
|
||||
...
|
||||
LL | ) -> <Dst as From<Self>>::Result where Dst: From<Self> {
|
||||
| ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized`
|
||||
| |
|
||||
| doesn't have a size known at compile-time
|
||||
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
||||
|
|
||||
= help: the trait `std::marker::Sized` is not implemented for `Self`
|
||||
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
|
||||
help: consider further restricting `Self`
|
||||
|
|
||||
LL | ) -> <Dst as From<Self>>::Result where Dst: From<Self>, Self: std::marker::Sized {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider relaxing the implicit `Sized` restriction
|
||||
|
|
||||
LL | trait From<Src: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -7,7 +7,9 @@ LL | type A: MultiDispatch<Self::B, O = Self>;
|
||||
| -------- required by this bound in `Trait`
|
||||
...
|
||||
LL | fn test<T: Trait<B=i32>>(b: i32) -> T where T::A: MultiDispatch<i32> { T::new(b) }
|
||||
| ^^^^^^^^^^^^ expected type parameter `T`, found associated type
|
||||
| - ^^^^^^^^^^^^ expected type parameter `T`, found associated type
|
||||
| |
|
||||
| this type parameter
|
||||
|
|
||||
= note: expected type parameter `T`
|
||||
found associated type `<<T as Trait>::A as MultiDispatch<i32>>::O`
|
||||
|
@ -6,7 +6,10 @@ LL | self.x += v.x;
|
||||
| |
|
||||
| cannot use `+=` on type `T`
|
||||
|
|
||||
= note: `T` might need a bound for `std::ops::AddAssign`
|
||||
help: consider restricting type parameter `T`
|
||||
|
|
||||
LL | impl<T: std::ops::AddAssign> Foo<T> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -6,7 +6,10 @@ LL | let z = x + y;
|
||||
| |
|
||||
| T
|
||||
|
|
||||
= note: `T` might need a bound for `std::ops::Add`
|
||||
help: consider restricting type parameter `T`
|
||||
|
|
||||
LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
|
||||
--> $DIR/missing_trait_impl.rs:9:5
|
||||
@ -16,7 +19,10 @@ LL | x += x;
|
||||
| |
|
||||
| cannot use `+=` on type `T`
|
||||
|
|
||||
= note: `T` might need a bound for `std::ops::AddAssign`
|
||||
help: consider restricting type parameter `T`
|
||||
|
|
||||
LL | fn bar<T: std::ops::AddAssign>(x: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user