Skip to content

Commit 882a5d2

Browse files
committed
Allow coercing sized metadata to unsized metadata
1 parent f387c93 commit 882a5d2

File tree

16 files changed

+213
-47
lines changed

16 files changed

+213
-47
lines changed

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ 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+
SizedMetadata, sym::sized_metadata, sized_metadata, Target::Struct, GenericRequirement::None;
186+
MetadataCoerceImpl, sym::metadata_coerce_impl,metadata_coerce_impl, Target::Impl, GenericRequirement::None;
185187

186188
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
187189

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
147147
impl_polarity => { table }
148148
impl_defaultness => { table }
149149
impl_constness => { table }
150-
coerce_unsized_info => { table }
150+
coerce_unsized_kind => { table }
151151
mir_const_qualif => { table }
152152
rendered_const => { table }
153153
asyncness => { table }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,9 +1463,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
14631463
// if this is an impl of `CoerceUnsized`, create its
14641464
// "unsized info", else just store None
14651465
if Some(trait_ref.def_id) == self.tcx.lang_items().coerce_unsized_trait() {
1466-
let coerce_unsized_info =
1467-
self.tcx.at(item.span).coerce_unsized_info(def_id);
1468-
record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
1466+
let coerce_unsized_kind =
1467+
self.tcx.at(item.span).coerce_unsized_kind(def_id);
1468+
record!(self.tables.coerce_unsized_kind[def_id] <- coerce_unsized_kind);
14691469
}
14701470
}
14711471

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ define_tables! {
338338
impl_constness: Table<DefIndex, hir::Constness>,
339339
impl_defaultness: Table<DefIndex, hir::Defaultness>,
340340
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
341-
coerce_unsized_info: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedInfo)>,
341+
coerce_unsized_kind: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedKind)>,
342342
mir_const_qualif: Table<DefIndex, Lazy!(mir::ConstQualifs)>,
343343
rendered_const: Table<DefIndex, Lazy!(String)>,
344344
asyncness: Table<DefIndex, hir::IsAsync>,

compiler/rustc_middle/src/query/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,8 +792,8 @@ rustc_queries! {
792792
}
793793

794794
/// Caches `CoerceUnsized` kinds for impls on custom types.
795-
query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo {
796-
desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) }
795+
query coerce_unsized_kind(key: DefId) -> ty::adjustment::CoerceUnsizedKind {
796+
desc { |tcx| "computing CoerceUnsized kind for `{}`", tcx.def_path_str(key) }
797797
separate_provide_extern
798798
}
799799

