old solver: improve normalization of Pointee::Metadata
This commit is contained in:
parent
77fb540684
commit
0c1f401d98
5 changed files with 113 additions and 39 deletions
|
@ -1985,10 +1985,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
||||||
|
|
||||||
match in_elem.kind() {
|
match in_elem.kind() {
|
||||||
ty::RawPtr(p) => {
|
ty::RawPtr(p) => {
|
||||||
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
|
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
|
||||||
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
|
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
|
||||||
});
|
});
|
||||||
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
|
|
||||||
require!(
|
require!(
|
||||||
metadata.is_unit(),
|
metadata.is_unit(),
|
||||||
InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
|
InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
|
||||||
|
@ -2000,10 +1999,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
||||||
}
|
}
|
||||||
match out_elem.kind() {
|
match out_elem.kind() {
|
||||||
ty::RawPtr(p) => {
|
ty::RawPtr(p) => {
|
||||||
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
|
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
|
||||||
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
|
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
|
||||||
});
|
});
|
||||||
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
|
|
||||||
require!(
|
require!(
|
||||||
metadata.is_unit(),
|
metadata.is_unit(),
|
||||||
InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }
|
InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }
|
||||||
|
|
|
@ -377,12 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// to fields, which can yield non-normalized types. So we need to provide a
|
// to fields, which can yield non-normalized types. So we need to provide a
|
||||||
// normalization function.
|
// normalization function.
|
||||||
let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
|
let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
|
||||||
let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, normalize);
|
ty.ptr_metadata_ty(*self.tcx, normalize)
|
||||||
assert!(
|
|
||||||
!only_if_sized,
|
|
||||||
"there should be no more 'maybe has that metadata' types during interpretation"
|
|
||||||
);
|
|
||||||
meta
|
|
||||||
};
|
};
|
||||||
return Ok(meta_ty(caller) == meta_ty(callee));
|
return Ok(meta_ty(caller) == meta_ty(callee));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1957,12 +1957,12 @@ impl<'tcx> Ty<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type of metadata for (potentially fat) pointers to this type,
|
/// Returns the type of metadata for (potentially fat) pointers to this type,
|
||||||
/// and a boolean signifying if this is conditional on this type being `Sized`.
|
/// or the struct tail if the metadata type cannot be determined.
|
||||||
pub fn ptr_metadata_ty(
|
pub fn ptr_metadata_ty_or_tail(
|
||||||
self,
|
self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
|
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
|
||||||
) -> (Ty<'tcx>, bool) {
|
) -> Result<Ty<'tcx>, Ty<'tcx>> {
|
||||||
let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
|
let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
|
||||||
match tail.kind() {
|
match tail.kind() {
|
||||||
// Sized types
|
// Sized types
|
||||||
|
@ -1984,31 +1984,47 @@ impl<'tcx> Ty<'tcx> {
|
||||||
| ty::Error(_)
|
| ty::Error(_)
|
||||||
// Extern types have metadata = ().
|
// Extern types have metadata = ().
|
||||||
| ty::Foreign(..)
|
| ty::Foreign(..)
|
||||||
// `dyn*` has no metadata
|
// `dyn*` has metadata = ().
|
||||||
| ty::Dynamic(_, _, ty::DynStar)
|
| ty::Dynamic(_, _, ty::DynStar)
|
||||||
// If returned by `struct_tail_without_normalization` this is a unit struct
|
// If returned by `struct_tail_with_normalize` this is a unit struct
|
||||||
// without any fields, or not a struct, and therefore is Sized.
|
// without any fields, or not a struct, and therefore is Sized.
|
||||||
| ty::Adt(..)
|
| ty::Adt(..)
|
||||||
// If returned by `struct_tail_without_normalization` this is the empty tuple,
|
// If returned by `struct_tail_with_normalize` this is the empty tuple,
|
||||||
// a.k.a. unit type, which is Sized
|
// a.k.a. unit type, which is Sized
|
||||||
| ty::Tuple(..) => (tcx.types.unit, false),
|
| ty::Tuple(..) => Ok(tcx.types.unit),
|
||||||
|
|
||||||
|
ty::Str | ty::Slice(_) => Ok(tcx.types.usize),
|
||||||
|
|
||||||
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
|
|
||||||
ty::Dynamic(_, _, ty::Dyn) => {
|
ty::Dynamic(_, _, ty::Dyn) => {
|
||||||
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
|
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
|
||||||
(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
|
Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]))
|
||||||
},
|
}
|
||||||
|
|
||||||
// type parameters only have unit metadata if they're sized, so return true
|
// We don't know the metadata of `self`, but it must be equal to the
|
||||||
// to make sure we double check this during confirmation
|
// metadata of `tail`.
|
||||||
ty::Param(_) | ty::Alias(..) => (tcx.types.unit, true),
|
ty::Param(_) | ty::Alias(..) => Err(tail),
|
||||||
|
|
||||||
ty::Infer(ty::TyVar(_))
|
ty::Infer(ty::TyVar(_))
|
||||||
| ty::Bound(..)
|
| ty::Bound(..)
|
||||||
| ty::Placeholder(..)
|
| ty::Placeholder(..)
|
||||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(
|
||||||
bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
|
"`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})"
|
||||||
}
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the type of metadata for (potentially fat) pointers to this type.
|
||||||
|
/// Causes an ICE if the metadata type cannot be determined.
|
||||||
|
pub fn ptr_metadata_ty(
|
||||||
|
self,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
|
||||||
|
) -> Ty<'tcx> {
|
||||||
|
match self.ptr_metadata_ty_or_tail(tcx, normalize) {
|
||||||
|
Ok(metadata) => metadata,
|
||||||
|
Err(tail) => bug!(
|
||||||
|
"`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1916,10 +1916,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||||
// Integers and floats are always Sized, and so have unit type metadata.
|
// Integers and floats are always Sized, and so have unit type metadata.
|
||||||
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
|
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
|
||||||
|
|
||||||
// type parameters, opaques, and unnormalized projections have pointer
|
// We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
|
||||||
// metadata if they're known (e.g. by the param_env) to be sized
|
// Otherwise, type parameters, opaques, and unnormalized projections have
|
||||||
|
// unit metadata if they're known (e.g. by the param_env) to be sized.
|
||||||
ty::Param(_) | ty::Alias(..)
|
ty::Param(_) | ty::Alias(..)
|
||||||
if selcx.infcx.predicate_must_hold_modulo_regions(
|
if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
|
||||||
&obligation.with(
|
&obligation.with(
|
||||||
selcx.tcx(),
|
selcx.tcx(),
|
||||||
ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
|
ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
|
||||||
|
@ -2289,7 +2290,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
|
||||||
assert_eq!(metadata_def_id, item_def_id);
|
assert_eq!(metadata_def_id, item_def_id);
|
||||||
|
|
||||||
let mut obligations = Vec::new();
|
let mut obligations = Vec::new();
|
||||||
let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
|
let normalize = |ty| {
|
||||||
normalize_with_depth_to(
|
normalize_with_depth_to(
|
||||||
selcx,
|
selcx,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
|
@ -2298,16 +2299,26 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
|
||||||
ty,
|
ty,
|
||||||
&mut obligations,
|
&mut obligations,
|
||||||
)
|
)
|
||||||
|
};
|
||||||
|
let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| {
|
||||||
|
if tail == self_ty {
|
||||||
|
// This is the fallback case for type parameters, unnormalizable projections
|
||||||
|
// and opaque types.
|
||||||
|
// If the `self_ty` is `Sized`, then the metadata is `()`.
|
||||||
|
let sized_predicate = ty::TraitRef::from_lang_item(
|
||||||
|
tcx,
|
||||||
|
LangItem::Sized,
|
||||||
|
obligation.cause.span(),
|
||||||
|
[self_ty],
|
||||||
|
);
|
||||||
|
obligations.push(obligation.with(tcx, sized_predicate));
|
||||||
|
tcx.types.unit
|
||||||
|
} else {
|
||||||
|
// We know that `self_ty` has the same metadata as `tail`. This allows us
|
||||||
|
// to prove predicates like `Wrapper<Tail>::Metadata == Tail::Metadata`.
|
||||||
|
Ty::new_projection(tcx, metadata_def_id, [tail])
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if check_is_sized {
|
|
||||||
let sized_predicate = ty::TraitRef::from_lang_item(
|
|
||||||
tcx,
|
|
||||||
LangItem::Sized,
|
|
||||||
obligation.cause.span(),
|
|
||||||
[self_ty],
|
|
||||||
);
|
|
||||||
obligations.push(obligation.with(tcx, sized_predicate));
|
|
||||||
}
|
|
||||||
(metadata_ty.into(), obligations)
|
(metadata_ty.into(), obligations)
|
||||||
} else {
|
} else {
|
||||||
bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
|
bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
|
||||||
|
|
54
tests/ui/traits/pointee-normalize-equate.rs
Normal file
54
tests/ui/traits/pointee-normalize-equate.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(ptr_metadata)]
|
||||||
|
|
||||||
|
use std::ptr::{self, Pointee};
|
||||||
|
|
||||||
|
fn cast_same_meta<T: ?Sized, U: ?Sized>(ptr: *const T) -> *const U
|
||||||
|
where
|
||||||
|
T: Pointee<Metadata = <U as Pointee>::Metadata>,
|
||||||
|
{
|
||||||
|
let (thin, meta) = ptr.to_raw_parts();
|
||||||
|
ptr::from_raw_parts(thin, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wrapper<T: ?Sized>(T);
|
||||||
|
|
||||||
|
// normalize `Wrapper<T>::Metadata` -> `T::Metadata`
|
||||||
|
fn wrapper_to_tail<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> {
|
||||||
|
cast_same_meta(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize `Wrapper<T>::Metadata` -> `T::Metadata` -> `()`
|
||||||
|
fn wrapper_to_unit<T>(ptr: *const ()) -> *const Wrapper<T> {
|
||||||
|
cast_same_meta(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Project {
|
||||||
|
type Assoc: ?Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WrapperProject<T: ?Sized + Project>(T::Assoc);
|
||||||
|
|
||||||
|
// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata`
|
||||||
|
fn wrapper_project_tail<T: ?Sized + Project>(ptr: *const T::Assoc) -> *const WrapperProject<T> {
|
||||||
|
cast_same_meta(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata` -> `()`
|
||||||
|
fn wrapper_project_unit<T: ?Sized + Project>(ptr: *const ()) -> *const WrapperProject<T>
|
||||||
|
where
|
||||||
|
T::Assoc: Sized,
|
||||||
|
{
|
||||||
|
cast_same_meta(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize `<[T] as Pointee>::Metadata` -> `usize`, even if `[T]: Sized`
|
||||||
|
fn sized_slice<T>(ptr: *const [T]) -> *const str
|
||||||
|
where
|
||||||
|
[T]: Sized,
|
||||||
|
{
|
||||||
|
cast_same_meta(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue