Be cautious of calling upvar_tys before mir

This commit is contained in:
Aman Arora 2020-11-07 01:04:27 -05:00 committed by Roxane
parent 5da2bf197d
commit e35e46c113
2 changed files with 136 additions and 110 deletions

View file

@ -2105,118 +2105,148 @@ where
}
fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout {
let tcx = cx.tcx();
let tag_layout = |tag: &Scalar| -> C::TyAndLayout {
let layout = Layout::scalar(cx, tag.clone());
MaybeResult::from(Ok(TyAndLayout {
layout: tcx.intern_layout(layout),
ty: tag.value.to_ty(tcx),
}))
};
enum TyMaybeWithLayout<C: LayoutOf> {
Ty(C::Ty),
TyAndLayout(C::TyAndLayout),
}
cx.layout_of(match *this.ty.kind() {
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::FnPtr(_)
| ty::Never
| ty::FnDef(..)
| ty::GeneratorWitness(..)
| ty::Foreign(..)
| ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this),
fn ty_and_layout_kind<
C: LayoutOf<Ty = Ty<'tcx>, TyAndLayout: MaybeResult<TyAndLayout<'tcx>>>
+ HasTyCtxt<'tcx>
+ HasParamEnv<'tcx>,
>(
this: TyAndLayout<'tcx>,
cx: &C,
i: usize,
ty: C::Ty,
) -> TyMaybeWithLayout<C> {
let tcx = cx.tcx();
let tag_layout = |tag: &Scalar| -> C::TyAndLayout {
let layout = Layout::scalar(cx, tag.clone());
MaybeResult::from(Ok(TyAndLayout {
layout: tcx.intern_layout(layout),
ty: tag.value.to_ty(tcx),
}))
};
// Potentially-fat pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
assert!(i < this.fields.count());
match *ty.kind() {
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::FnPtr(_)
| ty::Never
| ty::FnDef(..)
| ty::GeneratorWitness(..)
| ty::Foreign(..)
| ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this),
// Reuse the fat `*T` type as its own thin pointer data field.
// This provides information about, e.g., DST struct pointees
// (which may have no non-DST form), and will work as long
// as the `Abi` or `FieldsShape` is checked by users.
if i == 0 {
let nil = tcx.mk_unit();
let ptr_ty = if this.ty.is_unsafe_ptr() {
tcx.mk_mut_ptr(nil)
} else {
tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
};
return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map(
|mut ptr_layout| {
ptr_layout.ty = this.ty;
ptr_layout
},
));
}
// Potentially-fat pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
assert!(i < this.fields.count());
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => tcx.types.usize,
ty::Dynamic(_, _) => {
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
/* FIXME: use actual fn pointers
Warning: naively computing the number of entries in the
vtable by counting the methods on the trait + methods on
all parent traits does not work, because some methods can
be not object safe and thus excluded from the vtable.
Increase this counter if you tried to implement this but
failed to do it without duplicating a lot of code from
other places in the compiler: 2
tcx.mk_tup(&[
tcx.mk_array(tcx.types.usize, 3),
tcx.mk_array(Option<fn()>),
])
*/
// Reuse the fat `*T` type as its own thin pointer data field.
// This provides information about, e.g., DST struct pointees
// (which may have no non-DST form), and will work as long
// as the `Abi` or `FieldsShape` is checked by users.
if i == 0 {
let nil = tcx.mk_unit();
let ptr_ty = if ty.is_unsafe_ptr() {
tcx.mk_mut_ptr(nil)
} else {
tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
};
return TyMaybeWithLayout::TyAndLayout(MaybeResult::from(
cx.layout_of(ptr_ty).to_result().map(|mut ptr_layout| {
ptr_layout.ty = ty;
ptr_layout
}),
));
}
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize),
ty::Dynamic(_, _) => {
TyMaybeWithLayout::Ty(tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.mk_array(tcx.types.usize, 3),
))
/* FIXME: use actual fn pointers
Warning: naively computing the number of entries in the
vtable by counting the methods on the trait + methods on
all parent traits does not work, because some methods can
be not object safe and thus excluded from the vtable.
Increase this counter if you tried to implement this but
failed to do it without duplicating a lot of code from
other places in the compiler: 2
tcx.mk_tup(&[
tcx.mk_array(tcx.types.usize, 3),
tcx.mk_array(Option<fn()>),
])
*/
}
_ => bug!("TyAndLayout::field_type({:?}): not applicable", this),
}
_ => bug!("TyAndLayout::field_type({:?}): not applicable", this),
}
// Arrays and slices.
ty::Array(element, _) | ty::Slice(element) => TyMaybeWithLayout::Ty(element),
ty::Str => TyMaybeWithLayout::Ty(tcx.types.u8),
// Tuples, generators and closures.
ty::Closure(_, ref substs) => {
ty_and_layout_kind(this, cx, i, substs.as_closure().tupled_upvars_ty())
}
ty::Generator(def_id, ref substs, _) => match this.variants {
Variants::Single { index } => TyMaybeWithLayout::Ty(
substs
.as_generator()
.state_tys(def_id, tcx)
.nth(index.as_usize())
.unwrap()
.nth(i)
.unwrap(),
),
Variants::Multiple { ref tag, tag_field, .. } => {
if i == tag_field {
return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
}
TyMaybeWithLayout::Ty(substs.as_generator().prefix_tys().nth(i).unwrap())
}
},
ty::Tuple(tys) => TyMaybeWithLayout::Ty(tys[i].expect_ty()),
// ADTs.
ty::Adt(def, substs) => {
match this.variants {
Variants::Single { index } => {
TyMaybeWithLayout::Ty(def.variants[index].fields[i].ty(tcx, substs))
}
// Discriminant field for enums (where applicable).
Variants::Multiple { ref tag, .. } => {
assert_eq!(i, 0);
return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
}
}
}
ty::Projection(_)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Opaque(..)
| ty::Param(_)
| ty::Infer(_)
| ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty),
}
}
// Arrays and slices.
ty::Array(element, _) | ty::Slice(element) => element,
ty::Str => tcx.types.u8,
// Tuples, generators and closures.
ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(),
ty::Generator(def_id, ref substs, _) => match this.variants {
Variants::Single { index } => substs
.as_generator()
.state_tys(def_id, tcx)
.nth(index.as_usize())
.unwrap()
.nth(i)
.unwrap(),
Variants::Multiple { ref tag, tag_field, .. } => {
if i == tag_field {
return tag_layout(tag);
}
substs.as_generator().prefix_tys().nth(i).unwrap()
}
},
ty::Tuple(tys) => tys[i].expect_ty(),
// ADTs.
ty::Adt(def, substs) => {
match this.variants {
Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs),
// Discriminant field for enums (where applicable).
Variants::Multiple { ref tag, .. } => {
assert_eq!(i, 0);
return tag_layout(tag);
}
}
}
ty::Projection(_)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Opaque(..)
| ty::Param(_)
| ty::Infer(_)
| ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty),
cx.layout_of(match ty_and_layout_kind(this, cx, i, this.ty) {
TyMaybeWithLayout::Ty(result) => result,
TyMaybeWithLayout::TyAndLayout(result) => return result,
})
}

View file

@ -94,16 +94,12 @@ where
_ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (),
ty::Closure(_, substs) => {
for upvar_ty in substs.as_closure().upvar_tys() {
queue_type(self, upvar_ty);
}
queue_type(self, substs.as_closure().tupled_upvars_ty());
}
ty::Generator(def_id, substs, _) => {
let substs = substs.as_generator();
for upvar_ty in substs.upvar_tys() {
queue_type(self, upvar_ty);
}
queue_type(self, substs.tupled_upvars_ty());
let witness = substs.witness();
let interior_tys = match witness.kind() {