Skip to content

Commit bc5db48

Browse files
committed
Introduce TypedMetadata wrapper to coerce metadata
1 parent 0075bb4 commit bc5db48

File tree

23 files changed

+322
-89
lines changed

23 files changed

+322
-89
lines changed

compiler/rustc_codegen_ssa/src/base.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc_span::symbol::sym;
3737
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
3838
use rustc_target::abi::{Align, VariantIdx};
3939

40+
use std::assert_matches::assert_matches;
4041
use std::collections::BTreeSet;
4142
use std::convert::TryFrom;
4243
use std::ops::{Deref, DerefMut};
@@ -197,21 +198,21 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
197198
src_ty: Ty<'tcx>,
198199
dst_ty: Ty<'tcx>,
199200
old_info: Option<Bx::Value>,
200-
) -> (Bx::Value, Bx::Value) {
201+
) -> OperandValue<Bx::Value> {
201202
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
203+
let src_layout = bx.cx().layout_of(src_ty);
204+
let dst_layout = bx.cx().layout_of(dst_ty);
202205
match (src_ty.kind(), dst_ty.kind()) {
203206
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
204207
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
205208
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
206209
let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b)));
207-
(bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info))
210+
OperandValue::Pair(bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info))
208211
}
209-
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
212+
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if bx.cx().is_backend_scalar_pair(dst_layout) => {
210213
assert_eq!(def_a, def_b);
211-
let src_layout = bx.cx().layout_of(src_ty);
212-
let dst_layout = bx.cx().layout_of(dst_ty);
213214
if src_ty == dst_ty {
214-
return (src, old_info.unwrap());
215+
return OperandValue::Pair(src, old_info.unwrap());
215216
}
216217
let mut result = None;
217218
for i in 0..src_layout.fields.count() {
@@ -226,16 +227,29 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
226227

227228
let dst_f = dst_layout.field(bx.cx(), i);
228229
assert_ne!(src_f.ty, dst_f.ty);
229-
assert_eq!(result, None);
230+
assert_matches!(result, None);
230231
result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info));
231232
}
232-
let (lldata, llextra) = result.unwrap();
233+
let OperandValue::Pair(lldata, llextra) = result.unwrap()
234+
else { bug!() };
233235
let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true);
234236
let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true);
235237
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
236-
(bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
238+
OperandValue::Pair(bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
239+
}
240+
(&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => {
241+
assert_eq!(def_a, def_b);
242+
let typed_metadata = bx.tcx().require_lang_item(LangItem::TypedMetadata, None);
243+
assert_eq!(def_a.did(), typed_metadata);
244+
if src_ty == dst_ty {
245+
return OperandValue::Immediate(src);
246+
}
247+
let a = substs_a.type_at(0);
248+
let b = substs_b.type_at(0);
249+
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
250+
OperandValue::Immediate(unsized_info(bx, a, b, old_info))
237251
}
238-
_ => bug!("unsize_ptr: called on bad types"),
252+
_ => bug!("unsize_ptr: called on bad types {:?} -> {:?}", src_ty, dst_ty),
239253
}
240254
}
241255

@@ -250,12 +264,12 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
250264
let dst_ty = dst.layout.ty;
251265
match (src_ty.kind(), dst_ty.kind()) {
252266
(&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
253-
let (base, info) = match bx.load_operand(src).val {
267+
let val = match bx.load_operand(src).val {
254268
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
255269
OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
256270
OperandValue::Ref(..) => bug!(),
257271
};
258-
OperandValue::Pair(base, info).store(bx, dst);
272+
val.store(bx, dst);
259273
}
260274

261275
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {

compiler/rustc_codegen_ssa/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
2+
#![feature(assert_matches)]
23
#![feature(box_patterns)]
34
#![feature(try_blocks)]
45
#![feature(let_else)]

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
225225
operand.val
226226
}
227227
mir::CastKind::Pointer(PointerCast::Unsize) => {
228-
assert!(bx.cx().is_backend_scalar_pair(cast));
229228
let (lldata, llextra) = match operand.val {
230229
OperandValue::Pair(lldata, llextra) => {
231230
// unsize from a fat pointer -- this is a
@@ -240,9 +239,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
240239
bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
241240
}
242241
};
243-
let (lldata, llextra) =
244-
base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra);
245-
OperandValue::Pair(lldata, llextra)
242+
base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra)
246243
}
247244
mir::CastKind::Pointer(PointerCast::MutToConstPointer)
248245
| mir::CastKind::Misc

compiler/rustc_const_eval/src/interpret/cast.rs

+60-23
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
33