compiler/rustc_middle/src/ty/adjustment.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,11 @@ pub enum AutoBorrow<'tcx> {
181181
/// Demanding this struct also has the side-effect of reporting errors
182182
/// for inappropriate impls.
183183
#[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.
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+
JustMetadata,
196191
}

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,14 +2292,18 @@ impl<'tcx> Ty<'tcx> {
22922292
| ty::Closure(..)
22932293
| ty::Never
22942294
| ty::Error(_)
2295-
// Extern types have metadata = ().
2296-
| ty::Foreign(..)
22972295
// If returned by `struct_tail_without_normalization` this is a unit struct
22982296
// without any fields, or not a struct, and therefore is Sized.
22992297
| ty::Adt(..)
23002298
// If returned by `struct_tail_without_normalization` this is the empty tuple,
23012299
// a.k.a. unit type, which is Sized
2302-
| ty::Tuple(..) => (tcx.types.unit, false),
2300+
| ty::Tuple(..) => {
2301+
let sized_metadata = tcx.lang_items().sized_metadata().unwrap();
2302+
(tcx.type_of(sized_metadata).subst(tcx, &[tail.into()]), false)
2303+
},
2304+
2305+
// Extern types have metadata = ().
2306+
ty::Foreign(..) => (tcx.types.unit, false),
23032307

23042308
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
23052309
ty::Dynamic(..) => {
@@ -2309,7 +2313,10 @@ impl<'tcx> Ty<'tcx> {
23092313

23102314
// type parameters only have unit metadata if they're sized, so return true
23112315
// to make sure we double check this during confirmation
2312-
ty::Param(_) | ty::Projection(_) | ty::Opaque(..) => (tcx.types.unit, true),
2316+
ty::Param(_) | ty::Projection(_) | ty::Opaque(..) => {
2317+
let sized_metadata = tcx.lang_items().sized_metadata().unwrap();
2318+
(tcx.type_of(sized_metadata).subst(tcx, &[tail.into()]), true)
2319+
},
23132320

23142321
ty::Infer(ty::TyVar(_))
23152322
| ty::Bound(..)

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
190190
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
191191
use rustc_middle::mir::visit::Visitor as MirVisitor;
192192
use rustc_middle::mir::{self, Local, Location};
193-
use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast};
193+
use rustc_middle::ty::adjustment::{CoerceUnsizedKind, PointerCast};
194194
use rustc_middle::ty::print::with_no_trimmed_paths;
195195
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
196196
use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, VtblEntry};
@@ -1046,8 +1046,11 @@ fn find_vtable_types_for_unsizing<'tcx>(
10461046
(&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => {
10471047
assert_eq!(source_adt_def, target_adt_def);
10481048

1049-
let CustomCoerceUnsized::Struct(coerce_index) =
1050-
crate::custom_coerce_unsize_info(tcx, source_ty, target_ty);
1049+
let CoerceUnsizedKind::Struct(coerce_index) =
1050+
crate::custom_coerce_unsize_info(tcx, source_ty, target_ty)
1051+
else {
1052+
bug!("ty::Adt should have a struct `CoerceUnsized` kind");
1053+
};
10511054

10521055
let source_fields = &source_adt_def.non_enum_variant().fields;
10531056
let target_fields = &target_adt_def.non_enum_variant().fields;

compiler/rustc_monomorphize/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern crate rustc_middle;
1313

1414
use rustc_hir::lang_items::LangItem;
1515
use rustc_middle::traits;
16-
use rustc_middle::ty::adjustment::CustomCoerceUnsized;
16+
use rustc_middle::ty::adjustment::CoerceUnsizedKind;
1717
use rustc_middle::ty::query::Providers;
1818
use rustc_middle::ty::{self, Ty, TyCtxt};
1919

@@ -26,7 +26,7 @@ fn custom_coerce_unsize_info<'tcx>(
2626
tcx: TyCtxt<'tcx>,
2727
source_ty: Ty<'tcx>,
2828
target_ty: Ty<'tcx>,
29-
) -> CustomCoerceUnsized {
29+
) -> CoerceUnsizedKind {
3030
let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None);
3131

3232
let trait_ref = ty::Binder::dummy(ty::TraitRef {
@@ -38,7 +38,7 @@ fn custom_coerce_unsize_info<'tcx>(
3838
Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData {
3939
impl_def_id,
4040
..
41-
})) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(),
41+
})) => tcx.coerce_unsized_kind(impl_def_id),
4242
impl_source => {
4343
bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source);
4444
}

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ symbols! {
887887
memtag,
888888
message,
889889
meta,
890+
metadata_coerce_impl,
890891
metadata_type,
891892
min_align_of,
892893
min_align_of_val,
@@ -1311,6 +1312,7 @@ symbols! {
13111312
size_of,
13121313
size_of_val,
13131314
sized,
1315+
sized_metadata,
13141316
skip,
13151317
slice,
13161318
slice_len_fn,

compiler/rustc_typeck/src/coherence/builtin.rs

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_hir::ItemKind;
1010
use rustc_infer::infer;
1111
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1212
use rustc_infer::infer::{RegionckMode, TyCtxtInferExt};
13-
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
13+
use rustc_middle::ty::adjustment::CoerceUnsizedKind;
1414
use rustc_middle::ty::TypeFoldable;
1515
use rustc_middle::ty::{self, Ty, TyCtxt};
1616
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
@@ -148,7 +148,7 @@ fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: Loc
148148
// errors; other parts of the code may demand it for the info of
149149
// course.
150150
let span = tcx.def_span(impl_did);
151-
tcx.at(span).coerce_unsized_info(impl_did);
151+
tcx.at(span).coerce_unsized_kind(impl_did);
152152
}
153153

154154
fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
@@ -320,14 +320,15 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
320320
})
321321
}
322322

323-
pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
324-
debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
323+
pub fn coerce_unsized_kind<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedKind {
324+
debug!("compute_coerce_unsized_kind(impl_did={:?})", impl_did);
325325

326326
// this provider should only get invoked for local def-ids
327327
let impl_did = impl_did.expect_local();
328328
let span = tcx.def_span(impl_did);
329329

330330
let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
331+
let metadata_coerce_impl = tcx.lang_items().metadata_coerce_impl();
331332

332333
let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
333334
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
@@ -342,7 +343,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
342343
let param_env = tcx.param_env(impl_did);
343344
assert!(!source.has_escaping_bound_vars());
344345

345-
let err_info = CoerceUnsizedInfo { custom_kind: None };
346+
let err_info = CoerceUnsizedKind::Ptr;
346347

347348
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
348349

@@ -362,7 +363,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
362363
)
363364
.emit();
364365
}
365-
(mt_a.ty, mt_b.ty, unsize_trait, None)
366+
(mt_a.ty, mt_b.ty, unsize_trait, CoerceUnsizedKind::Ptr)
366367
};
367368
let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
368369
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
@@ -523,8 +524,60 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
523524
}
524525

