Skip to content

Commit 9f59da2

Browse files
committed
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.
1 parent d5c2c4a commit 9f59da2

File tree

4 files changed

+304
-21
lines changed

4 files changed

+304
-21
lines changed

src/librustc_codegen_llvm/abi.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use type_::Type;
1919
use type_of::{LayoutLlvmExt, PointerKind};
2020
use value::Value;
2121

22-
use rustc_target::abi::{LayoutOf, Size, TyLayout};
22+
use rustc_target::abi::{LayoutOf, Size, TyLayout, Abi as LayoutAbi};
2323
use rustc::ty::{self, Ty};
2424
use rustc::ty::layout;
2525

@@ -302,21 +302,44 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
302302
FnType::new_internal(cx, sig, extra_args, |ty, arg_idx| {
303303
let mut layout = cx.layout_of(ty);
304304
// Don't pass the vtable, it's not an argument of the virtual fn.
305-
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
305+
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
306+
// or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen
306307
if arg_idx == Some(0) {
307-
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
308-
// `Box<dyn Trait>` has a few newtype wrappers around the raw
309-
// pointer, so we'd have to "dig down" to find `*dyn Trait`.
310-
let pointee = if layout.is_unsized() {
311-
layout.ty
308+
let fat_pointer_ty = if layout.is_unsized() {
309+
// unsized `self` is passed as a pointer to `self`
310+
// FIXME (mikeyhew) change this to use &own if it is ever added to the language
311+
cx.tcx.mk_mut_ptr(layout.ty)
312312
} else {
313-
layout.ty.builtin_deref(true)
314-
.unwrap_or_else(|| {
315-
bug!("FnType::new_vtable: non-pointer self {:?}", layout)
316-
}).ty
313+
match layout.abi {
314+
LayoutAbi::ScalarPair(..) => (),
315+
_ => bug!("receiver type has unsupported layout: {:?}", layout)
316+
}
317+
318+
let mut fat_pointer_layout = layout;
319+
'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr()
320+
&& !fat_pointer_layout.ty.is_region_ptr()
321+
{
322+
'iter_fields: for i in 0..fat_pointer_layout.fields.count() {
323+
let field_layout = fat_pointer_layout.field(cx, i);
324+
325+
if !field_layout.is_zst() {
326+
fat_pointer_layout = field_layout;
327+
continue 'descend_newtypes
328+
}
329+
}
330+
331+
bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout);
332+
}
333+
334+
fat_pointer_layout.ty
317335
};
318-
let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee);
319-
layout = cx.layout_of(fat_ptr_ty).field(cx, 0);
336+
337+
// we now have a type like `*mut RcBox<dyn Trait>`
338+
// change its layout to that of `*mut ()`, a thin pointer, but keep the same type
339+
// this is understood as a special case elsewhere in the compiler
340+
let unit_pointer_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_unit());
341+
layout = cx.layout_of(unit_pointer_ty);
342+
layout.ty = fat_pointer_ty;
320343
}
321344
ArgType::new(layout)
322345
})

src/librustc_codegen_llvm/mir/block.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -642,14 +642,42 @@ impl FunctionCx<'a, 'll, 'tcx> {
642642
(&args[..], None)
643643
};
644644

