clean up ADT sized constraint computation
This commit is contained in:
parent
0e7e1bfdbc
commit
8ad94111ad
@ -703,8 +703,8 @@
|
|||||||
separate_provide_extern
|
separate_provide_extern
|
||||||
}
|
}
|
||||||
|
|
||||||
query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
|
query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
|
||||||
desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) }
|
desc { |tcx| "computing `Sized` constraint for `{}`", tcx.def_path_str(key) }
|
||||||
}
|
}
|
||||||
|
|
||||||
query adt_dtorck_constraint(
|
query adt_dtorck_constraint(
|
||||||
|
@ -590,10 +590,10 @@ pub fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<Destructor> {
|
|||||||
tcx.adt_destructor(self.did())
|
tcx.adt_destructor(self.did())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of types such that `Self: Sized` if and only if that
|
/// Returns a type such that `Self: Sized` if and only if that type is `Sized`,
|
||||||
/// type is `Sized`, or `ty::Error` if this type has a recursive layout.
|
/// or `None` if the type is always sized.
|
||||||
pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
|
pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
|
||||||
tcx.adt_sized_constraint(self.did())
|
if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2490,7 +2490,7 @@ pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
|
|||||||
|
|
||||||
ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
|
ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
|
||||||
|
|
||||||
ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(),
|
ty::Adt(def, _args) => def.sized_constraint(tcx).is_none(),
|
||||||
|
|
||||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
|
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
|
||||||
|
|
||||||
|
@ -157,10 +157,20 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
|||||||
// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized
|
// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized
|
||||||
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
|
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
|
||||||
|
|
||||||
// impl Sized for Adt where T: Sized forall T in field types
|
// impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
|
||||||
|
// `sized_constraint(Adt)` is the deepest struct trail that can be determined
|
||||||
|
// by the definition of `Adt`, independent of the generic args.
|
||||||
|
// impl Sized for Adt<Args...> if sized_constraint(Adt) == None
|
||||||
|
// As a performance optimization, `sized_constraint(Adt)` can return `None`
|
||||||
|
// if the ADTs definition implies that it is sized by for all possible args.
|
||||||
|
// In this case, the builtin impl will have no nested subgoals. This is a
|
||||||
|
// "best effort" optimization and `sized_constraint` may return `Some`, even
|
||||||
|
// if the ADT is sized for all possible args.
|
||||||
ty::Adt(def, args) => {
|
ty::Adt(def, args) => {
|
||||||
let sized_crit = def.sized_constraint(ecx.tcx());
|
let sized_crit = def.sized_constraint(ecx.tcx());
|
||||||
Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
|
Ok(sized_crit.map_or_else(Vec::new, |ty| {
|
||||||
|
vec![ty::Binder::dummy(ty.instantiate(ecx.tcx(), args))]
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2120,11 +2120,9 @@ fn sized_conditions(
|
|||||||
ty::Adt(def, args) => {
|
ty::Adt(def, args) => {
|
||||||
let sized_crit = def.sized_constraint(self.tcx());
|
let sized_crit = def.sized_constraint(self.tcx());
|
||||||
// (*) binder moved here
|
// (*) binder moved here
|
||||||
Where(
|
Where(obligation.predicate.rebind(
|
||||||
obligation
|
sized_crit.map_or_else(Vec::new, |ty| vec![ty.instantiate(self.tcx(), args)]),
|
||||||
.predicate
|
))
|
||||||
.rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
|
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
|
||||||
|
@ -1,78 +1,59 @@
|
|||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
|
use rustc_hir::LangItem;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitor};
|
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt, TypeVisitor};
|
||||||
use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable};
|
use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable};
|
||||||
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
|
|
||||||
fn sized_constraint_for_ty<'tcx>(
|
#[instrument(level = "debug", skip(tcx), ret)]
|
||||||
tcx: TyCtxt<'tcx>,
|
fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
adtdef: ty::AdtDef<'tcx>,
|
|
||||||
ty: Ty<'tcx>,
|
|
||||||
) -> Vec<Ty<'tcx>> {
|
|
||||||
use rustc_type_ir::TyKind::*;
|
use rustc_type_ir::TyKind::*;
|
||||||
|
|
||||||
let result = match ty.kind() {
|
match ty.kind() {
|
||||||
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
|
// these are always sized
|
||||||
| FnPtr(_) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) | Never => {
|
Bool
|
||||||
vec![]
|
| Char
|
||||||
}
|
| Int(..)
|
||||||
|
| Uint(..)
|
||||||
|
| Float(..)
|
||||||
|
| RawPtr(..)
|
||||||
|
| Ref(..)
|
||||||
|
| FnDef(..)
|
||||||
|
| FnPtr(..)
|
||||||
|
| Array(..)
|
||||||
|
| Closure(..)
|
||||||
|
| CoroutineClosure(..)
|
||||||
|
| Coroutine(..)
|
||||||
|
| CoroutineWitness(..)
|
||||||
|
| Never
|
||||||
|
| Dynamic(_, _, ty::DynStar) => None,
|
||||||
|
|
||||||
Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | CoroutineWitness(..) => {
|
// these are never sized
|
||||||
// these are never sized - return the target type
|
Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty),
|
||||||
vec![ty]
|
|
||||||
}
|
|
||||||
|
|
||||||
Tuple(tys) => match tys.last() {
|
Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)),
|
||||||
None => vec![],
|
|
||||||
Some(&ty) => sized_constraint_for_ty(tcx, adtdef, ty),
|
|
||||||
},
|
|
||||||
|
|
||||||
|
// recursive case
|
||||||
Adt(adt, args) => {
|
Adt(adt, args) => {
|
||||||
// recursive case
|
let intermediate = adt.sized_constraint(tcx);
|
||||||
let adt_tys = adt.sized_constraint(tcx);
|
intermediate.and_then(|intermediate| {
|
||||||
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
|
let ty = intermediate.instantiate(tcx, args);
|
||||||
adt_tys
|
sized_constraint_for_ty(tcx, ty)
|
||||||
.iter_instantiated(tcx, args)
|
})
|
||||||
.flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(..) => {
|
// these can be sized or unsized
|
||||||
// must calculate explicitly.
|
Param(..) | Alias(..) | Error(_) => Some(ty),
|
||||||
// FIXME: consider special-casing always-Sized projections
|
|
||||||
vec![ty]
|
|
||||||
}
|
|
||||||
|
|
||||||
Param(..) => {
|
|
||||||
// perf hack: if there is a `T: Sized` bound, then
|
|
||||||
// we know that `T` is Sized and do not need to check
|
|
||||||
// it on the impl.
|
|
||||||
|
|
||||||
let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else { return vec![ty] };
|
|
||||||
let predicates = tcx.predicates_of(adtdef.did()).predicates;
|
|
||||||
if predicates.iter().any(|(p, _)| {
|
|
||||||
p.as_trait_clause().is_some_and(|trait_pred| {
|
|
||||||
trait_pred.def_id() == sized_trait_def_id
|
|
||||||
&& trait_pred.self_ty().skip_binder() == ty
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
vec![ty]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Placeholder(..) | Bound(..) | Infer(..) => {
|
Placeholder(..) | Bound(..) | Infer(..) => {
|
||||||
bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty)
|
bug!("unexpected type `{ty:?}` in sized_constraint_for_ty")
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
|
fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
|
||||||
@ -90,29 +71,45 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
|
|||||||
///
|
///
|
||||||
/// In fact, there are only a few options for the types in the constraint:
|
/// In fact, there are only a few options for the types in the constraint:
|
||||||
/// - an obviously-unsized type
|
/// - an obviously-unsized type
|
||||||
/// - a type parameter or projection whose Sizedness can't be known
|
/// - a type parameter or projection whose sizedness can't be known
|
||||||
/// - a tuple of type parameters or projections, if there are multiple
|
#[instrument(level = "debug", skip(tcx), ret)]
|
||||||
/// such.
|
|
||||||
/// - an Error, if a type is infinitely sized
|
|
||||||
fn adt_sized_constraint<'tcx>(
|
fn adt_sized_constraint<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
|
) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
|
||||||
if let Some(def_id) = def_id.as_local() {
|
if let Some(def_id) = def_id.as_local() {
|
||||||
if let ty::Representability::Infinite(guar) = tcx.representability(def_id) {
|
if let ty::Representability::Infinite(_) = tcx.representability(def_id) {
|
||||||
return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_error(tcx, guar)]));
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let def = tcx.adt_def(def_id);
|
let def = tcx.adt_def(def_id);
|
||||||
|
|
||||||
let result =
|
if !def.is_struct() {
|
||||||
tcx.mk_type_list_from_iter(def.variants().iter().filter_map(|v| v.tail_opt()).flat_map(
|
bug!("`adt_sized_constraint` called on non-struct type: {def:?}");
|
||||||
|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did).instantiate_identity()),
|
}
|
||||||
));
|
|
||||||
|
|
||||||
debug!("adt_sized_constraint: {:?} => {:?}", def, result);
|
let tail_def = def.non_enum_variant().tail_opt()?;
|
||||||
|
let tail_ty = tcx.type_of(tail_def.did).instantiate_identity();
|
||||||
|
|
||||||
ty::EarlyBinder::bind(result)
|
let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?;
|
||||||
|
if constraint_ty.references_error() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perf hack: if there is a `constraint_ty: Sized` bound, then we know
|
||||||
|
// that the type is sized and do not need to check it on the impl.
|
||||||
|
let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, None);
|
||||||
|
let predicates = tcx.predicates_of(def.did()).predicates;
|
||||||
|
if predicates.iter().any(|(p, _)| {
|
||||||
|
p.as_trait_clause().is_some_and(|trait_pred| {
|
||||||
|
trait_pred.def_id() == sized_trait_def_id
|
||||||
|
&& trait_pred.self_ty().skip_binder() == constraint_ty
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ty::EarlyBinder::bind(constraint_ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `ParamEnv` struct definition for details.
|
/// See `ParamEnv` struct definition for details.
|
||||||
|
@ -29,5 +29,5 @@ fn new() -> P::Pointer<Self> {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut list = RcNode::<i32>::new();
|
let mut list = RcNode::<i32>::new();
|
||||||
//~^ ERROR the size for values of type `Node<i32, RcFamily>` cannot be known at compilation time
|
//~^ ERROR trait bounds were not satisfied
|
||||||
}
|
}
|
||||||
|
@ -15,20 +15,15 @@ help: consider relaxing the implicit `Sized` restriction
|
|||||||
LL | type Pointer<T>: Deref<Target = T> + ?Sized;
|
LL | type Pointer<T>: Deref<Target = T> + ?Sized;
|
||||||
| ++++++++
|
| ++++++++
|
||||||
|
|
||||||
error[E0599]: the size for values of type `Node<i32, RcFamily>` cannot be known at compilation time
|
error[E0599]: the variant or associated item `new` exists for enum `Node<i32, RcFamily>`, but its trait bounds were not satisfied
|
||||||
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:31:35
|
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:31:35
|
||||||
|
|
|
|
||||||
LL | enum Node<T, P: PointerFamily> {
|
LL | enum Node<T, P: PointerFamily> {
|
||||||
| ------------------------------ variant or associated item `new` not found for this enum because it doesn't satisfy `Node<i32, RcFamily>: Sized`
|
| ------------------------------ variant or associated item `new` not found for this enum
|
||||||
...
|
...
|
||||||
LL | let mut list = RcNode::<i32>::new();
|
LL | let mut list = RcNode::<i32>::new();
|
||||||
| ^^^ doesn't have a size known at compile-time
|
| ^^^ variant or associated item cannot be called on `Node<i32, RcFamily>` due to unsatisfied trait bounds
|
||||||
|
|
|
|
||||||
note: trait bound `Node<i32, RcFamily>: Sized` was not satisfied
|
|
||||||
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:4:18
|
|
||||||
|
|
|
||||||
LL | type Pointer<T>: Deref<Target = T>;
|
|
||||||
| ------- ^ unsatisfied trait bound introduced here
|
|
||||||
note: trait bound `(dyn Deref<Target = Node<i32, RcFamily>> + 'static): Sized` was not satisfied
|
note: trait bound `(dyn Deref<Target = Node<i32, RcFamily>> + 'static): Sized` was not satisfied
|
||||||
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:23:29
|
--> $DIR/issue-119942-unsatisified-gat-bound-during-assoc-ty-selection.rs:23:29
|
||||||
|
|
|
|
||||||
@ -37,8 +32,6 @@ LL | impl<T, P: PointerFamily> Node<T, P>
|
|||||||
LL | where
|
LL | where
|
||||||
LL | P::Pointer<Node<T, P>>: Sized,
|
LL | P::Pointer<Node<T, P>>: Sized,
|
||||||
| ^^^^^ unsatisfied trait bound introduced here
|
| ^^^^^ unsatisfied trait bound introduced here
|
||||||
note: the trait `Sized` must be implemented
|
|
||||||
--> $SRC_DIR/core/src/marker.rs:LL:COL
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user