Skip to content

Commit f5e53ec

Browse files
author
Lukas Markeffsky
committed
improve normalization of Pointee::Metadata
1 parent 5bd5d21 commit f5e53ec

File tree

5 files changed

+118
-40
lines changed

5 files changed

+118
-40
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1977,10 +1977,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
19771977

19781978
match in_elem.kind() {
19791979
ty::RawPtr(p) => {
1980-
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
1980+
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
19811981
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
19821982
});
1983-
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
19841983
require!(
19851984
metadata.is_unit(),
19861985
InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
@@ -1992,10 +1991,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
19921991
}
19931992
match out_elem.kind() {
19941993
ty::RawPtr(p) => {
1995-
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
1994+
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
19961995
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
19971996
});
1998-
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
19991997
require!(
20001998
metadata.is_unit(),
20011999
InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }

compiler/rustc_const_eval/src/interpret/terminator.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -372,14 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
372372
};
373373
if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
374374
// This is okay if they have the same metadata type.
375-
let meta_ty = |ty: Ty<'tcx>| {
376-
let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty);
377-
assert!(
378-
!only_if_sized,
379-
"there should be no more 'maybe has that metadata' types during interpretation"
380-
);
381-
meta
382-
};
375+
let meta_ty = |ty: Ty<'tcx>| ty.ptr_metadata_ty(*self.tcx, |ty| ty);
383376
return Ok(meta_ty(caller) == meta_ty(callee));
384377
}
385378

compiler/rustc_middle/src/ty/sty.rs

+31-15
Original file line numberDiff line numberDiff line change
@@ -2710,12 +2710,12 @@ impl<'tcx> Ty<'tcx> {
27102710
}
27112711

27122712
/// Returns the type of metadata for (potentially fat) pointers to this type,
2713-
/// and a boolean signifying if this is conditional on this type being `Sized`.
2714-
pub fn ptr_metadata_ty(
2713+
/// or the struct tail if the metadata type cannot be determinded.
2714+
pub fn ptr_metadata_ty_or_tail(
27152715
self,
27162716
tcx: TyCtxt<'tcx>,
27172717
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
2718-
) -> (Ty<'tcx>, bool) {
2718+
) -> Result<Ty<'tcx>, Ty<'tcx>> {
27192719
let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
27202720
match tail.kind() {
27212721
// Sized types
@@ -2739,29 +2739,45 @@ impl<'tcx> Ty<'tcx> {
27392739
| ty::Foreign(..)
27402740
// `dyn*` has no metadata
27412741
| ty::Dynamic(_, _, ty::DynStar)
2742-
// If returned by `struct_tail_without_normalization` this is a unit struct
2742+
// If returned by `struct_tail_with_normalize` this is a unit struct
27432743
// without any fields, or not a struct, and therefore is Sized.
27442744
| ty::Adt(..)
2745-
// If returned by `struct_tail_without_normalization` this is the empty tuple,
2745+
// If returned by `struct_tail_with_normalize` this is the empty tuple,
27462746
// a.k.a. unit type, which is Sized
2747-
| ty::Tuple(..) => (tcx.types.unit, false),
2747+
| ty::Tuple(..) => Ok(tcx.types.unit),
2748+
2749+
ty::Str | ty::Slice(_) => Ok(tcx.types.usize),
27482750

2749-
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
27502751
ty::Dynamic(_, _, ty::Dyn) => {
27512752
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
2752-
(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
2753-
},
2753+
Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]))
2754+
}
27542755

2755-
// type parameters only have unit metadata if they're sized, so return true
2756-
// to make sure we double check this during confirmation
2757-
ty::Param(_) | ty::Alias(..) => (tcx.types.unit, true),
2756+
// We don't know the metadata of `self`, but it must be equal to the
2757+
// metadata of `tail`.
2758+
ty::Param(_) | ty::Alias(..) => Err(tail),
27582759

27592760
ty::Infer(ty::TyVar(_))
27602761
| ty::Bound(..)
27612762
| ty::Placeholder(..)
2762-
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
2763-
bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
2764-
}
2763+
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(
2764+
"`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})"
2765+
),
2766+
}
2767+
}
2768+
2769+
/// Returns the type of metadata for (potentially fat) pointers to this type.
2770+
/// Causes an ICE if the metadata type cannot be determined.
2771+
pub fn ptr_metadata_ty(
2772+
self,
2773+
tcx: TyCtxt<'tcx>,
2774+
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
2775+
) -> Ty<'tcx> {
2776+
match self.ptr_metadata_ty_or_tail(tcx, normalize) {
2777+
Ok(metadata) => metadata,
2778+
Err(tail) => bug!(
2779+
"`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})"
2780+
),
27652781
}
27662782
}
27672783

compiler/rustc_trait_selection/src/traits/project.rs

