Skip to content

Commit 47b4b34

Browse files
authored
Unrolled build for rust-lang#120354
Rollup merge of rust-lang#120354 - lukas-code:metadata-normalize, r=lcnr improve normalization of `Pointee::Metadata` This PR makes it so that `<Wrapper<Tail> as Pointee>::Metadata` is normalized to `<Tail as Pointee>::Metadata` if we don't know `Wrapper<Tail>: Sized`. With that, the trait solver can prove projection predicates like `<Wrapper<Tail> as Pointee>::Metadata == <Tail as Pointee>::Metadata`, which makes it possible to use the metadata APIs to cast between the tail and the wrapper: ```rust #![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); fn cast_to_wrapper<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> { cast_same_meta(ptr) } ``` Previously, this failed to compile: ``` error[E0271]: type mismatch resolving `<Wrapper<T> as Pointee>::Metadata == <T as Pointee>::Metadata` --> src/lib.rs:16:5 | 15 | fn cast_to_wrapper<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> { | - found this type parameter 16 | cast_same_meta(ptr) | ^^^^^^^^^^^^^^ expected `Wrapper<T>`, found type parameter `T` | = note: expected associated type `<Wrapper<T> as Pointee>::Metadata` found associated type `<T as Pointee>::Metadata` = note: an associated type was expected, but a different one was found ``` (Yes, you can already do this with `as` casts. But using functions is so much :sparkles: *safer* :sparkles:, because you can't change the metadata on accident.) --- This PR essentially changes the built-in impls of `Pointee` from this: ```rust // before impl Pointee for u8 { type Metadata = (); } impl Pointee for [u8] { type Metadata = usize; } // ... impl Pointee for Wrapper<u8> { type Metadata = (); } impl Pointee for Wrapper<[u8]> { type Metadata = usize; } // ... // This impl is only selected if `T` is a type parameter or unnormalizable projection or opaque type. fallback impl<T: ?Sized> Pointee for Wrapper<T> where Wrapper<T>: Sized { type Metadata = (); } // This impl is only selected if `T` is a type parameter or unnormalizable projection or opaque type. fallback impl<T /*: Sized */> Pointee for T { type Metadata = (); } ``` to this: ```rust // after impl Pointee for u8 { type Metadata = (); } impl Pointee for [u8] { type Metadata = usize; } // ... impl<T: ?Sized> Pointee for Wrapper<T> { // in the old solver this will instead project to the "deep" tail directly, // e.g. `Wrapper<Wrapper<T>>::Metadata = T::Metadata` type Metadata = <T as Pointee>::Metadata; } // ... // This impl is only selected if `T` is a type parameter or unnormalizable projection or opaque type. fallback impl<T /*: Sized */> Pointee for T { type Metadata = (); } ```
2 parents f4cfd87 + 15ffe83 commit 47b4b34

File tree

7 files changed

+148
-59
lines changed

7 files changed

+148
-59
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

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

19861986
match in_elem.kind() {
19871987
ty::RawPtr(p) => {
1988-
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
1988+
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
19891989
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
19901990
});
1991-
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
19921991
require!(
19931992
metadata.is_unit(),
19941993
InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
@@ -2000,10 +1999,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
20001999
}
20012000
match out_elem.kind() {
20022001
ty::RawPtr(p) => {
2003-
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
2002+
let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
20042003
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
20052004
});
2006-
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
20072005
require!(
20082006
metadata.is_unit(),
20092007
InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }

compiler/rustc_const_eval/src/interpret/terminator.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -377,12 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
377377
// to fields, which can yield non-normalized types. So we need to provide a
378378
// normalization function.
379379
let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
380-
let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, normalize);
381-
assert!(
382-
!only_if_sized,
383-
"there should be no more 'maybe has that metadata' types during interpretation"
384-
);
385-
meta
380+
ty.ptr_metadata_ty(*self.tcx, normalize)
386381
};
387382
return Ok(meta_ty(caller) == meta_ty(callee));
388383
}

compiler/rustc_middle/src/ty/sty.rs

+32-16
Original file line numberDiff line numberDiff line change
@@ -2279,12 +2279,12 @@ impl<'tcx> Ty<'tcx> {
22792279
}
22802280

