Skip to content

Commit 196ff44

Browse files
committed
Auto merge of rust-lang#122493 - lukas-code:sized-constraint, r=lcnr
clean up `Sized` checking This PR cleans up `sized_constraint` and related functions to make them simpler and faster. This should not make more or less code compile, but it can change error output in some rare cases. ## enums and unions are `Sized`, even if they are not WF The previous code has some special handling for enums, which made them sized if and only if the last field of each variant is sized. For example given this definition (which is not WF) ```rust enum E<T1: ?Sized, T2: ?Sized, U1: ?Sized, U2: ?Sized> { A(T1, T2), B(U1, U2), } ``` the enum was sized if and only if `T2` and `U2` are sized, while `T1` and `T2` were ignored for `Sized` checking. After this PR this enum will always be sized. Unsized enums are not a thing in Rust and removing this special case allows us to return an `Option<Ty>` from `sized_constraint`, rather than a `List<Ty>`. Similarly, the old code made an union defined like this ```rust union Union<T: ?Sized, U: ?Sized> { head: T, tail: U, } ``` sized if and only if `U` is sized, completely ignoring `T`. This just makes no sense at all and now this union is always sized. ## apply the "perf hack" to all (non-error) types, instead of just type parameters This "perf hack" skips evaluating `sized_constraint(adt): Sized` if `sized_constraint(adt): Sized` exactly matches a predicate defined on `adt`, for example: ```rust // `Foo<T>: Sized` iff `T: Sized`, but we know `T: Sized` from a predicate of `Foo` struct Foo<T /*: Sized */>(T); ``` Previously this was only applied to type parameters and now it is applied to every type. This means that for example this type is now always sized: ```rust // Note that this definition is WF, but the type `S<T>` not WF in the global/empty ParamEnv struct S<T>([T]) where [T]: Sized; ``` I don't anticipate this to affect compile time of any real-world program, but it makes the code a bit nicer and it also makes error messages a bit more consistent if someone does write such a cursed type. ## tuples are sized if the last type is sized The old solver already has this behavior and this PR also implements it for the new solver and `is_trivially_sized`. This makes it so that tuples work more like a struct defined like this: ```rust struct TupleN<T1, T2, /* ... */ Tn: ?Sized>(T1, T2, /* ... */ Tn); ``` This might improve the compile time of programs with large tuples a little, but is mostly also a consistency fix. ## `is_trivially_sized` for more types This function is used post-typeck code (borrowck, const eval, codegen) to skip evaluating `T: Sized` in some cases. It will now return `true` in more cases, most notably `UnsafeCell<T>` and `ManuallyDrop<T>` where `T.is_trivially_sized`. I'm anticipating that this change will improve compile time for some real world programs.
2 parents 148a41c + 99efae3 commit 196ff44

File tree

14 files changed

+126
-126
lines changed

14 files changed

+126
-126
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,8 +1034,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10341034
) -> InterpResult<'tcx> {
10351035
trace!("{:?} is now live", local);
10361036

1037-
// We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through
1038-
// all fields of a tuple, and (b) does something expensive for ADTs.
1037+
// We avoid `ty.is_trivially_sized` since that does something expensive for ADTs.
10391038
fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
10401039
match ty.kind() {
10411040
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
@@ -1054,9 +1053,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10541053
| ty::Closure(..)
10551054
| ty::CoroutineClosure(..)
10561055
| ty::Never
1057-
| ty::Error(_) => true,
1056+
| ty::Error(_)
1057+
| ty::Dynamic(_, _, ty::DynStar) => true,
10581058

1059-
ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false,
1059+
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
10601060

10611061
ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)),
10621062

compiler/rustc_middle/src/query/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,8 +703,8 @@ rustc_queries! {
703703
separate_provide_extern
704704
}
705705

706-
query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
707-
desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) }
706+
query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
707+
desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) }
708708
}
709709

710710
query adt_dtorck_constraint(

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -590,16 +590,16 @@ impl<'tcx> AdtDef<'tcx> {
590590
tcx.adt_destructor(self.did())
591591
}
592592

593-
/// Returns a list of types such that `Self: Sized` if and only if that
594-
/// type is `Sized`, or `ty::Error` if this type has a recursive layout.
595-
pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
596-
tcx.adt_sized_constraint(self.did())
593+
/// Returns a type such that `Self: Sized` if and only if that type is `Sized`,
594+
/// or `None` if the type is always sized.
595+
pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
596+
if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None }
597597
}
598598
}
599599

600600
#[derive(Clone, Copy, Debug)]
601601
#[derive(HashStable)]
602602
pub enum Representability {
603603
Representable,
604-
Infinite,
604+
Infinite(ErrorGuaranteed),
605605
}

