From 9f59da28648c57d7c4fcac371f9f86adddeb20a3 Mon Sep 17 00:00:00 2001 From: Michael Hewson Date: Thu, 20 Sep 2018 03:16:10 -0400 Subject: Implement object-safety for arbitrary_self_types: part 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, all of the receivers that we care about are just a newtyped pointer — i.e. `Box`, `Rc`, `Pin>`, `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. --- src/librustc_codegen_llvm/abi.rs | 49 +++++++++++++++++++++++++--------- src/librustc_codegen_llvm/mir/block.rs | 40 ++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 19 deletions(-) (limited to 'src/librustc_codegen_llvm') diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 7c7662a88de..1662b57b8b3 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -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` 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` + // 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) }) diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index d98b7869ae9..0cb4963f97f 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -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) -- cgit 1.4.1-3-g733a5 From 192e7c4b51ab35f4cfbbacde9eeffa8e12dba873 Mon Sep 17 00:00:00 2001 From: Michael Hewson Date: Thu, 1 Nov 2018 18:28:52 -0400 Subject: Add comments explaining how codegen works for `dyn Trait` methods --- src/librustc_codegen_llvm/abi.rs | 5 +++++ src/librustc_codegen_llvm/mir/block.rs | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/librustc_codegen_llvm') diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 1662b57b8b3..e50534a4e1d 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -315,6 +315,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { _ => bug!("receiver type has unsupported layout: {:?}", layout) } + // In the case of Rc, we need to explicitly pass a *mut RcBox + // with a Scalar (not ScalarPair) ABI. This is a hack that is understood + // elsewhere in the compiler as a method on a `dyn Trait`. + // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we + // get a built-in pointer type let mut fat_pointer_layout = layout; 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() && !fat_pointer_layout.ty.is_region_ptr() diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 0cb4963f97f..a7f4c48c89b 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -647,8 +647,12 @@ impl FunctionCx<'a, 'll, 'tcx> { if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { 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` + // In the case of Rc, we need to explicitly pass a + // *mut RcBox with a Scalar (not ScalarPair) ABI. This is a hack + // that is understood elsewhere in the compiler as a method on + // `dyn Trait`. + // To get a `*mut RcBox`, we just keep unwrapping newtypes until + // we get a value of a built-in pointer type 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_region_ptr() { -- cgit 1.4.1-3-g733a5