Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/exporter/src/types/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ pub enum Operand {

#[cfg(feature = "rustc")]
impl Operand {
pub(crate) fn ty(&self) -> &Ty {
pub fn ty(&self) -> &Ty {
match self {
Operand::Copy(p) | Operand::Move(p) => &p.ty,
Operand::Constant(c) => &c.ty,
Expand Down
173 changes: 107 additions & 66 deletions frontend/exporter/src/types/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,15 +1120,8 @@ pub enum TyKind {
Str,
RawPtr(Box<Ty>, Mutability),
Ref(Region, Box<Ty>, Mutability),
#[custom_arm(FROM_TYPE::Dynamic(preds, region) => make_dyn(s, preds, region),)]
Dynamic(
/// Fresh type parameter that we use as the `Self` type in the prediates below.
ParamTy,
/// Clauses that define the trait object. These clauses use the fresh type parameter above
/// as `Self` type.
GenericPredicates,
Region,
),
#[custom_arm(FROM_TYPE::Dynamic(preds, region) => TyKind::Dynamic(resolve_for_dyn(s, preds, |_| ()), region.sinto(s)),)]
Dynamic(DynBinder<()>, Region),
#[custom_arm(FROM_TYPE::Coroutine(def_id, generics) => TO_TYPE::Coroutine(translate_item_ref(s, *def_id, generics)),)]
Coroutine(ItemRef),
Never,
Expand All @@ -1144,23 +1137,68 @@ pub enum TyKind {
Todo(String),
}

/// Transform existential predicates into properly resolved predicates.
/// A representation of `exists<T: Trait1 + Trait2>(value)`: we create a fresh type id and the
/// appropriate trait clauses. The contained value may refer to the fresh ty and the in-scope trait
/// clauses. This is used to represent types related to `dyn Trait`.
#[derive_group(Serializers)]
#[derive(Clone, Debug, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct DynBinder<T> {
/// Fresh type parameter that we use as the `Self` type in the prediates below.
pub existential_ty: ParamTy,
/// Clauses that define the trait object. These clauses use the fresh type parameter above
/// as `Self` type.
pub predicates: GenericPredicates,
/// The value inside the binder.
pub val: T,
}

/// Do trait resolution in the context of the clauses of a `dyn Trait` type.
#[cfg(feature = "rustc")]
fn make_dyn<'tcx, S: UnderOwnerState<'tcx>>(
fn resolve_for_dyn<'tcx, S: UnderOwnerState<'tcx>, R>(
s: &S,
// The predicates in the context.
epreds: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
region: &ty::Region<'tcx>,
) -> TyKind {
let tcx = s.base().tcx;
let def_id = s.owner_id();
let span = rustc_span::DUMMY_SP.sinto(s);
f: impl FnOnce(&mut PredicateSearcher<'tcx>) -> R,
) -> DynBinder<R> {
fn searcher_for_traits<'tcx, S: UnderOwnerState<'tcx>>(
s: &S,
clauses: &Vec<ty::Clause<'tcx>>,
) -> PredicateSearcher<'tcx> {
let tcx = s.base().tcx;
// Populate a predicate searcher that knows about the `dyn` clauses.
let mut predicate_searcher = s.with_predicate_searcher(|ps| ps.clone());
predicate_searcher
.insert_bound_predicates(clauses.iter().filter_map(|clause| clause.as_trait_clause()));
predicate_searcher.set_param_env(
rustc_trait_selection::traits::normalize_param_env_or_error(
tcx,
ty::ParamEnv::new(
tcx.mk_clauses_from_iter(
s.param_env()
.caller_bounds()
.iter()
.chain(clauses.iter().copied()),
),
),
rustc_trait_selection::traits::ObligationCause::dummy(),
),
);
predicate_searcher
}

// Pretend there's an extra type in the environment.
let new_param_ty = {
fn fresh_param_ty<'tcx, S: UnderOwnerState<'tcx>>(s: &S) -> ty::ParamTy {
let tcx = s.base().tcx;
let def_id = s.owner_id();
let generics = tcx.generics_of(def_id);
let param_count = generics.parent_count + generics.own_params.len();
ty::ParamTy::new(param_count as u32 + 1, rustc_span::Symbol::intern("_dyn"))
};
}

let tcx = s.base().tcx;
let span = rustc_span::DUMMY_SP.sinto(s);

// Pretend there's an extra type in the environment.
let new_param_ty = fresh_param_ty(s);
let new_ty = new_param_ty.to_ty(tcx);

// Set the new type as the `Self` parameter of our predicates.
Expand All @@ -1170,21 +1208,8 @@ fn make_dyn<'tcx, S: UnderOwnerState<'tcx>>(
.collect();

// Populate a predicate searcher that knows about the `dyn` clauses.
let mut predicate_searcher = s.with_predicate_searcher(|ps| ps.clone());
predicate_searcher
.insert_bound_predicates(clauses.iter().filter_map(|clause| clause.as_trait_clause()));
predicate_searcher.set_param_env(rustc_trait_selection::traits::normalize_param_env_or_error(
tcx,
ty::ParamEnv::new(
tcx.mk_clauses_from_iter(
s.param_env()
.caller_bounds()
.iter()
.chain(clauses.iter().copied()),
),
),
rustc_trait_selection::traits::ObligationCause::dummy(),
));
let mut predicate_searcher = searcher_for_traits(s, &clauses);
let val = f(&mut predicate_searcher);

// Using the predicate searcher, translate the predicates. Only the projection predicates need
// to be handled specially.
Expand Down Expand Up @@ -1231,9 +1256,11 @@ fn make_dyn<'tcx, S: UnderOwnerState<'tcx>>(
.collect();

let predicates = GenericPredicates { predicates };
let param_ty = new_param_ty.sinto(s);
let region = region.sinto(s);
TyKind::Dynamic(param_ty, predicates, region)
DynBinder {
existential_ty: new_param_ty.sinto(s),
predicates,
val,
}
}

/// Reflects [`ty::CanonicalUserTypeAnnotation`]
Expand Down Expand Up @@ -1331,8 +1358,13 @@ pub enum PointerCoercion {
#[derive_group(Serializers)]
#[derive(Clone, Debug, JsonSchema)]
pub enum UnsizingMetadata {
/// Unsize an array to a slice, storing the length as metadata.
Length(ConstantExpr),
VTablePtr(ImplExpr),
/// Unsize a non-dyn type to a dyn type, adding a vtable pointer as metadata.
DirectVTable(ImplExpr),
/// Unsize a dyn-type to another dyn-type, (optionally) indexing within the current vtable.
NestedVTable(DynBinder<ImplExpr>),
/// Couldn't compute
Unknown,
}

Expand All @@ -1355,35 +1387,44 @@ impl PointerCoercion {
}
ty::adjustment::PointerCoercion::ArrayToPointer => PointerCoercion::ArrayToPointer,
ty::adjustment::PointerCoercion::Unsize => {
// We only support unsizing behind references, pointers and boxes for now.
let meta = match (src_ty.builtin_deref(true), tgt_ty.builtin_deref(true)) {
(Some(src_ty), Some(tgt_ty)) => {
let tcx = s.base().tcx;
let typing_env = s.typing_env();
let (src_ty, tgt_ty) =
tcx.struct_lockstep_tails_raw(src_ty, tgt_ty, |ty| {
normalize(tcx, typing_env, ty)
});
match tgt_ty.kind() {
ty::Slice(_) | ty::Str => match src_ty.kind() {
ty::Array(_, len) => {
let len = len.sinto(s);
UnsizingMetadata::Length(len)
}
_ => UnsizingMetadata::Unknown,
},
ty::Dynamic(preds, ..) => {
let pred = preds[0].with_self_ty(tcx, src_ty);
let clause = pred.as_trait_clause().expect(
"the first `ExistentialPredicate` of `TyKind::Dynamic` \
// TODO: to properly find out what field we want, we should use the query
// `coerce_unsized_info`, which we call recursively to get the list of fields
// to go into until we reach a pointer/reference.
// We should also pass this list of field IDs in the unsizing metadata.

let (Some(src_ty), Some(tgt_ty)) =
(src_ty.builtin_deref(true), tgt_ty.builtin_deref(true))
else {
return PointerCoercion::Unsize(UnsizingMetadata::Unknown);
};

let tcx = s.base().tcx;
let typing_env = s.typing_env();
let (src_ty, tgt_ty) = tcx
.struct_lockstep_tails_raw(src_ty, tgt_ty, |ty| normalize(tcx, typing_env, ty));

let meta = match (&src_ty.kind(), &tgt_ty.kind()) {
(ty::Array(_, len), ty::Slice(_)) => {
let len = len.sinto(s);
UnsizingMetadata::Length(len)
}
(ty::Dynamic(from_preds, _), ty::Dynamic(to_preds, ..)) => {
let to_pred = to_preds.principal().unwrap().with_self_ty(tcx, src_ty);
let impl_expr = resolve_for_dyn(s, from_preds, |searcher| {
searcher.resolve(&to_pred, &|_| {}).s_unwrap(s).sinto(s)
});
UnsizingMetadata::NestedVTable(impl_expr)
}
(_, ty::Dynamic(preds, ..)) => {
let pred = preds[0].with_self_ty(tcx, src_ty);
let clause = pred.as_trait_clause().expect(
"the first `ExistentialPredicate` of `TyKind::Dynamic` \
should be a trait clause",
);
let tref = clause.rebind(clause.skip_binder().trait_ref);
let impl_expr = solve_trait(s, tref);
UnsizingMetadata::VTablePtr(impl_expr)
}
_ => UnsizingMetadata::Unknown,
}
);
let tref = clause.rebind(clause.skip_binder().trait_ref);
let impl_expr = solve_trait(s, tref);

UnsizingMetadata::DirectVTable(impl_expr)
}
_ => UnsizingMetadata::Unknown,
};
Expand Down