Skip to content

Commit 32beed4

Browse files
committed
Auto merge of rust-lang#122553 - lukas-code:sized-thin, r=<try>
make `Thin` a supertrait of `Sized` This is a follow-up to rust-lang#120354 to address rust-lang#120354 (comment) and remove the "fallback impl" hack from `<T as Pointee>::Metadata` normalization in both trait solvers. We make `Thin` and therefore `Pointee<Metadata = ()>` a supertrait of `Sized`, such that every (implicit or explicit) `T: Sized` bound now also requires `T: Thin` and `<T as Pointee>::Metadata == ()`. These new bounds will be used when normalizing `<T as Pointee>::Metadata` instead of a hacky builtin impl that only applies for type parameters and aliases and overlaps with other builtin impls. Note that [RFC 2580](https://rust-lang.github.io/rfcs/2580-ptr-meta.html), which introduced `Pointee` and `Thin`, lists these unresolved questions: > Should `Thin` be added as a supertrait of `Sized`? Or could it ever make sense to have fat pointers to statically-sized types? For now, they are answered with yes and no respectively. If we end up deciding that we do want types that are `Sized + !Thin`, then a possible alternative is to add make `Thin` a defaulted trait bound that can be relaxed with `T: ?Thin` and remove `Thin` as supertrait from `Sized` before the stabilization of `feature(ptr_metadata)`. --- The removal of the "fallback impl" also allows us to always project to the shallow struct tail in the old solver, making the implementation in the old solver almost identical to the new solver. This is required to make `<Wrapper<T> as Pointee>::Metadata` work correctly in the presence of trivial bounds, for example if we know `[T]: Thin`, then we should also know `Wrapper<T>: Thin`. Lastly, this PR implements some diagnostics changes to hide errors about `` type mismatch resolving `<T as Pointee>::Metadata == ()` `` when the actual error comes from `T: Sized`. This is a continuation of rust-lang#121863. r? `@lcnr`
2 parents 8579a18 + 81a8fae commit 32beed4

22 files changed

+495
-236
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -589,11 +589,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
589589
&self,
590590
mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
591591
) {
592-
let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
593-
if !result.is_empty() {
594-
mutate_fulfillment_errors(&mut result);
595-
self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
596-
self.err_ctxt().report_fulfillment_errors(result);
592+
let mut errors = self.fulfillment_cx.borrow_mut().select_where_possible(self);
593+
if !errors.is_empty() {
594+
mutate_fulfillment_errors(&mut errors);
595+
if errors.is_empty() {
596+
// We sometimes skip reporting fulfillment errors while constructing
597+
// a different error.
598+
self.dcx().span_delayed_bug(
599+
self.tcx.def_span(self.body_id),
600+
"skipped reporting fulfillment errors",
601+
);
602+
return;
603+
}
604+
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
605+
self.err_ctxt().report_fulfillment_errors(errors);
597606
}
598607
}
599608

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+4-16
Original file line numberDiff line numberDiff line change
@@ -541,22 +541,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
541541
.instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
542542
}
543543

544-
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
545-
// This is the "fallback impl" for type parameters, unnormalizable projections
546-
// and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
547-
// FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
548-
// exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
549-
let sized_predicate = ty::TraitRef::from_lang_item(
550-
tcx,
551-
LangItem::Sized,
552-
DUMMY_SP,
553-
[ty::GenericArg::from(goal.predicate.self_ty())],
554-
);
555-
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
556-
ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate));
557-
tcx.types.unit
558-
}
559-
560544
ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
561545
None => tcx.types.unit,
562546
Some(tail_def) => {
@@ -571,6 +555,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
571555
Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
572556
},
573557

558+
// The metadata of these types can only be known from param env or alias bound
559+
// candidates.
560+
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => return Err(NoSolution),
561+
574562
ty::Infer(
575563
ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
576564
)

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+114-67
Original file line numberDiff line numberDiff line change
@@ -80,61 +80,81 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
8080