525526
let (i, a, b) = diff_fields[0];
526-
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
527-
(a, b, coerce_unsized_trait, Some(kind))
527+
let kind = CoerceUnsizedKind::Struct(i);
528+
(a, b, coerce_unsized_trait, kind)
529+
}
530+
531+
// #[lang = "metadata_coerce_impl"]
532+
// impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata>
533+
// for <T as Pointee>::Metadata
534+
(source, target) if Some(impl_did.to_def_id()) == metadata_coerce_impl => {
535+
let kind = CoerceUnsizedKind::JustMetadata;
536+
537+
let pointee_trait = tcx.require_lang_item(LangItem::PointeeTrait, Some(span));
538+
let pointee_metadata = tcx.require_lang_item(LangItem::Metadata, Some(span));
539+
let sized_metadata = tcx.lang_items().sized_metadata();
540+
let dyn_metadata = tcx.lang_items().dyn_metadata();
541+
542+
// FIXME(CAD97): should this form check be elsewhere? on the lang item wf check?
543+
let (ty::Adt(def_a, substs_a), ty::Projection(proj_b)) = (source, target)
544+
else {
545+
tcx.sess.struct_span_err(span, r#"#[lang = "metadata_coerce_impl] malformed"#).emit();
546+
return err_info;
547+
};
548+
549+
let a = if Some(def_a.did()) == sized_metadata || Some(def_a.did()) == dyn_metadata {
550+
// FIXME(CAD97): actual error reporting
551+
if !substs_a.len() == 1 {
552+
bug!("ill-formed metadata type");
553+
}
554+
Some(substs_a.type_at(0))
555+
} else {
556+
tcx.sess.struct_span_err(
557+
span,
558+
r#"#`[lang = "metadata_coerce_impl]` must be used on \
559+
a Self type of `SizedMetadata` or `DynMetadata`"#
560+
).emit();
561+
None
562+
};
563+
564+
let b = if proj_b.item_def_id == pointee_metadata && proj_b.trait_def_id(tcx) == pointee_trait {
565+
Some(proj_b.self_ty())
566+
} else {
567+
tcx.sess.struct_span_err(
568+
span,
569+
r#"#`[lang = "metadata_coerce_impl]` must \
570+
coerce to `<? as Pointee>::Metadata`"#
571+
).emit();
572+
None
573+
};
574+
575+
let (Some(a), Some(b)) = (a, b)
576+
else {
577+
return err_info;
578+
};
579+
580+
(a, b, unsize_trait, kind)
528581
}
529582

530583
_ => {
@@ -569,6 +622,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
569622
RegionckMode::default(),
570623
);
571624

572-
CoerceUnsizedInfo { custom_kind: kind }
625+
kind
573626
})
574627
}

compiler/rustc_typeck/src/coherence/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ fn enforce_empty_impls_for_marker_traits(
143143
}
144144

145145
pub fn provide(providers: &mut Providers) {
146-
use self::builtin::coerce_unsized_info;
146+
use self::builtin::coerce_unsized_kind;
147147
use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls};
148148
use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
149149
use self::orphan::orphan_check_crate;
@@ -154,7 +154,7 @@ pub fn provide(providers: &mut Providers) {
154154
crate_incoherent_impls,
155155
inherent_impls,
156156
crate_inherent_impls_overlap_check,
157-
coerce_unsized_info,
157+
coerce_unsized_kind,
158158
orphan_check_crate,
159159
..*providers
160160
};

compiler/rustc_typeck/src/impl_wf_check.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,17 @@ fn enforce_impl_params_are_constrained(
150150
ty::GenericParamDefKind::Type { .. } => {
151151
let param_ty = ty::ParamTy::for_def(param);
152152
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
153-
report_unused_parameter(
154-
tcx,
155-
tcx.def_span(param.def_id),
156-
"type",
157-
&param_ty.to_string(),
158-
);
153+
// UNLESS it's #[lang = "metadata_coerce_impl"], used to impl
154+
// CoerceUnsized<<U as Pointee>::Metadata> for SizedMetadata<T>
155+
let metadata_coerce_impl = tcx.lang_items().metadata_coerce_impl();
156+
if Some(impl_def_id.to_def_id()) != metadata_coerce_impl {
157+
report_unused_parameter(
158+
tcx,
159+
tcx.def_span(param.def_id),
160+
"type",
161+
&param_ty.to_string(),
162+
);
163+
}
159164
}
160165
}
161166
ty::GenericParamDefKind::Lifetime => {

library/core/src/ops/unsize.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::marker::Unsize;
2+
#[cfg(not(bootstrap))]
3+
use crate::ptr::{Pointee, SizedMetadata};
24

35
/// Trait that indicates that this is a pointer or a wrapper for one,
46
/// where unsizing can be performed on the pointee.
@@ -68,6 +70,21 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
6870
#[unstable(feature = "coerce_unsized", issue = "27732")]
6971
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
7072

73+
// SizedMetadata<T> -> <U as Pointee>::Metadata
74+
#[unstable(feature = "coerce_unsized", issue = "27732")]
75+
#[cfg(not(bootstrap))]
76+
#[lang = "metadata_coerce_impl"]
77+
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata>
78+
for SizedMetadata<T>
79+
{
80+
}
81+
82+
// // DynMetadata<T> -> <U as Pointee>::Metadata
83+
// #[unstable(feature = "coerce_unsized", issue = "27732")]
84+
// #[cfg(not(bootstrap))]
85+
// #[lang = "metadata_coerce_impl"]
86+
// impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata> for DynMetadata<T> {}
87+
7188
/// `DispatchFromDyn` is used in the implementation of object safety checks (specifically allowing
7289
/// arbitrary self types), to guarantee that a method's receiver type can be dispatched on.
7390
///

0 commit comments

Comments
 (0)