Skip to content

Commit c9b796a

Browse files
committed
Auto merge of #122493 - lukas-code:sized-constraint, r=<try>
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 e69f14b + b5ef7bd commit c9b796a

File tree

14 files changed

+113
-117
lines changed

14 files changed

+113
-117
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1008,8 +1008,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10081008
) -> InterpResult<'tcx> {
10091009
trace!("{:?} is now live", local);
10101010

1011-
// We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through
1012-
// all fields of a tuple, and (b) does something expensive for ADTs.
1011+
// We avoid `ty.is_trivially_sized` since that does something expensive for ADTs.
10131012
fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
10141013
match ty.kind() {
10151014
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
@@ -1028,11 +1027,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10281027
| ty::Closure(..)
10291028
| ty::CoroutineClosure(..)
10301029
| ty::Never
1031-
| ty::Error(_) => true,
1030+
| ty::Error(_)
1031+
| ty::Dynamic(_, _, ty::DynStar) => true,
10321032

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

1035-
ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)),
1035+
ty::Tuple(tys) => tys.last().map_or(true, |&ty| is_very_trivially_sized(ty)),
10361036

10371037
// We don't want to do any queries, so there is not much we can do with ADTs.
10381038
ty::Adt(..) => false,

compiler/rustc_middle/src/query/mod.rs

+2-2
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 `Sized` constraint for `{}`", tcx.def_path_str(key) }
708708
}
709709

