Skip to content

Commit

Permalink
Allow coercing sized metadata to unsized metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
CAD97 committed Apr 15, 2022
1 parent f387c93 commit 882a5d2
Show file tree
Hide file tree
Showing 16 changed files with 213 additions and 47 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ language_item_table! {
PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
SizedMetadata, sym::sized_metadata, sized_metadata, Target::Struct, GenericRequirement::None;
MetadataCoerceImpl, sym::metadata_coerce_impl,metadata_coerce_impl, Target::Impl, GenericRequirement::None;

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

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
impl_polarity => { table }
impl_defaultness => { table }
impl_constness => { table }
coerce_unsized_info => { table }
coerce_unsized_kind => { table }
mir_const_qualif => { table }
rendered_const => { table }
asyncness => { table }
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,9 +1463,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// if this is an impl of `CoerceUnsized`, create its
// "unsized info", else just store None
if Some(trait_ref.def_id) == self.tcx.lang_items().coerce_unsized_trait() {
let coerce_unsized_info =
self.tcx.at(item.span).coerce_unsized_info(def_id);
record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
let coerce_unsized_kind =
self.tcx.at(item.span).coerce_unsized_kind(def_id);
record!(self.tables.coerce_unsized_kind[def_id] <- coerce_unsized_kind);
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ define_tables! {
impl_constness: Table<DefIndex, hir::Constness>,
impl_defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedInfo)>,
coerce_unsized_kind: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedKind)>,
mir_const_qualif: Table<DefIndex, Lazy!(mir::ConstQualifs)>,
rendered_const: Table<DefIndex, Lazy!(String)>,
asyncness: Table<DefIndex, hir::IsAsync>,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,8 @@ rustc_queries! {
}

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

Expand Down
17 changes: 6 additions & 11 deletions compiler/rustc_middle/src/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,11 @@ pub enum AutoBorrow<'tcx> {
/// Demanding this struct also has the side-effect of reporting errors
/// for inappropriate impls.
#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)]
pub struct CoerceUnsizedInfo {
/// If this is a "custom coerce" impl, then what kind of custom
/// coercion is it? This applies to impls of `CoerceUnsized` for
/// structs, primarily, where we store a bit of info about which
/// fields need to be coerced.
pub custom_kind: Option<CustomCoerceUnsized>,
}

#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)]
pub enum CustomCoerceUnsized {
/// Records the index of the field being coerced.
pub enum CoerceUnsizedKind {
/// Coercion of a raw pointer or ref.
Ptr,
/// Coercion of a struct. Records the index of the field being coerced.
Struct(usize),
/// Coercion of the pointee metadata directly.
JustMetadata,
}
15 changes: 11 additions & 4 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,14 +2292,18 @@ impl<'tcx> Ty<'tcx> {
| ty::Closure(..)
| ty::Never
| ty::Error(_)
// Extern types have metadata = ().
| ty::Foreign(..)
// If returned by `struct_tail_without_normalization` this is a unit struct
// without any fields, or not a struct, and therefore is Sized.
| ty::Adt(..)
// If returned by `struct_tail_without_normalization` this is the empty tuple,
// a.k.a. unit type, which is Sized
| ty::Tuple(..) => (tcx.types.unit, false),
| ty::Tuple(..) => {
let sized_metadata = tcx.lang_items().sized_metadata().unwrap();
(tcx.type_of(sized_metadata).subst(tcx, &[tail.into()]), false)
},

// Extern types have metadata = ().
ty::Foreign(..) => (tcx.types.unit, false),

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

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

ty::Infer(ty::TyVar(_))
| ty::Bound(..)
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::mir::visit::Visitor as MirVisitor;
use rustc_middle::mir::{self, Local, Location};
use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast};
use rustc_middle::ty::adjustment::{CoerceUnsizedKind, PointerCast};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, VtblEntry};
Expand Down Expand Up @@ -1046,8 +1046,11 @@ fn find_vtable_types_for_unsizing<'tcx>(
(&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => {
assert_eq!(source_adt_def, target_adt_def);

let CustomCoerceUnsized::Struct(coerce_index) =
crate::custom_coerce_unsize_info(tcx, source_ty, target_ty);
let CoerceUnsizedKind::Struct(coerce_index) =
crate::custom_coerce_unsize_info(tcx, source_ty, target_ty)
else {
bug!("ty::Adt should have a struct `CoerceUnsized` kind");
};

let source_fields = &source_adt_def.non_enum_variant().fields;
let target_fields = &target_adt_def.non_enum_variant().fields;
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_monomorphize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern crate rustc_middle;

use rustc_hir::lang_items::LangItem;
use rustc_middle::traits;
use rustc_middle::ty::adjustment::CustomCoerceUnsized;
use rustc_middle::ty::adjustment::CoerceUnsizedKind;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};

Expand All @@ -26,7 +26,7 @@ fn custom_coerce_unsize_info<'tcx>(
tcx: TyCtxt<'tcx>,
source_ty: Ty<'tcx>,
target_ty: Ty<'tcx>,
) -> CustomCoerceUnsized {
) -> CoerceUnsizedKind {
let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None);

let trait_ref = ty::Binder::dummy(ty::TraitRef {
Expand All @@ -38,7 +38,7 @@ fn custom_coerce_unsize_info<'tcx>(
Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData {
impl_def_id,
..
})) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(),
})) => tcx.coerce_unsized_kind(impl_def_id),
impl_source => {
bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source);
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ symbols! {
memtag,
message,
meta,
metadata_coerce_impl,
metadata_type,
min_align_of,
min_align_of_val,
Expand Down Expand Up @@ -1311,6 +1312,7 @@ symbols! {
size_of,
size_of_val,
sized,
sized_metadata,
skip,
slice,
slice_len_fn,
Expand Down
71 changes: 62 additions & 9 deletions compiler/rustc_typeck/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_hir::ItemKind;
use rustc_infer::infer;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionckMode, TyCtxtInferExt};
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::adjustment::CoerceUnsizedKind;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
Expand Down Expand Up @@ -148,7 +148,7 @@ fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: Loc
// errors; other parts of the code may demand it for the info of
// course.
let span = tcx.def_span(impl_did);
tcx.at(span).coerce_unsized_info(impl_did);
tcx.at(span).coerce_unsized_kind(impl_did);
}

fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
Expand Down Expand Up @@ -320,14 +320,15 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
})
}

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

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

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

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

let err_info = CoerceUnsizedInfo { custom_kind: None };
let err_info = CoerceUnsizedKind::Ptr;

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

Expand All @@ -362,7 +363,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
)
.emit();
}
(mt_a.ty, mt_b.ty, unsize_trait, None)
(mt_a.ty, mt_b.ty, unsize_trait, CoerceUnsizedKind::Ptr)
};
let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
Expand Down Expand Up @@ -523,8 +524,60 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
}

let (i, a, b) = diff_fields[0];
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
(a, b, coerce_unsized_trait, Some(kind))
let kind = CoerceUnsizedKind::Struct(i);
(a, b, coerce_unsized_trait, kind)
}

// #[lang = "metadata_coerce_impl"]
// impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata>
// for <T as Pointee>::Metadata
(source, target) if Some(impl_did.to_def_id()) == metadata_coerce_impl => {
let kind = CoerceUnsizedKind::JustMetadata;

let pointee_trait = tcx.require_lang_item(LangItem::PointeeTrait, Some(span));
let pointee_metadata = tcx.require_lang_item(LangItem::Metadata, Some(span));
let sized_metadata = tcx.lang_items().sized_metadata();
let dyn_metadata = tcx.lang_items().dyn_metadata();

// FIXME(CAD97): should this form check be elsewhere? on the lang item wf check?
let (ty::Adt(def_a, substs_a), ty::Projection(proj_b)) = (source, target)
else {
tcx.sess.struct_span_err(span, r#"#[lang = "metadata_coerce_impl] malformed"#).emit();
return err_info;
};

let a = if Some(def_a.did()) == sized_metadata || Some(def_a.did()) == dyn_metadata {
// FIXME(CAD97): actual error reporting
if !substs_a.len() == 1 {
bug!("ill-formed metadata type");
}
Some(substs_a.type_at(0))
} else {
tcx.sess.struct_span_err(
span,
r#"#`[lang = "metadata_coerce_impl]` must be used on \
a Self type of `SizedMetadata` or `DynMetadata`"#
).emit();
None
};

let b = if proj_b.item_def_id == pointee_metadata && proj_b.trait_def_id(tcx) == pointee_trait {
Some(proj_b.self_ty())
} else {
tcx.sess.struct_span_err(
span,
r#"#`[lang = "metadata_coerce_impl]` must \
coerce to `<? as Pointee>::Metadata`"#
).emit();
None
};

let (Some(a), Some(b)) = (a, b)
else {
return err_info;
};

(a, b, unsize_trait, kind)
}

_ => {
Expand Down Expand Up @@ -569,6 +622,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
RegionckMode::default(),
);

CoerceUnsizedInfo { custom_kind: kind }
kind
})
}
4 changes: 2 additions & 2 deletions compiler/rustc_typeck/src/coherence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ fn enforce_empty_impls_for_marker_traits(
}