645-
for (i, arg) in first_args.iter().enumerate() {
645+
'make_args: for (i, arg) in first_args.iter().enumerate() {
646646
let mut op = self.codegen_operand(&bx, arg);
647+
647648
if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
648-
if let Pair(data_ptr, meta) = op.val {
649-
llfn = Some(meth::VirtualIndex::from_index(idx)
650-
.get_fn(&bx, meta, &fn_ty));
651-
llargs.push(data_ptr);
652-
continue;
649+
if let Pair(..) = op.val {
650+
// descend through newtype wrappers until `op` is a builtin pointer to
651+
// `dyn Trait`, e.g. `*const dyn Trait`, `&mut dyn Trait`
652+
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
653+
&& !op.layout.ty.is_region_ptr()
654+
{
655+
'iter_fields: for i in 0..op.layout.fields.count() {
656+
let field = op.extract_field(&bx, i);
657+
if !field.layout.is_zst() {
658+
// we found the one non-zero-sized field that is allowed
659+
// now find *its* non-zero-sized field, or stop if it's a
660+
// pointer
661+
op = field;
662+
continue 'descend_newtypes
663+
}
664+
}
665+
666+
span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
667+
}
668+
669+
// now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
670+
// data pointer and vtable. Look up the method in the vtable, and pass
671+
// the data pointer as the first argument
672+
match op.val {
673+
Pair(data_ptr, meta) => {
674+
llfn = Some(meth::VirtualIndex::from_index(idx)
675+
.get_fn(&bx, meta, &fn_ty));
676+
llargs.push(data_ptr);
677+
continue 'make_args
678+
}
679+
other => bug!("expected a Pair, got {:?}", other)
680+
}
653681
} else if let Ref(data_ptr, Some(meta), _) = op.val {
654682
// by-value dynamic dispatch
655683
llfn = Some(meth::VirtualIndex::from_index(idx)

src/librustc_typeck/coherence/builtin.rs

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ pub fn check_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def_id: DefId) {
3131
Checker { tcx, trait_def_id }
3232
.check(tcx.lang_items().drop_trait(), visit_implementation_of_drop)
3333
.check(tcx.lang_items().copy_trait(), visit_implementation_of_copy)
34-
.check(tcx.lang_items().coerce_unsized_trait(),
35-
visit_implementation_of_coerce_unsized);
34+
.check(tcx.lang_items().coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
35+
.check(tcx.lang_items().coerce_sized_trait(), visit_implementation_of_coerce_sized);
3636
}
3737

3838
struct Checker<'a, 'tcx: 'a> {
@@ -162,6 +162,164 @@ fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
162162
}
163163
}
164164

165+
fn visit_implementation_of_coerce_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_did: DefId) {
166+
debug!("visit_implementation_of_coerce_sized: impl_did={:?}",
167+
impl_did);
168+
if impl_did.is_local() {
169+
let coerce_sized_trait = tcx.lang_items().coerce_sized_trait().unwrap();
170+
171+
let impl_node_id = tcx.hir.as_local_node_id(impl_did).unwrap();
172+
let span = tcx.hir.span(impl_node_id);
173+
174+
let source = tcx.type_of(impl_did);
175+
assert!(!source.has_escaping_regions());
176+
let target = {
177+
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
178+
assert_eq!(trait_ref.def_id, coerce_sized_trait);
179+
180+
trait_ref.substs.type_at(1)
181+
};
182+
183+
debug!("visit_implementation_of_coerce_sized: {:?} -> {:?}",
184+
source,
185+
target);
186+
187+
let param_env = tcx.param_env(impl_did);
188+
189+
let create_err = |msg: &str| {
190+
struct_span_err!(tcx.sess, span, E0378, "{}", msg)
191+
};
192+
193+
tcx.infer_ctxt().enter(|infcx| {
194+
let cause = ObligationCause::misc(span, impl_node_id);
195+
196+
use ty::TyKind::*;
197+
match (&source.sty, &target.sty) {
198+
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
199+
if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok()
200+
&& mutbl_a == *mutbl_b => (),
201+
(&RawPtr(tm_a), &RawPtr(tm_b))
202+
if tm_a.mutbl == tm_b.mutbl => (),
203+
(&Adt(def_a, substs_a), &Adt(def_b, substs_b))
204+
if def_a.is_struct() && def_b.is_struct() =>
205+
{
206+
if def_a != def_b {
207+
let source_path = tcx.item_path_str(def_a.did);
208+
let target_path = tcx.item_path_str(def_b.did);
209+
210+
create_err(
211+
&format!(
212+
"the trait `CoerceSized` may only be implemented \
213+
for a coercion between structures with the same \
214+
definition; expected {}, found {}",
215+
source_path, target_path,
216+
)
217+
).emit();
218+
219+
return
220+
}
221+
222+
let fields = &def_a.non_enum_variant().fields;
223+
224+
let coerced_fields = fields.iter().filter_map(|field| {
225+
if tcx.type_of(field.did).is_phantom_data() {
226+
// ignore PhantomData fields
227+
return None
228+
}
229+
230+
let ty_a = field.ty(tcx, substs_a);
231+
let ty_b = field.ty(tcx, substs_b);
232+
if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
233+
if ok.obligations.is_empty() {
234+
create_err(
235+
"the trait `CoerceSized` may only be implemented for structs \
236+
containing the field being coerced, `PhantomData` fields, \
237+
and nothing else"
238+
).note(
239+
&format!(
240+
"extra field `{}` of type `{}` is not allowed",
241+
field.ident, ty_a,
242+
)
243+
).emit();
244+
245+
return None;
246+
}
247+
}
248+
249+
Some(field)
250+
}).collect::<Vec<_>>();
251+
252+
if coerced_fields.is_empty() {
253+
create_err(
254+
"the trait `CoerceSized` may only be implemented \
255+
for a coercion between structures with a single field \
256+
being coerced, none found"
257+
).emit();
258+
} else if coerced_fields.len() > 1 {
259+
create_err(
260+
"implementing the `CoerceSized` trait requires multiple coercions",
261+
).note(
262+
"the trait `CoerceSized` may only be implemented \
263+
for a coercion between structures with a single field \
264+
being coerced"
265+
).note(
266+
&format!(
267+
"currently, {} fields need coercions: {}",
268+
coerced_fields.len(),
269+
coerced_fields.iter().map(|field| {
270+
format!("{} ({} to {})",
271+
field.ident,
272+
field.ty(tcx, substs_a),
273+
field.ty(tcx, substs_b),
274+
)
275+
}).collect::<Vec<_>>()
276+
.join(", ")
277+
)
278+
).emit();
279+
} else {
280+
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
281+
282+
for field in coerced_fields {
283+
284+
let predicate = tcx.predicate_for_trait_def(
285+
param_env,
286+
cause.clone(),
287+
coerce_sized_trait,
288+
0,
289+
field.ty(tcx, substs_a),
290+
&[field.ty(tcx, substs_b).into()]
291+
);
292+
293+
fulfill_cx.register_predicate_obligation(&infcx, predicate);
294+
}
295+
296+
// Check that all transitive obligations are satisfied.
297+
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
298+
infcx.report_fulfillment_errors(&errors, None, false);
299+
}
300+
301+
// Finally, resolve all regions.
302+
let region_scope_tree = region::ScopeTree::default();
303+
let outlives_env = OutlivesEnvironment::new(param_env);
304+
infcx.resolve_regions_and_report_errors(
305+
impl_did,
306+
&region_scope_tree,
307+
&outlives_env,
308+
SuppressRegionErrors::default(),
309+
);
310+
}
311+
}
312+
_ => {
313+
create_err(
314+
"the trait `CoerceSsized` may only be implemented \
315+
for a coercion between structures"
316+
).emit();
317+
}
318+
}
319+
})
320+
}
321+
}
322+
165323
pub fn coerce_unsized_info<'a, 'gcx>(gcx: TyCtxt<'a, 'gcx, 'gcx>,
166324
impl_did: DefId)
167325
-> CoerceUnsizedInfo {

src/librustc_typeck/diagnostics.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3084,6 +3084,80 @@ containing the unsized type is the last and only unsized type field in the
30843084
struct.
30853085
"##,
30863086

3087+
E0378: r##"
3088+
The `CoerceSized` trait currently can only be implemented for builtin pointer
3089+
types and structs that are newtype wrappers around them — that is, the struct
3090+
must have only one field (except for`PhantomData`), and that field must itself
3091+
implement `CoerceSized`.
3092+
3093+
Examples:
3094+
3095+
```
3096+
#![feature(coerce_sized, unsize)]
3097+
use std::{
3098+
marker::Unsize,
3099+
ops::CoerceSized,
3100+
};
3101+
3102+
struct Ptr<T: ?Sized>(*const T);
3103+
3104+
impl<T: ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T>
3105+
where
3106+
T: Unsize<U>,
3107+
{}
3108+
3109+
impl<T: ?Sized, U: ?Sized> CoerceSized<Ptr<T>> for Ptr<U>
3110+
where
3111+
T: Unsize<U>,
3112+
{}
3113+
```
3114+
3115+
```
3116+
#![feature(coerce_unsized, coerce_sized)]
3117+
use std::ops::{CoerceUnsized, CoerceSized};
3118+
3119+
struct Wrapper<T> {
3120+
ptr: T,
3121+
_phantom: PhantomData<()>,
3122+
}
3123+
3124+
impl<T, U> CoerceUnsized<Wrapper<U>> for Wrapper<T>
3125+
where
3126+
T: CoerceUnsized<U>,
3127+
{}
3128+
3129+
impl<T, U> CoerceSized<Wrapper<T>> for Wrapper<U>
3130+
where
3131+
T: CoerceUnsized<U>,
3132+
U: CoerceSized<T>,
3133+
{}
3134+
```
3135+
3136+
Example of illegal CoerceSized implementation
3137+
(illegal because of extra field)
3138+
3139+
```compile-fail,E0378
3140+
#![feature(coerce_unsized, coerce_sized)]
3141+
use std::ops::{CoerceUnsized, CoerceSized};
3142+
3143+
struct WrapperWithExtraField<T> {
3144+
ptr: T,
3145+
extra_stuff: i32,
3146+
}
3147+
3148+
impl<T, U> CoerceUnsized<WrapperWithExtraField<U>> for WrapperWithExtraField<T>
3149+
where
3150+
T: CoerceUnsized<U>,
3151+
{}
3152+
3153+
impl<T, U> CoerceSized<WrapperWithExtraField<T>> for WrapperWithExtraField<U>
3154+
where
3155+
T: CoerceUnsized<U>,
3156+
U: CoerceSized<T>,
3157+
{}
3158+
```
3159+
"##,
3160+
30873161
E0390: r##"
30883162
You tried to implement methods for a primitive type. Erroneous code example:
30893163

0 commit comments

Comments
 (0)