Skip to content

Commit 8f171c4

Browse files
committed
Replace struct_tail and struct_lockstep_tails with variants handling normalization.
The old struct tail functions did not deal with `<T as Trait>::A` and `impl Trait`, at least not explicitly. (We didn't notice this bug before because it is only exposed when the tail (post deep normalization) is not `Sized`, so it was a rare case to deal with.) For post type-checking (i.e. during codegen), there is now `struct_tail_erasing_lifetimes` and `struct_lockstep_tails_erasing_lifetimes`, which each take an additional `ParamEnv` argument to drive normalization. For pre type-checking cases where normalization is not needed, there is `struct_tail_without_normalization`. (Currently, the only instance of this is `Expectation::rvalue_hint`.) All of these new entrypoints work by calling out to common helper routines. The helpers are parameterized over a closure that handles the normalization.
1 parent 0324a2b commit 8f171c4

File tree

10 files changed

+111
-22
lines changed

10 files changed

+111
-22
lines changed

src/librustc/ty/layout.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
543543
return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr)));
544544
}
545545

546-
let unsized_part = tcx.struct_tail(pointee);
546+
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
547547
let metadata = match unsized_part.sty {
548548
ty::Foreign(..) => {
549549
return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr)));
@@ -1664,7 +1664,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
16641664
ty::Ref(_, pointee, _) |
16651665
ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
16661666
let non_zero = !ty.is_unsafe_ptr();
1667-
let tail = tcx.struct_tail(pointee);
1667+
let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
16681668
match tail.sty {
16691669
ty::Param(_) | ty::Projection(_) => {
16701670
debug_assert!(tail.has_param_types() || tail.has_self_ty());
@@ -2015,7 +2015,7 @@ where
20152015
}));
20162016
}
20172017