pub fn provide(providers: &mut Providers) {
use self::builtin::coerce_unsized_info;
use self::builtin::coerce_unsized_kind;
use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls};
use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
use self::orphan::orphan_check_crate;
Expand All @@ -154,7 +154,7 @@ pub fn provide(providers: &mut Providers) {
crate_incoherent_impls,
inherent_impls,
crate_inherent_impls_overlap_check,
coerce_unsized_info,
coerce_unsized_kind,
orphan_check_crate,
..*providers
};
Expand Down
17 changes: 11 additions & 6 deletions compiler/rustc_typeck/src/impl_wf_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,17 @@ fn enforce_impl_params_are_constrained(
ty::GenericParamDefKind::Type { .. } => {
let param_ty = ty::ParamTy::for_def(param);
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
report_unused_parameter(
tcx,
tcx.def_span(param.def_id),
"type",
&param_ty.to_string(),
);
// UNLESS it's #[lang = "metadata_coerce_impl"], used to impl
// CoerceUnsized<<U as Pointee>::Metadata> for SizedMetadata<T>
let metadata_coerce_impl = tcx.lang_items().metadata_coerce_impl();
if Some(impl_def_id.to_def_id()) != metadata_coerce_impl {
report_unused_parameter(
tcx,
tcx.def_span(param.def_id),
"type",
&param_ty.to_string(),
);
}
}
}
ty::GenericParamDefKind::Lifetime => {
Expand Down
17 changes: 17 additions & 0 deletions library/core/src/ops/unsize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::marker::Unsize;
#[cfg(not(bootstrap))]
use crate::ptr::{Pointee, SizedMetadata};

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

// SizedMetadata<T> -> <U as Pointee>::Metadata
#[unstable(feature = "coerce_unsized", issue = "27732")]
#[cfg(not(bootstrap))]
#[lang = "metadata_coerce_impl"]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata>
for SizedMetadata<T>
{
}

// // DynMetadata<T> -> <U as Pointee>::Metadata
// #[unstable(feature = "coerce_unsized", issue = "27732")]
// #[cfg(not(bootstrap))]
// #[lang = "metadata_coerce_impl"]
// impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<<U as Pointee>::Metadata> for DynMetadata<T> {}

/// `DispatchFromDyn` is used in the implementation of object safety checks (specifically allowing
/// arbitrary self types), to guarantee that a method's receiver type can be dispatched on.
///
Expand Down
Loading

0 comments on commit 882a5d2

Please sign in to comment.