22812281
/// Returns the type of metadata for (potentially fat) pointers to this type,
2282-
/// and a boolean signifying if this is conditional on this type being `Sized`.
2283-
pub fn ptr_metadata_ty(
2282+
/// or the struct tail if the metadata type cannot be determined.
2283+
pub fn ptr_metadata_ty_or_tail(
22842284
self,
22852285
tcx: TyCtxt<'tcx>,
22862286
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
2287-
) -> (Ty<'tcx>, bool) {
2287+
) -> Result<Ty<'tcx>, Ty<'tcx>> {
22882288
let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
22892289
match tail.kind() {
22902290
// Sized types
@@ -2307,31 +2307,47 @@ impl<'tcx> Ty<'tcx> {
23072307
| ty::Error(_)
23082308
// Extern types have metadata = ().
23092309
| ty::Foreign(..)
2310-
// `dyn*` has no metadata
2310+
// `dyn*` has metadata = ().
23112311
| ty::Dynamic(_, _, ty::DynStar)
2312-
// If returned by `struct_tail_without_normalization` this is a unit struct
2312+
// If returned by `struct_tail_with_normalize` this is a unit struct
23132313
// without any fields, or not a struct, and therefore is Sized.
23142314
| ty::Adt(..)
2315-
// If returned by `struct_tail_without_normalization` this is the empty tuple,
2315+
// If returned by `struct_tail_with_normalize` this is the empty tuple,
23162316
// a.k.a. unit type, which is Sized
2317-
| ty::Tuple(..) => (tcx.types.unit, false),
2317+
| ty::Tuple(..) => Ok(tcx.types.unit),
2318+
2319+
ty::Str | ty::Slice(_) => Ok(tcx.types.usize),
23182320

2319-
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
23202321
ty::Dynamic(_, _, ty::Dyn) => {
23212322
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
2322-
(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
2323-
},
2323+
Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]))
2324+
}
23242325

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

23292330
ty::Infer(ty::TyVar(_))
23302331
| ty::Bound(..)
23312332
| ty::Placeholder(..)
2332-
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
2333-
bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
2334-
}
2333+
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(
2334+
"`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})"
2335+
),
2336+
}
2337+
}
2338+
2339+
/// Returns the type of metadata for (potentially fat) pointers to this type.
2340+
/// Causes an ICE if the metadata type cannot be determined.
2341+
pub fn ptr_metadata_ty(
2342+
self,
2343+
tcx: TyCtxt<'tcx>,
2344+
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
2345+
) -> Ty<'tcx> {
2346+
match self.ptr_metadata_ty_or_tail(tcx, normalize) {
2347+
Ok(metadata) => metadata,
2348+
Err(tail) => bug!(
2349+
"`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})"
2350+
),
23352351
}
23362352
}
23372353

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+10-19
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
491491
goal: Goal<'tcx, Self>,
492492
) -> QueryResult<'tcx> {
493493
let tcx = ecx.tcx();
494+
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
495+
assert_eq!(metadata_def_id, goal.predicate.def_id());
494496
ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
495497
let metadata_ty = match goal.predicate.self_ty().kind() {
496498
ty::Bool
@@ -522,7 +524,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
522524
}
523525

