mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Implement object-safety for arbitrary_self_types: part 2
For now, all of the receivers that we care about are just a newtyped pointer — i.e. `Box<Self>`, `Rc<Self>`, `Pin<Box<Self>>`, `Pin<&mut Self>`. This is much simpler to implement in codeine than the more general case, because the ABI is the same as a pointer. So we add some checks in typeck/coherence/builtin.rs to make sure that implementors of CoerceSized are just newtyped pointers. In this commit, we also implement the codegen bits.
This commit is contained in:
parent
d5c2c4a433
commit
9f59da2864
@ -19,7 +19,7 @@ use type_::Type;
|
||||
use type_of::{LayoutLlvmExt, PointerKind};
|
||||
use value::Value;
|
||||
|
||||
use rustc_target::abi::{LayoutOf, Size, TyLayout};
|
||||
use rustc_target::abi::{LayoutOf, Size, TyLayout, Abi as LayoutAbi};
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout;
|
||||
|
||||
@ -302,21 +302,44 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
FnType::new_internal(cx, sig, extra_args, |ty, arg_idx| {
|
||||
let mut layout = cx.layout_of(ty);
|
||||
// Don't pass the vtable, it's not an argument of the virtual fn.
|
||||
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
|
||||
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
|
||||
// or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen
|
||||
if arg_idx == Some(0) {
|
||||
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
|
||||
// `Box<dyn Trait>` has a few newtype wrappers around the raw
|
||||
// pointer, so we'd have to "dig down" to find `*dyn Trait`.
|
||||
let pointee = if layout.is_unsized() {
|
||||
layout.ty
|
||||
let fat_pointer_ty = if layout.is_unsized() {
|
||||
// unsized `self` is passed as a pointer to `self`
|
||||
// FIXME (mikeyhew) change this to use &own if it is ever added to the language
|
||||
cx.tcx.mk_mut_ptr(layout.ty)
|
||||
} else {
|
||||
layout.ty.builtin_deref(true)
|
||||
.unwrap_or_else(|| {
|
||||
bug!("FnType::new_vtable: non-pointer self {:?}", layout)
|
||||
}).ty
|
||||
match layout.abi {
|
||||
LayoutAbi::ScalarPair(..) => (),
|
||||
_ => bug!("receiver type has unsupported layout: {:?}", layout)
|
||||
}
|
||||
|
||||
let mut fat_pointer_layout = layout;
|
||||
'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr()
|
||||
&& !fat_pointer_layout.ty.is_region_ptr()
|
||||
{
|
||||
'iter_fields: for i in 0..fat_pointer_layout.fields.count() {
|
||||
let field_layout = fat_pointer_layout.field(cx, i);
|
||||
|
||||
if !field_layout.is_zst() {
|
||||
fat_pointer_layout = field_layout;
|
||||
continue 'descend_newtypes
|
||||
}
|
||||
}
|
||||
|
||||
bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout);
|
||||
}
|
||||
|
||||
fat_pointer_layout.ty
|
||||
};
|
||||
let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee);
|
||||
layout = cx.layout_of(fat_ptr_ty).field(cx, 0);
|
||||
|
||||
// we now have a type like `*mut RcBox<dyn Trait>`
|
||||
// change its layout to that of `*mut ()`, a thin pointer, but keep the same type
|
||||
// this is understood as a special case elsewhere in the compiler
|
||||
let unit_pointer_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_unit());
|
||||
layout = cx.layout_of(unit_pointer_ty);
|
||||
layout.ty = fat_pointer_ty;
|
||||
}
|
||||
ArgType::new(layout)
|
||||
})
|
||||
|
@ -642,14 +642,42 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
(&args[..], None)
|
||||
};
|
||||
|
||||
for (i, arg) in first_args.iter().enumerate() {
|
||||
'make_args: for (i, arg) in first_args.iter().enumerate() {
|
||||
let mut op = self.codegen_operand(&bx, arg);
|
||||
|
||||
if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
|
||||
if let Pair(data_ptr, meta) = op.val {
|
||||
llfn = Some(meth::VirtualIndex::from_index(idx)
|
||||
.get_fn(&bx, meta, &fn_ty));
|
||||
llargs.push(data_ptr);
|
||||
continue;
|
||||
if let Pair(..) = op.val {
|
||||
// descend through newtype wrappers until `op` is a builtin pointer to
|
||||
// `dyn Trait`, e.g. `*const dyn Trait`, `&mut dyn Trait`
|
||||
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
|
||||
&& !op.layout.ty.is_region_ptr()
|
||||
{
|
||||
'iter_fields: for i in 0..op.layout.fields.count() {
|
||||
let field = op.extract_field(&bx, i);
|
||||
if !field.layout.is_zst() {
|
||||
// we found the one non-zero-sized field that is allowed
|
||||
// now find *its* non-zero-sized field, or stop if it's a
|
||||
// pointer
|
||||
op = field;
|
||||
continue 'descend_newtypes
|
||||
}
|
||||
}
|
||||
|
||||
span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
|
||||
}
|
||||
|
||||
// now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
|
||||
// data pointer and vtable. Look up the method in the vtable, and pass
|
||||
// the data pointer as the first argument
|
||||
match op.val {
|
||||
Pair(data_ptr, meta) => {
|
||||
llfn = Some(meth::VirtualIndex::from_index(idx)
|
||||
.get_fn(&bx, meta, &fn_ty));
|
||||
llargs.push(data_ptr);
|
||||
continue 'make_args
|
||||
}
|
||||
other => bug!("expected a Pair, got {:?}", other)
|
||||
}
|
||||
} else if let Ref(data_ptr, Some(meta), _) = op.val {
|
||||
// by-value dynamic dispatch
|
||||
llfn = Some(meth::VirtualIndex::from_index(idx)
|
||||
|
@ -31,8 +31,8 @@ pub fn check_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def_id: DefId) {
|
||||
Checker { tcx, trait_def_id }
|
||||
.check(tcx.lang_items().drop_trait(), visit_implementation_of_drop)
|
||||
.check(tcx.lang_items().copy_trait(), visit_implementation_of_copy)
|
||||
.check(tcx.lang_items().coerce_unsized_trait(),
|
||||
visit_implementation_of_coerce_unsized);
|
||||
.check(tcx.lang_items().coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
|
||||
.check(tcx.lang_items().coerce_sized_trait(), visit_implementation_of_coerce_sized);
|
||||
}
|
||||
|
||||
struct Checker<'a, 'tcx: 'a> {
|
||||
@ -162,6 +162,164 @@ fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_implementation_of_coerce_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_did: DefId) {
|
||||
debug!("visit_implementation_of_coerce_sized: impl_did={:?}",
|
||||
impl_did);
|
||||
if impl_did.is_local() {
|
||||
let coerce_sized_trait = tcx.lang_items().coerce_sized_trait().unwrap();
|
||||
|
||||
let impl_node_id = tcx.hir.as_local_node_id(impl_did).unwrap();
|
||||
let span = tcx.hir.span(impl_node_id);
|
||||
|
||||
let source = tcx.type_of(impl_did);
|
||||
assert!(!source.has_escaping_regions());
|
||||
let target = {
|
||||
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
|
||||
assert_eq!(trait_ref.def_id, coerce_sized_trait);
|
||||
|
||||
trait_ref.substs.type_at(1)
|
||||
};
|
||||
|
||||
debug!("visit_implementation_of_coerce_sized: {:?} -> {:?}",
|
||||
source,
|
||||
target);
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
||||
let create_err = |msg: &str| {
|
||||
struct_span_err!(tcx.sess, span, E0378, "{}", msg)
|
||||
};
|
||||
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let cause = ObligationCause::misc(span, impl_node_id);
|
||||
|
||||
use ty::TyKind::*;
|
||||
match (&source.sty, &target.sty) {
|
||||
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
|
||||
if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok()
|
||||
&& mutbl_a == *mutbl_b => (),
|
||||
(&RawPtr(tm_a), &RawPtr(tm_b))
|
||||
if tm_a.mutbl == tm_b.mutbl => (),
|
||||
(&Adt(def_a, substs_a), &Adt(def_b, substs_b))
|
||||
if def_a.is_struct() && def_b.is_struct() =>
|
||||
{
|
||||
if def_a != def_b {
|
||||
let source_path = tcx.item_path_str(def_a.did);
|
||||
let target_path = tcx.item_path_str(def_b.did);
|
||||
|
||||
create_err(
|
||||
&format!(
|
||||
"the trait `CoerceSized` may only be implemented \
|
||||
for a coercion between structures with the same \
|
||||
definition; expected {}, found {}",
|
||||
source_path, target_path,
|
||||
)
|
||||
).emit();
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let fields = &def_a.non_enum_variant().fields;
|
||||
|
||||
let coerced_fields = fields.iter().filter_map(|field| {
|
||||
if tcx.type_of(field.did).is_phantom_data() {
|
||||
// ignore PhantomData fields
|
||||
return None
|
||||
}
|
||||
|
||||
let ty_a = field.ty(tcx, substs_a);
|
||||
let ty_b = field.ty(tcx, substs_b);
|
||||
if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
|
||||
if ok.obligations.is_empty() {
|
||||
create_err(
|
||||
"the trait `CoerceSized` may only be implemented for structs \
|
||||
containing the field being coerced, `PhantomData` fields, \
|
||||
and nothing else"
|
||||
).note(
|
||||
&format!(
|
||||
"extra field `{}` of type `{}` is not allowed",
|
||||
field.ident, ty_a,
|
||||
)
|
||||
).emit();
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(field)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
if coerced_fields.is_empty() {
|
||||
create_err(
|
||||
"the trait `CoerceSized` may only be implemented \
|
||||
for a coercion between structures with a single field \
|
||||
being coerced, none found"
|
||||
).emit();
|
||||
} else if coerced_fields.len() > 1 {
|
||||
create_err(
|
||||
"implementing the `CoerceSized` trait requires multiple coercions",
|
||||
).note(
|
||||
"the trait `CoerceSized` may only be implemented \
|
||||
for a coercion between structures with a single field \
|
||||
being coerced"
|
||||
).note(
|
||||
&format!(
|
||||
"currently, {} fields need coercions: {}",
|
||||
coerced_fields.len(),
|
||||
coerced_fields.iter().map(|field| {
|
||||
format!("{} ({} to {})",
|
||||
field.ident,
|
||||
field.ty(tcx, substs_a),
|
||||
field.ty(tcx, substs_b),
|
||||
)
|
||||
}).collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
).emit();
|
||||
} else {
|
||||
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
|
||||
|
||||
for field in coerced_fields {
|
||||
|
||||
let predicate = tcx.predicate_for_trait_def(
|
||||
param_env,
|
||||
cause.clone(),
|
||||
coerce_sized_trait,
|
||||
0,
|
||||
field.ty(tcx, substs_a),
|
||||
&[field.ty(tcx, substs_b).into()]
|
||||
);
|
||||
|
||||
fulfill_cx.register_predicate_obligation(&infcx, predicate);
|
||||
}
|
||||
|
||||
// Check that all transitive obligations are satisfied.
|
||||
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
|
||||
infcx.report_fulfillment_errors(&errors, None, false);
|
||||
}
|
||||
|
||||
// Finally, resolve all regions.
|
||||
let region_scope_tree = region::ScopeTree::default();
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
infcx.resolve_regions_and_report_errors(
|
||||
impl_did,
|
||||
®ion_scope_tree,
|
||||
&outlives_env,
|
||||
SuppressRegionErrors::default(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
create_err(
|
||||
"the trait `CoerceSsized` may only be implemented \
|
||||
for a coercion between structures"
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coerce_unsized_info<'a, 'gcx>(gcx: TyCtxt<'a, 'gcx, 'gcx>,
|
||||
impl_did: DefId)
|
||||
-> CoerceUnsizedInfo {
|
||||
|
@ -3084,6 +3084,80 @@ containing the unsized type is the last and only unsized type field in the
|
||||
struct.
|
||||
"##,
|
||||
|
||||
E0378: r##"
|
||||
The `CoerceSized` trait currently can only be implemented for builtin pointer
|
||||
types and structs that are newtype wrappers around them — that is, the struct
|
||||
must have only one field (except for`PhantomData`), and that field must itself
|
||||
implement `CoerceSized`.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#![feature(coerce_sized, unsize)]
|
||||
use std::{
|
||||
marker::Unsize,
|
||||
ops::CoerceSized,
|
||||
};
|
||||
|
||||
struct Ptr<T: ?Sized>(*const T);
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T>
|
||||
where
|
||||
T: Unsize<U>,
|
||||
{}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> CoerceSized<Ptr<T>> for Ptr<U>
|
||||
where
|
||||
T: Unsize<U>,
|
||||
{}
|
||||
```
|
||||
|
||||
```
|
||||
#![feature(coerce_unsized, coerce_sized)]
|
||||
use std::ops::{CoerceUnsized, CoerceSized};
|
||||
|
||||
struct Wrapper<T> {
|
||||
ptr: T,
|
||||
_phantom: PhantomData<()>,
|
||||
}
|
||||
|
||||
impl<T, U> CoerceUnsized<Wrapper<U>> for Wrapper<T>
|
||||
where
|
||||
T: CoerceUnsized<U>,
|
||||
{}
|
||||
|
||||
impl<T, U> CoerceSized<Wrapper<T>> for Wrapper<U>
|
||||
where
|
||||
T: CoerceUnsized<U>,
|
||||
U: CoerceSized<T>,
|
||||
{}
|
||||
```
|
||||
|
||||
Example of illegal CoerceSized implementation
|
||||
(illegal because of extra field)
|
||||
|
||||
```compile-fail,E0378
|
||||
#![feature(coerce_unsized, coerce_sized)]
|
||||
use std::ops::{CoerceUnsized, CoerceSized};
|
||||
|
||||
struct WrapperWithExtraField<T> {
|
||||
ptr: T,
|
||||
extra_stuff: i32,
|
||||
}
|
||||
|
||||
impl<T, U> CoerceUnsized<WrapperWithExtraField<U>> for WrapperWithExtraField<T>
|
||||
where
|
||||
T: CoerceUnsized<U>,
|
||||
{}
|
||||
|
||||
impl<T, U> CoerceSized<WrapperWithExtraField<T>> for WrapperWithExtraField<U>
|
||||
where
|
||||
T: CoerceUnsized<U>,
|
||||
U: CoerceSized<T>,
|
||||
{}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0390: r##"
|
||||
You tried to implement methods for a primitive type. Erroneous code example:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user