+29-13
Original file line numberDiff line numberDiff line change
@@ -1919,7 +1919,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
19191919
// type parameters, opaques, and unnormalized projections have pointer
19201920
// metadata if they're known (e.g. by the param_env) to be sized
19211921
ty::Param(_) | ty::Alias(..)
1922-
if selcx.infcx.predicate_must_hold_modulo_regions(
1922+
if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
19231923
&obligation.with(
19241924
selcx.tcx(),
19251925
ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
@@ -2289,7 +2289,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
22892289
assert_eq!(metadata_def_id, item_def_id);
22902290

22912291
let mut obligations = Vec::new();
2292-
let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
2292+
let normalize = |ty| {
22932293
normalize_with_depth_to(
22942294
selcx,
22952295
obligation.param_env,
@@ -2298,17 +2298,33 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
22982298
ty,
22992299
&mut obligations,
23002300
)
2301-
});
2302-
if check_is_sized {
2303-
let sized_predicate = ty::TraitRef::from_lang_item(
2304-
tcx,
2305-
LangItem::Sized,
2306-
obligation.cause.span(),
2307-
[self_ty],
2308-
);
2309-
obligations.push(obligation.with(tcx, sized_predicate));
2310-
}
2311-
(metadata_ty.into(), obligations)
2301+
};
2302+
let metadata = match self_ty.ptr_metadata_ty_or_tail(tcx, normalize) {
2303+
Ok(metadata) => metadata,
2304+
Err(tail) => {
2305+
let sized_predicate = ty::TraitRef::from_lang_item(
2306+
tcx,
2307+
LangItem::Sized,
2308+
obligation.cause.span(),
2309+
[self_ty],
2310+
);
2311+
let sized_obligation = obligation.with(tcx, sized_predicate);
2312+
if self_ty == tail
2313+
|| selcx.infcx.predicate_must_hold_considering_regions(&sized_obligation)
2314+
{
2315+
// If the `self_ty` is `Sized`, then the metadata is `()`.
2316+
// We check this before projecting to the metadata of `tail`,
2317+
// because we may know `self_ty: Sized`, but not `tail: Sized`.
2318+
obligations.push(sized_obligation);
2319+
tcx.types.unit
2320+
} else {
2321+
// We know that `self_ty` has the same metadata as `tail`. This allows
2322+
// us to prove predicates like `Wrapper<T>::Metadata == T::Metadata`.
2323+
Ty::new_projection(tcx, metadata_def_id, [tail])
2324+
}
2325+
}
2326+
};
2327+
(metadata.into(), obligations)
23122328
} else {
23132329
bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
23142330
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// check-pass
2+
3+
#![feature(ptr_metadata)]
4+
5+
use std::ptr::{self, Pointee};
6+
7+
fn cast_same_meta<T: ?Sized, U: ?Sized>(ptr: *const T) -> *const U
8+
where
9+
T: Pointee<Metadata = <U as Pointee>::Metadata>,
10+
{
11+
let (thin, meta) = ptr.to_raw_parts();
12+
ptr::from_raw_parts(thin, meta)
13+
}
14+
15+
struct Wrapper<T: ?Sized>(T);
16+
17+
// if `Wrapper<T>: ?Sized` then normalize `Wrapper<T>::Metadata` -> `T::Metadata`
18+
fn wrapper_to_tail<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> {
19+
cast_same_meta(ptr)
20+
}
21+
22+
// if `Wrapper<T>: Sized` then normalize `Wrapper<T>::Metadata` -> `()`
23+
fn wrapper_to_unit<T: ?Sized>(ptr: *const ()) -> *const Wrapper<T>
24+
where
25+
Wrapper<T>: Sized,
26+
{
27+
cast_same_meta(ptr)
28+
}
29+
30+
trait Project {
31+
type Assoc: ?Sized;
32+
}
33+
34+
struct WrapperProject<T: ?Sized + Project>(T::Assoc);
35+
36+
// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata`
37+
fn wrapper_project<T: ?Sized + Project>(ptr: *const T::Assoc) -> *const WrapperProject<T> {
38+
cast_same_meta(ptr)
39+
}
40+
41+
// In this example, `WrapperProject<&'a T>` is `Sized` modulo regions,
42+
// but `?Sized` considering regions.
43+
// Normalize `WrapperProject<&'a T>::Metadata` -> `<&'a T as Project>::Assoc::Metadata`
44+
// and don't require `WrapperProject<&'a T>: Sized`.
45+
fn sized_modulo_regions<'a, T: ?Sized + 'static>(
46+
ptr: *const <&'a T as Project>::Assoc,
47+
) -> *const WrapperProject<&'a T>
48+
where
49+
for<'b> &'b T: Project,
50+
WrapperProject<&'static T>: Sized,
51+
{
52+
cast_same_meta(ptr)
53+
}
54+
55+
fn main() {}

0 commit comments

Comments
 (0)