710710
query adt_dtorck_constraint(

compiler/rustc_middle/src/ty/adt.rs

+5-5
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`, or `None`
594+
/// if the type is always sized, or `ty::Error` if this type has a recursive layout.
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

+1-1
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

+7-4
Original file line numberDiff line numberDiff line change
@@ -2483,13 +2483,16 @@ impl<'tcx> Ty<'tcx> {
24832483
| ty::Closure(..)
24842484
| ty::CoroutineClosure(..)
24852485
| ty::Never
2486-
| ty::Error(_) => true,
2486+
| ty::Error(_)
2487+
| ty::Dynamic(_, _, ty::DynStar) => true,
24872488

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

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

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

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

compiler/rustc_middle/src/values.rs

+4-4
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

+1-1
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

+4-2
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,13 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
152152
bug!("unexpected type `{ty}`")
153153
}
154154

155-
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
155+
ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |&ty| vec![ty::Binder::dummy(ty)])),
156156

157157
ty::Adt(def, args) => {
158158
let sized_crit = def.sized_constraint(ecx.tcx());
159-
Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
159+
Ok(sized_crit.map_or_else(Vec::new, |ty| {
160+
vec![ty::Binder::dummy(ty.instantiate(ecx.tcx(), args))]
161+
}))
160162
}
161163
}
162164
}

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

+4-7
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

+3-5
Original file line numberDiff line numberDiff line change
@@ -2120,11 +2120,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
21202120
ty::Adt(def, args) => {
21212121
let sized_crit = def.sized_constraint(self.tcx());
21222122
// (*) binder moved here
2123-
Where(
2124-
obligation
2125-
.predicate
2126-
.rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
2127-
)
2123+
Where(obligation.predicate.rebind(
2124+
sized_crit.map_or_else(Vec::new, |ty| vec![ty.instantiate(self.tcx(), args)]),
2125+
))
21282126
}
21292127

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

compiler/rustc_ty_utils/src/representability.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub(crate) fn provide(providers: &mut Providers) {
1212
macro_rules! rtry {
1313
($e:expr) => {
1414
match $e {
15-
e @ Representability::Infinite => return e,
15+
e @ Representability::Infinite(_) => return e,
1616
Representability::Representable => {}
1717
}
1818
};

compiler/rustc_ty_utils/src/ty.rs

+72-69
Original file line numberDiff line numberDiff line change
@@ -3,76 +3,56 @@ use rustc_hir as hir;
33
use rustc_hir::def::DefKind;
44
use rustc_index::bit_set::BitSet;
55
use rustc_middle::query::Providers;
6-
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitor};
6+
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt, TypeVisitor};
77
use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable};
88
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
99
use rustc_span::DUMMY_SP;
1010
use rustc_trait_selection::traits;
1111

12-
fn sized_constraint_for_ty<'tcx>(
13-
tcx: TyCtxt<'tcx>,
14-
adtdef: ty::AdtDef<'tcx>,
15-
ty: Ty<'tcx>,
16-
) -> Vec<Ty<'tcx>> {
12+
#[instrument(level = "debug", skip(tcx), ret)]
13+
fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1714
use rustc_type_ir::TyKind::*;
1815

19-
let result = match ty.kind() {
20-
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
21-
| FnPtr(_) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) | Never => {
22-
vec![]
23-
}
24-
25-
Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | CoroutineWitness(..) => {
26-
// these are never sized - return the target type
27-
vec![ty]
28-
}
29-
30-
Tuple(tys) => match tys.last() {
31-
None => vec![],
32-
Some(&ty) => sized_constraint_for_ty(tcx, adtdef, ty),
33-
},
34-
16+
match ty.kind() {
17+
// these are always sized
18+
Bool
19+
| Char
20+
| Int(..)
21+
| Uint(..)
22+
| Float(..)
23+
| RawPtr(..)
24+
| Ref(..)
25+
| FnDef(..)
26+
| FnPtr(..)
27+
| Array(..)
28+
| Closure(..)
29+
| CoroutineClosure(..)
30+
| Coroutine(..)
31+
| CoroutineWitness(..)
32+
| Never
33+
| Dynamic(_, _, ty::DynStar) => None,
34+
35+
// these are never sized
36+
Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty),
37+
38+
Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)),
39+
40+
// recursive case
3541
Adt(adt, args) => {
36-
// recursive case
37-
let adt_tys = adt.sized_constraint(tcx);
38-
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
39-
adt_tys
40-
.iter_instantiated(tcx, args)
41-
.flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
42-
.collect()
43-
}
44-
45-
Alias(..) => {
46-
// must calculate explicitly.
47-
// FIXME: consider special-casing always-Sized projections
48-
vec![ty]
42+
let intermediate = adt.sized_constraint(tcx);
43+
intermediate.and_then(|intermediate| {
44+
let ty = intermediate.instantiate(tcx, args);
45+
sized_constraint_for_ty(tcx, ty)
46+
})
4947
}
5048

51-
Param(..) => {
52-
// perf hack: if there is a `T: Sized` bound, then
53-
// we know that `T` is Sized and do not need to check
54-
// it on the impl.
55-
56-
let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else { return vec![ty] };
57-
let predicates = tcx.predicates_of(adtdef.did()).predicates;
58-
if predicates.iter().any(|(p, _)| {
59-
p.as_trait_clause().is_some_and(|trait_pred| {
60-
trait_pred.def_id() == sized_trait_def_id
61-
&& trait_pred.self_ty().skip_binder() == ty
62-
})
63-
}) {
64-
vec![]
65-
} else {
66-
vec![ty]
67-
}
68-
}
49+
// these can be sized or unsized
50+
Param(..) | Alias(..) | Error(_) => Some(ty),
6951

7052
Placeholder(..) | Bound(..) | Infer(..) => {
71-
bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty)
53+
bug!("unexpected type `{ty:?}` in sized_constraint_for_ty")
7254
}
73-
};
74-
debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result);
75-
result
55+
}
7656
}
7757

7858
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
@@ -90,29 +70,52 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
9070
///
9171
/// In fact, there are only a few options for the types in the constraint:
9272
/// - an obviously-unsized type
93-
/// - a type parameter or projection whose Sizedness can't be known
94-
/// - a tuple of type parameters or projections, if there are multiple
95-
/// such.
73+
/// - a type parameter or projection whose sizedness can't be known
9674
/// - an Error, if a type is infinitely sized
75+
#[instrument(level = "debug", skip(tcx), ret)]
9776
fn adt_sized_constraint<'tcx>(
9877
tcx: TyCtxt<'tcx>,
9978
def_id: DefId,
100-
) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
79+
) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
10180
if let Some(def_id) = def_id.as_local() {
102-
if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
103-
return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_misc_error(tcx)]));
81+
if let ty::Representability::Infinite(guar) = tcx.representability(def_id) {
82+
return Some(ty::EarlyBinder::bind(Ty::new_error(tcx, guar)));
10483
}
10584
}
10685
let def = tcx.adt_def(def_id);
10786

108-
let result =
109-
tcx.mk_type_list_from_iter(def.variants().iter().filter_map(|v| v.tail_opt()).flat_map(
110-
|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did).instantiate_identity()),
111-
));
87+
if !def.is_struct() {
88+
bug!("`adt_sized_constraint` called on non-struct type: {def:?}");
89+
}
90+
91+
let tail_def = def.non_enum_variant().tail_opt()?;
92+
let tail_ty = tcx.type_of(tail_def.did).instantiate_identity();
93+
94+
let result = sized_constraint_for_ty(tcx, tail_ty);
95+
96+
let result = result.filter(|&ty| {
97+
// perf hack: if there is a `ty: Sized` bound, then we know that
98+
// the type is sized and do not need to check it on the impl.
99+
100+
if ty.references_error() {
101+
return true;
102+
}
103+
104+
let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else {
105+
return true;
106+
};
107+
108+
let predicates = tcx.predicates_of(def.did()).predicates;
112109

113-
debug!("adt_sized_constraint: {:?} => {:?}", def, result);
110+
!predicates.iter().any(|(p, _)| {
111+
p.as_trait_clause().is_some_and(|trait_pred| {
112+
trait_pred.def_id() == sized_trait_def_id
113+
&& trait_pred.self_ty().skip_binder() == ty
114+
})
115+
})
116+
});
114117

115-
ty::EarlyBinder::bind(result)
118+
result.map(ty::EarlyBinder::bind)
116119
}
117120

118121
/// See `ParamEnv` struct definition for details.

tests/ui/generic-associated-types/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ where
2929

3030
fn main() {
3131
let mut list = RcNode::<i32>::new();
32-
//~^ ERROR the size for values of type `Node<i32, RcFamily>` cannot be known at compilation time
32+
//~^ ERROR trait bounds were not satisfied
3333
}

0 commit comments

Comments
 (0)