44
use rustc_apfloat::ieee::{Double, Single};
55
use rustc_apfloat::{Float, FloatConvert};
6-
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
6+
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar, ScalarMaybeUninit};
77
use rustc_middle::mir::CastKind;
88
use rustc_middle::ty::adjustment::PointerCast;
99
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
@@ -305,22 +305,37 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
305305
source_ty: Ty<'tcx>,
306306
cast_ty: Ty<'tcx>,
307307
) -> InterpResult<'tcx> {
308+
// We *could* forward `data` without even checking that it is initialized, but for now,
309+
// let's only allow casting properly initialized pointers.
310+
let (data, old_meta) = match *self.read_immediate(src)? {
311+
// If the input ptr is thin, use `Uninit` for the old metadata.
312+
// `unsize_just_metadata` knows how to handle that.
313+
Immediate::Scalar(data) => (data.check_init()?, ScalarMaybeUninit::Uninit),
314+
Immediate::ScalarPair(data, meta) => (data.check_init()?, meta),
315+
};
316+
317+
let new_meta = self.unsize_just_metadata(old_meta, source_ty, cast_ty)?;
318+
self.write_immediate(Immediate::ScalarPair(data.into(), new_meta.into()), dest)
319+
}
320+
321+
fn unsize_just_metadata(
322+
&mut self,
323+
src_meta: ScalarMaybeUninit<M::PointerTag>,
324+
// The pointee types
325+
source_ty: Ty<'tcx>,
326+
cast_ty: Ty<'tcx>,
327+
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
308328
// A<Struct> -> A<Trait> conversion
309329
let (src_pointee_ty, dest_pointee_ty) =
310330
self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env);
311331

312-
match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
332+
Ok(match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
313333
(&ty::Array(_, length), &ty::Slice(_)) => {
314-
let ptr = self.read_immediate(src)?.to_scalar()?;
315-
// u64 cast is from usize to u64, which is always good
316-
let val =
317-
Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
318-
self.write_immediate(val, dest)
334+
Scalar::from_machine_usize(length.eval_usize(*self.tcx, self.param_env), self)
319335
}
320336
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
321-
let val = self.read_immediate(src)?;
322337
if data_a.principal_def_id() == data_b.principal_def_id() {
323-
return self.write_immediate(*val, dest);
338+
return src_meta.check_init();
324339
}
325340
// trait upcasting coercion
326341
let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
@@ -330,27 +345,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
330345

331346
if let Some(entry_idx) = vptr_entry_idx {
332347
let entry_idx = u64::try_from(entry_idx).unwrap();
333-
let (old_data, old_vptr) = val.to_scalar_pair()?;
348+
let old_vptr = src_meta.check_init()?;
334349
let old_vptr = self.scalar_to_ptr(old_vptr)?;
335350
let new_vptr = self
336351
.read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
337-
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
352+
Scalar::from_maybe_pointer(new_vptr, self)
338353
} else {
339-
self.write_immediate(*val, dest)
354+
src_meta.check_init()?
340355
}
341356
}
342357
(_, &ty::Dynamic(ref data, _)) => {
343358
// Initial cast from sized to dyn trait
344359
let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
345-
let ptr = self.read_immediate(src)?.to_scalar()?;
346-
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
347-
self.write_immediate(val, dest)
360+
Scalar::from_maybe_pointer(vtable, &*self.tcx)
348361
}
349362

350363
_ => {
351-
span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty)
364+
span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", source_ty, cast_ty)
352365
}
353-
}
366+
})
354367
}
355368