2018-
match tcx.struct_tail(pointee).sty {
2018+
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).sty {
20192019
ty::Slice(_) |
20202020
ty::Str => tcx.types.usize,
20212021
ty::Dynamic(_, _) => {

src/librustc/ty/util.rs

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,42 @@ impl<'tcx> TyCtxt<'tcx> {
258258
false
259259
}
260260

261-
/// Returns the deeply last field of nested structures, or the same type,
262-
/// if not a structure at all. Corresponds to the only possible unsized
263-
/// field, and its type can be used to determine unsizing strategy.
264-
pub fn struct_tail(self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
261+
/// Attempts to returns the deeply last field of nested structures, but
262+
/// does not apply any normalization in its search. Returns the same type
263+
/// if input `ty` is not a structure at all.
264+
pub fn struct_tail_without_normalization(self, ty: Ty<'tcx>) -> Ty<'tcx>
265+
{
266+
let tcx = self;
267+
tcx.struct_tail_with_normalize(ty, |ty| ty)
268+
}
269+
270+
/// Returns the deeply last field of nested structures, or the same type if
271+
/// not a structure at all. Corresponds to the only possible unsized field,
272+
/// and its type can be used to determine unsizing strategy.
273+
pub fn struct_tail_erasing_lifetimes(self,
274+
ty: Ty<'tcx>,
275+
param_env: ty::ParamEnv<'tcx>)
276+
-> Ty<'tcx>
277+
{
278+
let tcx = self;
279+
tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty))
280+
}
281+
282+
/// Returns the deeply last field of nested structures, or the same type if
283+
/// not a structure at all. Corresponds to the only possible unsized field,
284+
/// and its type can be used to determine unsizing strategy.
285+
///
286+
/// This is parameterized over the normalization strategy (i.e. how to
287+
/// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
288+
/// function to indicate no normalization should take place.
289+
///
290+
/// See also `struct_tail_erasing_lifetimes`, which is what callers running
291+
/// after type checking should use.
292+
pub fn struct_tail_with_normalize(self,
293+
mut ty: Ty<'tcx>,
294+
normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>)
295+
-> Ty<'tcx>
296+
{
265297
loop {
266298
match ty.sty {
267299
ty::Adt(def, substs) => {
@@ -282,6 +314,15 @@ impl<'tcx> TyCtxt<'tcx> {
282314
}
283315
}
284316

317+
ty::Projection(_) | ty::Opaque(..) => {
318+
let normalized = normalize(ty);
319+
if ty == normalized {
320+
return ty;
321+
} else {
322+
ty = normalized;
323+
}
324+
}
325+
285326
_ => {
286327
break;
287328
}
@@ -295,10 +336,34 @@ impl<'tcx> TyCtxt<'tcx> {
295336
/// structure definitions.
296337
/// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
297338
/// whereas struct_tail produces `T`, and `Trait`, respectively.
298-
pub fn struct_lockstep_tails(self,
299-
source: Ty<'tcx>,
300-
target: Ty<'tcx>)
301-
-> (Ty<'tcx>, Ty<'tcx>) {
339+
///
340+
/// Must only be called after type-checking is complete; otherwise
341+
/// normalization attempt may cause compiler bugs.
342+
pub fn struct_lockstep_tails_erasing_lifetimes(self,
343+
source: Ty<'tcx>,
344+
target: Ty<'tcx>,
345+
param_env: ty::ParamEnv<'tcx>)
346+
-> (Ty<'tcx>, Ty<'tcx>)
347+
{
348+
let tcx = self;
349+
tcx.struct_lockstep_tails_with_normalize(
350+
source, target, |ty| tcx.normalize_erasing_regions(param_env, ty))
351+
}
352+
353+
/// Same as applying struct_tail on `source` and `target`, but only
354+
/// keeps going as long as the two types are instances of the same
355+
/// structure definitions.
356+
/// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
357+
/// whereas struct_tail produces `T`, and `Trait`, respectively.
358+
///
359+
/// See also struct_lockstep_tails_erasing_lifetimes, which
360+
/// is what callers running after type checking should use.
361+
pub fn struct_lockstep_tails_with_normalize(self,
362+
source: Ty<'tcx>,
363+
target: Ty<'tcx>,
364+
normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>)
365+
-> (Ty<'tcx>, Ty<'tcx>)
366+
{
302367
let (mut a, mut b) = (source, target);
303368
loop {
304369
match (&a.sty, &b.sty) {
@@ -320,6 +385,22 @@ impl<'tcx> TyCtxt<'tcx> {
320385
break;
321386
}
322387
},
388+
(ty::Projection(_), _) | (ty::Opaque(..), _) |
389+
(_, ty::Projection(_)) | (_, ty::Opaque(..)) => {
390+
// If either side is a projection, attempt to
391+
// progress via normalization. (Should be safe to
392+
// apply to both sides as normalization is
393+
// idempotent.)
394+
let a_norm = normalize(a);
395+
let b_norm = normalize(b);
396+
if a == a_norm && b == b_norm {
397+
break;
398+
} else {
399+
a = a_norm;
400+
b = b_norm;
401+
}
402+
}
403+
323404
_ => break,
324405
}
325406
}

src/librustc_codegen_ssa/base.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
128128
target: Ty<'tcx>,
129129
old_info: Option<Cx::Value>,
130130
) -> Cx::Value {
131-
let (source, target) = cx.tcx().struct_lockstep_tails(source, target);
131+
let (source, target) =
132+
cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env());
132133
match (&source.sty, &target.sty) {
133134
(&ty::Array(_, len), &ty::Slice(_)) => {
134135
cx.const_usize(len.unwrap_usize(cx.tcx()))

src/librustc_codegen_ssa/traits/type_.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,12 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {
7777
}
7878

7979
fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool {
80-
if ty.is_sized(self.tcx().at(DUMMY_SP), ty::ParamEnv::reveal_all()) {
80+
let param_env = ty::ParamEnv::reveal_all();
81+
if ty.is_sized(self.tcx().at(DUMMY_SP), param_env) {
8182
return false;
8283
}
8384

84-
let tail = self.tcx().struct_tail(ty);
85+
let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env);
8586
match tail.sty {
8687
ty::Foreign(..) => false,
8788
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,

src/librustc_mir/interpret/cast.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
270270
dty: Ty<'tcx>,
271271
) -> InterpResult<'tcx> {
272272
// A<Struct> -> A<Trait> conversion
273-
let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty);
273+
let (src_pointee_ty, dest_pointee_ty) =
274+
self.tcx.struct_lockstep_tails_erasing_lifetimes(sty, dty, self.param_env);
274275

275276
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
276277
(&ty::Array(_, length), &ty::Slice(_)) => {

src/librustc_mir/interpret/intern.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ for
146146
let value = self.ecx.read_immediate(mplace.into())?;
147147
// Handle trait object vtables
148148
if let Ok(meta) = value.to_meta() {
149-
if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(referenced_ty).sty {
149+
if let ty::Dynamic(..) =
150+
self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.param_env).sty
151+
{
150152
if let Ok(vtable) = meta.unwrap().to_ptr() {
151153
// explitly choose `Immutable` here, since vtables are immutable, even
152154
// if the reference of the fat pointer is mutable

src/librustc_mir/interpret/validity.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
361361
"uninitialized data in fat pointer metadata", self.path);
362362
let layout = self.ecx.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
363363
if layout.is_unsized() {
364-
let tail = self.ecx.tcx.struct_tail(layout.ty);
364+
let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(layout.ty,
365+
self.ecx.param_env);
365366
match tail.sty {
366367
ty::Dynamic(..) => {
367368
let vtable = meta.unwrap();

src/librustc_mir/monomorphize/collector.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,12 +851,13 @@ fn find_vtable_types_for_unsizing<'tcx>(
851851
target_ty: Ty<'tcx>,
852852
) -> (Ty<'tcx>, Ty<'tcx>) {
853853
let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| {
854+
let param_env = ty::ParamEnv::reveal_all();
854855
let type_has_metadata = |ty: Ty<'tcx>| -> bool {
855856
use syntax_pos::DUMMY_SP;
856-
if ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) {
857+
if ty.is_sized(tcx.at(DUMMY_SP), param_env) {
857858
return false;
858859
}
859-
let tail = tcx.struct_tail(ty);
860+
let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env);
860861
match tail.sty {
861862
ty::Foreign(..) => false,
862863
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
@@ -866,7 +867,7 @@ fn find_vtable_types_for_unsizing<'tcx>(
866867
if type_has_metadata(inner_source) {
867868
(inner_source, inner_target)
868869
} else {
869-
tcx.struct_lockstep_tails(inner_source, inner_target)
870+
tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env)
870871
}
871872
};
872873

src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ impl<'a, 'tcx> Expectation<'tcx> {
315315
/// See the test case `test/run-pass/coerce-expect-unsized.rs` and #20169
316316
/// for examples of where this comes up,.
317317
fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
318-
match fcx.tcx.struct_tail(ty).sty {
318+
match fcx.tcx.struct_tail_without_normalization(ty).sty {
319319
ty::Slice(_) | ty::Str | ty::Dynamic(..) => {
320320
ExpectRvalueLikeUnsized(ty)
321321
}

src/librustc_typeck/check/wfcheck.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ fn check_item_type(
366366

367367
let mut forbid_unsized = true;
368368
if allow_foreign_ty {
369-
if let ty::Foreign(_) = fcx.tcx.struct_tail(item_ty).sty {
369+
let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env);
370+
if let ty::Foreign(_) = tail.sty {
370371
forbid_unsized = false;
371372
}
372373
}

0 commit comments

Comments
 (0)