Skip to content

Commit 8ad9411

Browse files
author
Lukas Markeffsky
committed
clean up ADT sized constraint computation
1 parent 0e7e1bf commit 8ad9411

File tree

8 files changed

+93
-95
lines changed

8 files changed

+93
-95
lines changed

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

710710
query adt_dtorck_constraint(

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -590,10 +590,10 @@ 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

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2490,7 +2490,7 @@ impl<'tcx> Ty<'tcx> {
24902490

24912491
ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
24922492

2493-
ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(),
2493+
ty::Adt(def, _args) => def.sized_constraint(tcx).is_none(),
24942494

24952495
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
24962496

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,20 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
157157
// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized
158158
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
159159

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

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

Lines changed: 3 additions & 5 deletions
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/ty.rs

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,59 @@
11
use rustc_data_structures::fx::FxHashSet;
22
use rustc_hir as hir;
33
use rustc_hir::def::DefKind;
4+
use rustc_hir::LangItem;
45
use rustc_index::bit_set::BitSet;
56
use rustc_middle::query::Providers;
6-
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitor};
7+
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt, TypeVisitor};
78
use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable};
89
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
910
use rustc_span::DUMMY_SP;
1011
use rustc_trait_selection::traits;
1112

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

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-
17+
match ty.kind() {
18+
// these are always sized
19+
Bool
20+
| Char
21+
| Int(..)
22+
| Uint(..)
23+
| Float(..)
24+
| RawPtr(..)
25+
| Ref(..)
26+
| FnDef(..)
27+
| FnPtr(..)
28+
| Array(..)
29+
| Closure(..)
30+
| CoroutineClosure(..)
31+
| Coroutine(..)
32+
| CoroutineWitness(..)
33+
| Never
34+
| Dynamic(_, _, ty::DynStar) => None,
35+
36+
// these are never sized
37+
Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty),
38+
39+
Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)),
40+
41+
// recursive case
3542
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+
let intermediate = adt.sized_constraint(tcx);
44+
intermediate.and_then(|intermediate| {
45+
let ty = intermediate.instantiate(tcx, args);
46+
sized_constraint_for_ty(tcx, ty)
47+
})
4348
}
4449

45-
Alias(..) => {
46-
// must calculate explicitly.
47-
// FIXME: consider special-casing always-Sized projections
48-
vec![ty]
49-
}
50-
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-
}
50+
// these can be sized or unsized
51+
Param(..) | Alias(..) | Error(_) => Some(ty),
6952

7053
Placeholder(..) | Bound(..) | Infer(..) => {
71-
bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty)
54+
bug!("unexpected type `{ty:?}` in sized_constraint_for_ty")
7255
}
73-
};
74-
debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result);
75-
result
56+
}
7657
}
7758

7859
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
@@ -90,29 +71,45 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
9071
///
9172
/// In fact, there are only a few options for the types in the constraint:
9273
/// - 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.
96-
/// - an Error, if a type is infinitely sized
74+
/// - a type parameter or projection whose sizedness can't be known
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 let ty::Representability::Infinite(guar) = tcx.representability(def_id) {
103-
return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_error(tcx, guar)]));
81+
if let ty::Representability::Infinite(_) = tcx.representability(def_id) {
82+
return None;
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();
11293

113-
debug!("adt_sized_constraint: {:?} => {:?}", def, result);
94+
let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?;
95+
if constraint_ty.references_error() {
96+
return None;
97+
}
98+
99+
// perf hack: if there is a `constraint_ty: Sized` bound, then we know
100+
// that the type is sized and do not need to check it on the impl.
101+
let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, None);
102+
let predicates = tcx.predicates_of(def.did()).predicates;
103+
if predicates.iter().any(|(p, _)| {
104+
p.as_trait_clause().is_some_and(|trait_pred| {
105+
trait_pred.def_id() == sized_trait_def_id
106+
&& trait_pred.self_ty().skip_binder() == constraint_ty
107+
})
108+
}) {
109+
return None;
110+
}
114111

115-
ty::EarlyBinder::bind(result)
112+
Some(ty::EarlyBinder::bind(constraint_ty))
116113
}
117114

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

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

Lines changed: 1 addition & 1 deletion
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
}

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

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,15 @@ help: consider relaxing the implicit `Sized` restriction
1515
LL | type Pointer<T>: Deref<Target = T> + ?Sized;
1616
| ++++++++
1717

18-
error[E0599]: the size for values of type `Node<i32, RcFamily>` cannot be known at compilation time
18+
error[E0599]: the variant or associated item `new` exists for enum `Node<i32, RcFamily>`, but its trait bounds were not satisfied
1919
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:31:35
2020
|
2121
LL | enum Node<T, P: PointerFamily> {
22-
| ------------------------------ variant or associated item `new` not found for this enum because it doesn't satisfy `Node<i32, RcFamily>: Sized`
22+
| ------------------------------ variant or associated item `new` not found for this enum
2323
...
2424
LL | let mut list = RcNode::<i32>::new();
25-
| ^^^ doesn't have a size known at compile-time
25+
| ^^^ variant or associated item cannot be called on `Node<i32, RcFamily>` due to unsatisfied trait bounds
2626
|
27-
note: trait bound `Node<i32, RcFamily>: Sized` was not satisfied
28-
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:4:18
29-
|
30-
LL | type Pointer<T>: Deref<Target = T>;
31-
| ------- ^ unsatisfied trait bound introduced here
3227
note: trait bound `(dyn Deref<Target = Node<i32, RcFamily>> + 'static): Sized` was not satisfied
3328
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:23:29
3429
|
@@ -37,8 +32,6 @@ LL | impl<T, P: PointerFamily> Node<T, P>
3732
LL | where
3833
LL | P::Pointer<Node<T, P>>: Sized,
3934
| ^^^^^ unsatisfied trait bound introduced here
40-
note: the trait `Sized` must be implemented
41-
--> $SRC_DIR/core/src/marker.rs:LL:COL
4235

4336
error: aborting due to 2 previous errors
4437

0 commit comments

Comments
 (0)