8181
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
8282
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
83+
#[instrument(skip(self), level = "debug")]
8384
fn report_fulfillment_errors(
8485
&self,
8586
mut errors: Vec<FulfillmentError<'tcx>>,
8687
) -> ErrorGuaranteed {
88+
if errors.is_empty() {
89+
bug!("attempted to report fulfillment errors, but there we no errors");
90+
}
91+
8792
self.sub_relations
8893
.borrow_mut()
8994
.add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
9095

96+
let mut reported = None;
97+
98+
// We want to ignore desugarings when filtering errors: spans are equivalent even
99+
// if one is the result of a desugaring and the other is not.
100+
let strip_desugaring = |span: Span| {
101+
let expn_data = span.ctxt().outer_expn_data();
102+
if let ExpnKind::Desugaring(_) = expn_data.kind { expn_data.call_site } else { span }
103+
};
104+
91105
#[derive(Debug)]
92-
struct ErrorDescriptor<'tcx> {
106+
struct ErrorDescriptor<'tcx, 'err> {
93107
predicate: ty::Predicate<'tcx>,
94-
index: Option<usize>, // None if this is an old error
108+
source: Option<(usize, &'err FulfillmentError<'tcx>)>, // None if this is an old error
95109
}
96110

97111
let mut error_map: FxIndexMap<_, Vec<_>> = self
98112
.reported_trait_errors
99113
.borrow()
100114
.iter()
101-
.map(|(&span, predicates)| {
102-
(
103-
span,
104-
predicates
105-
.0
106-
.iter()
107-
.map(|&predicate| ErrorDescriptor { predicate, index: None })
108-
.collect(),
109-
)
115+
.map(|(&span, &(ref predicates, guar))| {
116+
reported = Some(guar);
117+
let span = strip_desugaring(span);
118+
let reported_errors = predicates
119+
.iter()
120+
.map(|&predicate| ErrorDescriptor { predicate, source: None })
121+
.collect();
122+
(span, reported_errors)
110123
})
111124
.collect();
112125

126+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127+
enum ErrorOrd {
128+
Default,
129+
Sized,
130+
Metadata,
131+
Coerce,
132+
WellFormed,
133+
}
134+
113135
// Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
114-
// with more relevant type information and hide redundant E0282 errors.
136+
// with more relevant type information and hide redundant E0282 ("type annotations needed") errors.
115137
errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
116138
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
117139
if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() =>
118140
{
119-
1
141+
ErrorOrd::Sized
142+
}
143+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred))
144+
if Some(pred.def_id()) == self.tcx.lang_items().metadata_type() =>
145+
{
146+
ErrorOrd::Metadata
120147
}
121-
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
122-
ty::PredicateKind::Coerce(_) => 2,
123-
_ => 0,
148+
ty::PredicateKind::Coerce(_) => ErrorOrd::Coerce,
149+
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => ErrorOrd::WellFormed,
150+
_ => ErrorOrd::Default,
124151
});
125152

126153
for (index, error) in errors.iter().enumerate() {
127-
// We want to ignore desugarings here: spans are equivalent even
128-
// if one is the result of a desugaring and the other is not.
129-
let mut span = error.obligation.cause.span;
130-
let expn_data = span.ctxt().outer_expn_data();
131-
if let ExpnKind::Desugaring(_) = expn_data.kind {
132-
span = expn_data.call_site;
133-
}
134-
154+
let span = strip_desugaring(error.obligation.cause.span);
135155
error_map.entry(span).or_default().push(ErrorDescriptor {
136156
predicate: error.obligation.predicate,
137-
index: Some(index),
157+
source: Some((index, error)),
138158
});
139159
}
140160