356369
fn unsize_into(
@@ -360,16 +373,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
360373
dest: &PlaceTy<'tcx, M::PointerTag>,
361374
) -> InterpResult<'tcx> {
362375
trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
363-
match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
364-
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
365-
| (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
376+
let typed_metadata = self.tcx.lang_items().typed_metadata();
377+
match (src.layout.ty.kind(), cast_ty.ty.kind()) {
378+
(ty::Ref(_, s, _), ty::Ref(_, c, _) | ty::RawPtr(TypeAndMut { ty: c, .. }))
379+
| (ty::RawPtr(TypeAndMut { ty: s, .. }), ty::RawPtr(TypeAndMut { ty: c, .. })) => {
366380
self.unsize_into_ptr(src, dest, *s, *c)
367381
}
368-
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
382+
(ty::Adt(def_a, _), ty::Adt(def_b, _)) if def_a.is_box() || def_b.is_box() => {
369383
assert_eq!(def_a, def_b);
370-
384+
if !def_a.is_box() || !def_b.is_box() {
385+
span_bug!(
386+
self.cur_span(),
387+
"invalid unsizing between {:?} -> {:?}",
388+
src.layout.ty,
389+
cast_ty.ty
390+
);
391+
}
392+
self.unsize_into_ptr(src, dest, src.layout.ty.boxed_ty(), cast_ty.ty.boxed_ty())
393+
}
394+
(ty::Adt(def_a, substs_a), ty::Adt(def_b, substs_b))
395+
if def_a == def_b && Some(def_a.did()) == typed_metadata =>
396+
{
397+
// unsizing of TypedMetadata container
398+
// Example: `TypedMetadata<T>` -> `TypedMetadata<dyn Trait>`
399+
let a_pointee = substs_a.type_at(0);
400+
let b_pointee = substs_b.type_at(0);
401+
let src_field = self.operand_field(src, 0)?;
402+
let src = self.read_immediate(&src_field)?.to_scalar_or_uninit();
403+
let dst_field = self.place_field(dest, 0)?;
404+
let new_meta = self.unsize_just_metadata(src, a_pointee, b_pointee)?;
405+
self.write_scalar(new_meta, &dst_field)
406+
}
407+
(ty::Adt(def_a, _), ty::Adt(def_b, _)) if def_a == def_b => {
371408
// unsizing of generic struct with pointer fields
372-
// Example: `Arc<T>` -> `Arc<Trait>`
409+
// Example: `Arc<T>` -> `Arc<dyn Trait>`
373410
// here we need to increase the size of every &T thin ptr field to a fat ptr
374411
for i in 0..src.layout.fields.count() {
375412
let cast_ty_field = cast_ty.field(self, i);

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ language_item_table! {
182182
PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
183183
Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
184184
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
185+
TypedMetadata, sym::typed_metadata, typed_metadata, Target::Struct, GenericRequirement::Exact(1);
185186

186187
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
187188

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
215215
impl_polarity => { table_direct }
216216
impl_defaultness => { table_direct }
217217
constness => { table_direct }
218-
coerce_unsized_info => { table }
218+
coerce_unsized_kind => { table }
219219
mir_const_qualif => { table }
220220
rendered_const => { table }
221221
asyncness => { table_direct }

compiler/rustc_metadata/src/rmeta/encoder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1482,9 +1482,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
14821482
// if this is an impl of `CoerceUnsized`, create its
14831483
// "unsized info", else just store None
14841484
if Some(trait_ref.def_id) == self.tcx.lang_items().coerce_unsized_trait() {
1485-
let coerce_unsized_info =
1486-
self.tcx.at(item.span).coerce_unsized_info(def_id);
1487-
record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
1485+
let coerce_unsized_kind =
1486+
self.tcx.at(item.span).coerce_unsized_kind(def_id);
1487+
record!(self.tables.coerce_unsized_kind[def_id] <- coerce_unsized_kind);
14881488
}
14891489
}
14901490

compiler/rustc_metadata/src/rmeta/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ define_tables! {
368368
is_intrinsic: Table<DefIndex, ()>,
369369
impl_defaultness: Table<DefIndex, hir::Defaultness>,
370370
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
371-
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
371+
coerce_unsized_kind: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedKind>>,
372372
mir_const_qualif: Table<DefIndex, LazyValue<mir::ConstQualifs>>,
373373
rendered_const: Table<DefIndex, LazyValue<String>>,
374374
asyncness: Table<DefIndex, hir::IsAsync>,

compiler/rustc_middle/src/query/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,8 @@ rustc_queries! {
835835
}
836836

837837
/// Caches `CoerceUnsized` kinds for impls on custom types.
838-
query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo {
839-
desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) }
838+
query coerce_unsized_kind(key: DefId) -> ty::adjustment::CoerceUnsizedKind {
839+
desc { |tcx| "computing CoerceUnsized kind for `{}`", tcx.def_path_str(key) }
840840
cache_on_disk_if { key.is_local() }
841841
separate_provide_extern
842842
}

compiler/rustc_middle/src/ty/adjustment.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,15 @@ pub enum AutoBorrow<'tcx> {
177177
/// Information for `CoerceUnsized` impls, storing information we
178178
/// have computed about the coercion.
179179
///
180-
/// This struct can be obtained via the `coerce_impl_info` query.
180+
/// This enum can be obtained via the `coerce_unsized_kind` query.
181181
/// Demanding this struct also has the side-effect of reporting errors
182182
/// for inappropriate impls.
183-
#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)]
184-
pub struct CoerceUnsizedInfo {
185-
/// If this is a "custom coerce" impl, then what kind of custom
186-
/// coercion is it? This applies to impls of `CoerceUnsized` for
187-
/// structs, primarily, where we store a bit of info about which
188-
/// fields need to be coerced.
189-
pub custom_kind: Option<CustomCoerceUnsized>,
190-
}
191-
192-
#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)]
193-
pub enum CustomCoerceUnsized {
194-
/// Records the index of the field being coerced.
183+
#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Debug, HashStable)]
184+
pub enum CoerceUnsizedKind {
185+
/// Coercion of a raw pointer or ref.
186+
Ptr,
187+
/// Coercion of a struct. Records the index of the field being coerced.
195188
Struct(usize),
189+
/// Coercion of the pointee metadata directly.
190+
TypedMetadata,
196191
}

compiler/rustc_middle/src/ty/parameterized.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ trivially_parameterized_over_tcx! {
5959
ty::ReprOptions,
6060
ty::TraitDef,
6161
ty::Visibility,
62-
ty::adjustment::CoerceUnsizedInfo,
62+
ty::adjustment::CoerceUnsizedKind,
6363
ty::fast_reject::SimplifiedTypeGen<DefId>,
6464
rustc_ast::Attribute,
6565
rustc_ast::MacArgs,

0 commit comments

Comments
 (0)