524526
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
525-
// FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
527+
// This is the "fallback impl" for type parameters, unnormalizable projections
528+
// and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
529+
// FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
530+
// exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
526531
let sized_predicate = ty::TraitRef::from_lang_item(
527532
tcx,
528533
LangItem::Sized,
@@ -536,30 +541,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
536541

537542
ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
538543
None => tcx.types.unit,
539-
Some(field_def) => {
540-
let self_ty = field_def.ty(tcx, args);
541-
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
542-
ecx.add_goal(
543-
GoalSource::Misc,
544-
goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
545-
);
546-
return ecx
547-
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
544+
Some(tail_def) => {
545+
let tail_ty = tail_def.ty(tcx, args);
546+
Ty::new_projection(tcx, metadata_def_id, [tail_ty])
548547
}
549548
},
550549
ty::Adt(_, _) => tcx.types.unit,
551550

552551
ty::Tuple(elements) => match elements.last() {
553552
None => tcx.types.unit,
554-
Some(&self_ty) => {
555-
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
556-
ecx.add_goal(
557-
GoalSource::Misc,
558-
goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
559-
);
560-
return ecx
561-
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
562-
}
553+
Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
563554
},
564555

565556
ty::Infer(

compiler/rustc_trait_selection/src/traits/project.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -1935,10 +1935,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
19351935
// Integers and floats are always Sized, and so have unit type metadata.
19361936
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
19371937

1938-
// type parameters, opaques, and unnormalized projections have pointer
1939-
// metadata if they're known (e.g. by the param_env) to be sized
1938+
// We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
1939+
// Otherwise, type parameters, opaques, and unnormalized projections have
1940+
// unit metadata if they're known (e.g. by the param_env) to be sized.
19401941
ty::Param(_) | ty::Alias(..)
1941-
if selcx.infcx.predicate_must_hold_modulo_regions(
1942+
if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
19421943
&obligation.with(
19431944
selcx.tcx(),
19441945
ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
@@ -2312,7 +2313,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
23122313
assert_eq!(metadata_def_id, item_def_id);
23132314

23142315
let mut obligations = Vec::new();
2315-
let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
2316+
let normalize = |ty| {
23162317
normalize_with_depth_to(
23172318
selcx,
23182319
obligation.param_env,
@@ -2321,16 +2322,27 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
23212322
ty,
23222323
&mut obligations,
23232324
)
2325+
};
2326+
let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| {
2327+
if tail == self_ty {
2328+
// This is the "fallback impl" for type parameters, unnormalizable projections
2329+
// and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
2330+
// FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
2331+
// exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
2332+
let sized_predicate = ty::TraitRef::from_lang_item(
2333+
tcx,
2334+
LangItem::Sized,
2335+
obligation.cause.span(),
2336+
[self_ty],
2337+
);
2338+
obligations.push(obligation.with(tcx, sized_predicate));
2339+
tcx.types.unit
2340+
} else {
2341+
// We know that `self_ty` has the same metadata as `tail`. This allows us
2342+
// to prove predicates like `Wrapper<Tail>::Metadata == Tail::Metadata`.
2343+
Ty::new_projection(tcx, metadata_def_id, [tail])
2344+
}
23242345
});
2325-
if check_is_sized {
2326-
let sized_predicate = ty::TraitRef::from_lang_item(
2327-
tcx,
2328-
LangItem::Sized,
2329-
obligation.cause.span(),
2330-
[self_ty],
2331-
);
2332-
obligations.push(obligation.with(tcx, sized_predicate));
2333-
}
23342346
(metadata_ty.into(), obligations)
23352347
} else {
23362348
bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);

compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
4141
/// not entirely accurate if inference variables are involved.
4242
///
4343
/// This version may conservatively fail when outlives obligations
44-
/// are required.
44+
/// are required. Therefore, this version should only be used for
45+
/// optimizations or diagnostics and be treated as if it can always
46+
/// return `false`.
47+
///
48+
/// # Example
49+
///
50+
/// ```
51+
/// # #![allow(dead_code)]
52+
/// trait Trait {}
53+
///
54+
/// fn check<T: Trait>() {}
55+
///
56+
/// fn foo<T: 'static>()
57+
/// where
58+
/// &'static T: Trait,
59+
/// {
60+
/// // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation,
61+
/// // which means that `predicate_must_hold_considering_regions` will return
62+
/// // `false`.
63+
/// check::<&'_ T>();
64+
/// }
65+
/// ```
4566
fn predicate_must_hold_considering_regions(
4667
&self,
4768
obligation: &PredicateObligation<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// check-pass
2+
// revisions: old next
3+
//[next] compile-flags: -Znext-solver
4+
5+
#![feature(ptr_metadata)]
6+
7+
use std::ptr::{self, Pointee};
8+
9+
fn cast_same_meta<T: ?Sized, U: ?Sized>(ptr: *const T) -> *const U
10+
where
11+
T: Pointee<Metadata = <U as Pointee>::Metadata>,
12+
{
13+
let (thin, meta) = ptr.to_raw_parts();
14+
ptr::from_raw_parts(thin, meta)
15+
}
16+
17+
struct Wrapper<T: ?Sized>(T);
18+
19+
// normalize `Wrapper<T>::Metadata` -> `T::Metadata`
20+
fn wrapper_to_tail<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> {
21+
cast_same_meta(ptr)
22+
}
23+
24+
// normalize `Wrapper<T>::Metadata` -> `T::Metadata` -> `()`
25+
fn wrapper_to_unit<T>(ptr: *const ()) -> *const Wrapper<T> {
26+
cast_same_meta(ptr)
27+
}
28+
29+
trait Project {
30+
type Assoc: ?Sized;
31+
}
32+
33+
struct WrapperProject<T: ?Sized + Project>(T::Assoc);
34+
35+
// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata`
36+
fn wrapper_project_tail<T: ?Sized + Project>(ptr: *const T::Assoc) -> *const WrapperProject<T> {
37+
cast_same_meta(ptr)
38+
}
39+
40+
// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata` -> `()`
41+
fn wrapper_project_unit<T: ?Sized + Project>(ptr: *const ()) -> *const WrapperProject<T>
42+
where
43+
T::Assoc: Sized,
44+
{
45+
cast_same_meta(ptr)
46+
}
47+
48+
// normalize `<[T] as Pointee>::Metadata` -> `usize`, even if `[T]: Sized`
49+
fn sized_slice<T>(ptr: *const [T]) -> *const str
50+
where
51+
[T]: Sized,
52+
{
53+
cast_same_meta(ptr)
54+
}
55+
56+
fn main() {}

0 commit comments

Comments
 (0)