Skip to content

Explain origin of implicit Sized obligations and provide suggestions when possible #85947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
15 changes: 6 additions & 9 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1477,15 +1477,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::disallowed(),
),
bounded_ty: this.lower_ty(bounded_ty, ImplTraitContext::disallowed()),
bounds: this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| {
match *bound {
// Ignore `?Trait` bounds.
// They were copied into type parameters already.
GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
_ => Some(
this.lower_param_bound(bound, ImplTraitContext::disallowed()),
),
}
bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| {
// We used to ignore `?Trait` bounds, as they were copied into type
// parameters already, but we need to keep them around only for
// diagnostics when we suggest removal of `?Sized` bounds. See
// `suggest_constraining_type_param`.
this.lower_param_bound(bound, ImplTraitContext::disallowed())
})),
span,
})
Expand Down
23 changes: 20 additions & 3 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
secondary_span: Option<(Span, String)>,
mut values: Option<ValuePairs<'tcx>>,
terr: &TypeError<'tcx>,
swap_secondary_and_primary: bool,
) {
let span = cause.span(self.tcx);
debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr);
Expand Down Expand Up @@ -1582,9 +1583,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
match terr {
TypeError::ObjectUnsafeCoercion(_) => {}
_ => {
diag.span_label(span, terr.to_string());
if let Some((sp, msg)) = secondary_span {
diag.span_label(sp, msg);
if swap_secondary_and_primary {
let terr = if let Some(infer::ValuePairs::Types(infer::ExpectedFound {
expected,
..
})) = values
{
format!("expected this to be `{}`", expected)
} else {
terr.to_string()
};
diag.span_label(sp, terr);
diag.span_label(span, msg);
} else {
diag.span_label(span, terr.to_string());
diag.span_label(sp, msg);
}
} else {
diag.span_label(span, terr.to_string());
}
}
};
Expand Down Expand Up @@ -1971,7 +1988,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
}
};
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false);
diag
}

Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_infer/src/traits/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,10 @@ pub trait TraitEngine<'tcx>: 'tcx {
cause: ObligationCause<'tcx>,
) {
let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) };
let predicate = trait_ref.without_const().to_predicate(infcx.tcx);
self.register_predicate_obligation(
infcx,
Obligation {
cause,
recursion_depth: 0,
param_env,
predicate: trait_ref.without_const().to_predicate(infcx.tcx),
},
Obligation { cause, recursion_depth: 0, param_env, predicate },
);
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl Elaborator<'tcx> {

let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(data, _) => {
ty::PredicateKind::Trait(data, _, _) => {
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
let predicates = cx.tcx.explicit_predicates_of(item.def_id);
for &(predicate, span) in predicates.predicates {
let trait_predicate = match predicate.kind().skip_binder() {
Trait(trait_predicate, _constness) => trait_predicate,
Trait(trait_predicate, _constness, _) => trait_predicate,
_ => continue,
};
let def_id = trait_predicate.trait_ref.def_id;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
let mut has_emitted = false;
for &(predicate, _) in cx.tcx.explicit_item_bounds(def) {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Trait(ref poly_trait_predicate, _) =
if let ty::PredicateKind::Trait(ref poly_trait_predicate, _, _) =
predicate.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ pub enum ObligationCauseCode<'tcx> {
/// Like `ItemObligation`, but with extra detail on the source of the obligation.
BindingObligation(DefId, Span),

/// Like `ItemObligation`, but with extra detail on the source of the obligation.
ImplicitSizedObligation(DefId, Span),

/// A type like `&'a T` is WF only if `T: 'a`.
ReferenceOutlivesReferent(Ty<'tcx>),

Expand Down
39 changes: 37 additions & 2 deletions compiler/rustc_middle/src/ty/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,44 @@ impl AssocItem {
// regions just fine, showing `fn(&MyType)`.
tcx.fn_sig(self.def_id).skip_binder().to_string()
}
ty::AssocKind::Type => format!("type {};", self.ident),
ty::AssocKind::Type => {
let default = match tcx.hir().get_if_local(self.def_id) {
Some(
hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Type(_, Some(ty)),
..
})
| hir::Node::ImplItem(hir::ImplItem {
kind: hir::ImplItemKind::TyAlias(ty),
..
}),
) => {
format!(" = {:?}", ty)
}
_ => String::new(),
};
format!("type {}{};", self.ident, default)
}
Comment on lines +61 to +78
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't have a test. It wasn't meant to be in this commit 🤦

ty::AssocKind::Const => {
format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id))
let default = match tcx.hir().get_if_local(self.def_id) {
Some(
hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Const(_, Some(body_id)),
..
})
| hir::Node::ImplItem(hir::ImplItem {
kind: hir::ImplItemKind::Const(_, body_id),
..
}),
) => match tcx.hir().find(body_id.hir_id) {
Some(value) => {
format!(" = {:?}", value)
}
None => String::new(),
},
_ => String::new(),
};
format!("const {}: {:?}{};", self.ident, tcx.type_of(self.def_id), default)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2097,7 +2097,7 @@ impl<'tcx> TyCtxt<'tcx> {
let generic_predicates = self.super_predicates_of(trait_did);

for (predicate, _) in generic_predicates.predicates {
if let ty::PredicateKind::Trait(data, _) = predicate.kind().skip_binder() {
if let ty::PredicateKind::Trait(data, ..) = predicate.kind().skip_binder() {
if set.insert(data.def_id()) {
stack.push(data.def_id());
}
Expand Down
116 changes: 116 additions & 0 deletions compiler/rustc_middle/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::ty::TyKind::*;
use crate::ty::{InferTy, TyCtxt, TyS};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -130,6 +131,121 @@ pub fn suggest_constraining_type_param(
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));
// See if there's a `?Sized` bound that can be removed to suggest that.
match param.bounds {
[] => {}
bounds => {
// First look at the `where` clause because we can have `where T: ?Sized`, but that
// `?Sized` bound is *also* included in the `GenericParam` as a bound, which breaks
// the spans. Hence the somewhat involved logic that follows.
let mut where_unsized_bounds = FxHashSet::default();
for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
match predicate {
WherePredicate::BoundPredicate(WhereBoundPredicate {
bounded_ty:
hir::Ty {
kind:
hir::TyKind::Path(hir::QPath::Resolved(
None,
hir::Path {
segments: [segment],
res:
hir::def::Res::Def(hir::def::DefKind::TyParam, _),
..
},
)),
..
},
bounds,
span,
..
}) if segment.ident.as_str() == param_name => {
for (pos, bound) in bounds.iter().enumerate() {
match bound {
hir::GenericBound::Trait(
poly,
hir::TraitBoundModifier::Maybe,
) if poly.trait_ref.trait_def_id() == def_id => {
let sp = match (
bounds.len(),
pos,
generics.where_clause.predicates.len(),
where_pos,
) {
// where T: ?Sized
// ^^^^^^^^^^^^^^^
(1, _, 1, _) => generics.where_clause.span,
// where Foo: Bar, T: ?Sized,
// ^^^^^^^^^^^
(1, _, len, pos) if pos == len - 1 => {
generics.where_clause.predicates[pos - 1]
.span()
.shrink_to_hi()
.to(*span)
}
// where T: ?Sized, Foo: Bar,
// ^^^^^^^^^^^
(1, _, _, pos) => span.until(
generics.where_clause.predicates[pos + 1].span(),
),
// where T: ?Sized + Bar, Foo: Bar,
// ^^^^^^^^^
(_, 0, _, _) => {
bound.span().to(bounds[1].span().shrink_to_lo())
}
// where T: Bar + ?Sized, Foo: Bar,
// ^^^^^^^^^
(_, pos, _, _) => bounds[pos - 1]
.span()
.shrink_to_hi()
.to(bound.span()),
};
where_unsized_bounds.insert(bound.span());
err.span_suggestion_verbose(
sp,
"consider removing the `?Sized` bound to make the \
type parameter `Sized`",
String::new(),
Applicability::MaybeIncorrect,
);
}
_ => {}
}
}
}
_ => {}
}
}
for (pos, bound) in bounds.iter().enumerate() {
match bound {
hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
if poly.trait_ref.trait_def_id() == def_id
&& !where_unsized_bounds.contains(&bound.span()) =>
{
let sp = match (bounds.len(), pos) {
// T: ?Sized,
// ^^^^^^^^
(1, _) => param.span.shrink_to_hi().to(bound.span()),
// T: ?Sized + Bar,
// ^^^^^^^^^
(_, 0) => bound.span().to(bounds[1].span().shrink_to_lo()),
// T: Bar + ?Sized,
// ^^^^^^^^^
(_, pos) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
};
err.span_suggestion_verbose(
sp,
"consider removing the `?Sized` bound to make the type parameter \
`Sized`",
String::new(),
Applicability::MaybeIncorrect,
);
}
_ => {}
}
}
}
}
return true;
}
let mut suggest_restrict = |span| {
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,15 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
ArgumentMutability(_) | Mutability => write!(f, "types differ in mutability"),
TupleSize(values) => write!(
f,
"expected a tuple with {} element{}, \
found one with {} element{}",
"expected a tuple with {} element{}, found one with {} element{}",
values.expected,
pluralize!(values.expected),
values.found,
pluralize!(values.found)
),
FixedArraySize(values) => write!(
f,
"expected an array with a fixed size of {} element{}, \
found one with {} element{}",
"expected an array with a fixed size of {} element{}, found one with {} element{}",
values.expected,
pluralize!(values.expected),
values.found,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ impl FlagComputation {

fn add_predicate_atom(&mut self, atom: ty::PredicateKind<'_>) {
match atom {
ty::PredicateKind::Trait(trait_pred, _constness) => {
ty::PredicateKind::Trait(trait_pred, _constness, _) => {
self.add_substs(trait_pred.trait_ref.substs);
}
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
Expand Down
Loading