@@ -144,59 +164,63 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
144164
for (_, error_set) in error_map.iter() {
145165
// We want to suppress "duplicate" errors with the same span.
146166
for error in error_set {
147-
if let Some(index) = error.index {
167+
let Some((index, error_source)) = error.source else {
168+
continue;
169+
};
170+
171+
for error2 in error_set {
148172
// Suppress errors that are either:
149173
// 1) strictly implied by another error.
150174
// 2) implied by an error with a smaller index.
151-
for error2 in error_set {
152-
if error2.index.is_some_and(|index2| is_suppressed[index2]) {
153-
// Avoid errors being suppressed by already-suppressed
154-
// errors, to prevent all errors from being suppressed
155-
// at once.
156-
continue;
157-
}
175+
if self.error_implied_by(error.predicate, error2.predicate)
176+
&& (!error2.source.is_some_and(|(index2, _)| index2 >= index)
177+
|| !self.error_implied_by(error2.predicate, error.predicate))
178+
{
179+
info!("skipping `{}` (implied by `{}`)", error.predicate, error2.predicate);
180+
is_suppressed[index] = true;
181+
break;
182+
}
158183

159-
if self.error_implies(error2.predicate, error.predicate)
160-
&& !(error2.index >= error.index
161-
&& self.error_implies(error.predicate, error2.predicate))
162-
{
163-
info!("skipping {:?} (implied by {:?})", error, error2);
164-
is_suppressed[index] = true;
165-
break;
166-
}
184+
// Also suppress the error if we are absolutely certain that a different
185+
// error is the one that the user should fix. This will suppress errors
186+
// about `<T as Pointee>::Metadata == ()` that can be fixed by `T: Sized`.
187+
if error.predicate.to_opt_poly_projection_pred().is_some()
188+
&& error2.predicate.to_opt_poly_trait_pred().is_some()
189+
&& self.error_fixed_by(
190+
error_source.obligation.clone(),
191+
error2.predicate.expect_clause(),
192+
)
193+
{
194+
info!("skipping `{}` (fixed by `{}`)", error.predicate, error2.predicate);
195+
is_suppressed[index] = true;
196+
break;
167197
}
168198
}
169199
}
170200
}
171201

172-
let mut reported = None;
173-
174202
for from_expansion in [false, true] {
175-
for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
176-
if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
177-
let guar = self.report_fulfillment_error(error);
178-
reported = Some(guar);
179-
// We want to ignore desugarings here: spans are equivalent even
180-
// if one is the result of a desugaring and the other is not.
181-
let mut span = error.obligation.cause.span;
182-
let expn_data = span.ctxt().outer_expn_data();
183-
if let ExpnKind::Desugaring(_) = expn_data.kind {
184-
span = expn_data.call_site;
185-
}
186-
self.reported_trait_errors
187-
.borrow_mut()
188-
.entry(span)
189-
.or_insert_with(|| (vec![], guar))
190-
.0
191-
.push(error.obligation.predicate);
203+
for (error, &suppressed) in iter::zip(&errors, &is_suppressed) {
204+
let span = error.obligation.cause.span;
205+
if suppressed || span.from_expansion() != from_expansion {
206+
continue;
192207
}
208+
209+
let guar = self.report_fulfillment_error(error);
210+
reported = Some(guar);
211+
212+
self.reported_trait_errors
213+
.borrow_mut()
214+
.entry(span)
215+
.or_insert_with(|| (vec![], guar))
216+
.0
217+
.push(error.obligation.predicate);
193218
}
194219
}
195220

196-
// It could be that we don't report an error because we have seen an `ErrorReported` from
197-
// another source. We should probably be able to fix most of these, but some are delayed
198-
// bugs that get a proper error after this function.
199-
reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
221+
// If all errors are suppressed, then we must have reported at least one error
222+
// from a previous call to this function.
223+
reported.unwrap_or_else(|| bug!("failed to report fulfillment errors"))
200224
}
201225

202226
/// Reports that an overflow has occurred and halts compilation. We
@@ -1465,10 +1489,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
14651489
&& self.can_eq(param_env, goal.term, assumption.term)
14661490
}
14671491

1468-
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
1469-
// `error` occurring implies that `cond` occurs.
1492+
/// Returns whether `cond` not occurring implies that `error` does not occur - i.e., that
1493+
/// `error` occurring implies that `cond` occurs.
14701494
#[instrument(level = "debug", skip(self), ret)]
1471-
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
1495+
fn error_implied_by(&self, error: ty::Predicate<'tcx>, cond: ty::Predicate<'tcx>) -> bool {
14721496
if cond == error {
14731497
return true;
14741498
}
@@ -1490,6 +1514,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
14901514
}
14911515
}
14921516

1517+
/// Returns whether fixing `cond` will also fix `error`.
1518+
#[instrument(level = "debug", skip(self), ret)]
1519+
fn error_fixed_by(&self, mut error: PredicateObligation<'tcx>, cond: ty::Clause<'tcx>) -> bool {
1520+
self.probe(|_| {
1521+
let ocx = ObligationCtxt::new(self);
1522+
1523+
let clauses = elaborate(self.tcx, std::iter::once(cond)).collect::<Vec<_>>();
1524+
let clauses = ocx.normalize(&error.cause, error.param_env, clauses);
1525+
let mut clauses = self.resolve_vars_if_possible(clauses);
1526+
1527+
if clauses.has_infer() {
1528+
return false;
1529+
}
1530+
1531+
clauses.extend(error.param_env.caller_bounds());
1532+
let clauses = self.tcx.mk_clauses(&clauses);
1533+
error.param_env = ty::ParamEnv::new(clauses, error.param_env.reveal());
1534+
1535+
ocx.register_obligation(error);
1536+
ocx.select_all_or_error().is_empty()
1537+
})
1538+
}
1539+
14931540
#[instrument(skip(self), level = "debug")]
14941541
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
14951542
if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default()

0 commit comments

Comments
 (0)