compiler/rustc_middle/src/ty/inhabitedness/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) {
6161
/// requires calling [`InhabitedPredicate::instantiate`]
6262
fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
6363
if let Some(def_id) = def_id.as_local() {
64-
if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
64+
if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) {
6565
return InhabitedPredicate::True;
6666
}
6767
}

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2484,13 +2484,16 @@ impl<'tcx> Ty<'tcx> {
24842484
| ty::Closure(..)
24852485
| ty::CoroutineClosure(..)
24862486
| ty::Never
2487-
| ty::Error(_) => true,
2487+
| ty::Error(_)
2488+
| ty::Dynamic(_, _, ty::DynStar) => true,
24882489

2489-
ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false,
2490+
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
24902491

2491-
ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
2492+
ty::Tuple(tys) => tys.last().map_or(true, |ty| ty.is_trivially_sized(tcx)),
24922493

2493-
ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(),
2494+
ty::Adt(def, args) => def
2495+
.sized_constraint(tcx)
2496+
.map_or(true, |ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)),
24942497

24952498
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
24962499

compiler/rustc_middle/src/values.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
106106
representable_ids.insert(def_id);
107107
}
108108
}
109-
recursive_type_error(tcx, item_and_field_ids, &representable_ids);
110-
Representability::Infinite
109+
let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
110+
Representability::Infinite(guar)
111111
}
112112
}
113113

@@ -268,7 +268,7 @@ pub fn recursive_type_error(
268268
tcx: TyCtxt<'_>,
269269
mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
270270
representable_ids: &FxHashSet<LocalDefId>,
271-
) {
271+
) -> ErrorGuaranteed {
272272
const ITEM_LIMIT: usize = 5;
273273

274274
// Rotate the cycle so that the item with the lowest span is first
@@ -344,7 +344,7 @@ pub fn recursive_type_error(
344344
suggestion,
345345
Applicability::HasPlaceholders,
346346
)
347-
.emit();
347+
.emit()
348348
}
349349

350350
fn find_item_ty_spans(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ pub(super) trait GoalKind<'tcx>:
128128
goal: Goal<'tcx, Self>,
129129
) -> QueryResult<'tcx>;
130130

131-
/// A type is `Copy` or `Clone` if its components are `Sized`.
131+
/// A type is `Sized` if its tail component is `Sized`.
132132
///
133133
/// These components are given by built-in rules from
134134
/// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].

compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,25 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
154154
bug!("unexpected type `{ty}`")
155155
}
156156

157-
// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized
158-
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
159-
160-
// impl Sized for Adt where T: Sized forall T in field types
157+
// impl Sized for ()
158+
// impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
159+
ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |&ty| vec![ty::Binder::dummy(ty)])),
160+
161+
// impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
162+
// `sized_constraint(Adt)` is the deepest struct trail that can be determined
163+
// by the definition of `Adt`, independent of the generic args.
164+
// impl Sized for Adt<Args...> if sized_constraint(Adt) == None
165+
// As a performance optimization, `sized_constraint(Adt)` can return `None`
166+
// if the ADTs definition implies that it is sized by for all possible args.
167+
// In this case, the builtin impl will have no nested subgoals. This is a
168+
// "best effort" optimization and `sized_constraint` may return `Some`, even
169+
// if the ADT is sized for all possible args.
161170
ty::Adt(def, args) => {
162-
let sized_crit = def.sized_constraint(ecx.tcx());
163-
Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
171+
if let Some(sized_crit) = def.sized_constraint(ecx.tcx()) {
172+
Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.tcx(), args))])
173+
} else {
174+
Ok(vec![])
175+
}
164176
}
165177
}
166178
}

compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
2020
// such cases.
2121
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
2222
key.value.predicate.kind().skip_binder()
23+
&& let Some(sized_def_id) = tcx.lang_items().sized_trait()
24+
&& trait_ref.def_id() == sized_def_id
25+
&& trait_ref.self_ty().is_trivially_sized(tcx)
2326
{
24-
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
25-
if trait_ref.def_id() == sized_def_id {
26-
if trait_ref.self_ty().is_trivially_sized(tcx) {
27-
return Some(());
28-
}
29-
}
30-
}
27+
return Some(());
3128
}
3229

3330
if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) =

compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,13 +2123,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
21232123
),
21242124

21252125
ty::Adt(def, args) => {
2126-
let sized_crit = def.sized_constraint(self.tcx());
2127-
// (*) binder moved here
2128-
Where(
2129-
obligation
2130-
.predicate
2131-
.rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
2132-
)
2126+
if let Some(sized_crit) = def.sized_constraint(self.tcx()) {
2127+
// (*) binder moved here
2128+
Where(
2129+
obligation.predicate.rebind(vec![sized_crit.instantiate(self.tcx(), args)]),
2130+
)
2131+
} else {
2132+
Where(ty::Binder::dummy(Vec::new()))
2133+
}
21332134
}
21342135

21352136
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,

0 commit comments

Comments
 (0)