about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-09-14 23:28:14 +0000
committerMichael Goulet <michael@errs.io>2022-10-14 04:43:56 +0000
commitfeb4244f54c4ecf828ba8e0ae3d8c23d80b64646 (patch)
tree7359d92923722416d1e91cf4984fb9d5ed5bf4c0
parent76386bd65e650b5289b142daa310a4b98230c3db (diff)
downloadrust-feb4244f54c4ecf828ba8e0ae3d8c23d80b64646.tar.gz
rust-feb4244f54c4ecf828ba8e0ae3d8c23d80b64646.zip
Allow dyn* upcasting
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs61
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs27
-rw-r--r--compiler/rustc_hir_analysis/src/check/coercion.rs12
-rw-r--r--src/test/ui/dyn-star/upcast.rs33
4 files changed, 97 insertions, 36 deletions
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index cb5436fd61a..70e1fe62e47 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -38,7 +38,7 @@ use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_span::Symbol;
 use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
-use rustc_target::abi::{Align, VariantIdx};
+use rustc_target::abi::{Align, Size, VariantIdx};
 
 use std::collections::BTreeSet;
 use std::convert::TryFrom;
@@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         (&ty::Array(_, len), &ty::Slice(_)) => {
             cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
         }
-        (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+        (
+            &ty::Dynamic(ref data_a, _, src_dyn_kind),
+            &ty::Dynamic(ref data_b, _, target_dyn_kind),
+        ) => {
+            assert_eq!(src_dyn_kind, target_dyn_kind);
+
             let old_info =
                 old_info.expect("unsized_info: missing old info for trait upcasting coercion");
             if data_a.principal_def_id() == data_b.principal_def_id() {
@@ -166,11 +171,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             if let Some(entry_idx) = vptr_entry_idx {
                 let ptr_ty = cx.type_i8p();
                 let ptr_align = cx.tcx().data_layout.pointer_align.abi;
-                let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
-                    cx.layout_of(cx.tcx().mk_mut_ptr(target)),
-                    1,
-                    true,
-                );
+                let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
                 let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
                 let gep = bx.inbounds_gep(
                     ptr_ty,
@@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 old_info
             }
         }
-        (_, &ty::Dynamic(ref data, ..)) => {
-            let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
-                cx.layout_of(cx.tcx().mk_mut_ptr(target)),
-                1,
-                true,
-            );
+        (_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
+            let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
             cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
         }
         _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
     }
 }
 
+// Returns the vtable pointer type of a `dyn` or `dyn*` type
+fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
+    cx: &Cx,
+    target: Ty<'tcx>,
+    kind: ty::DynKind,
+) -> <Cx as BackendTypes>::Type {
+    cx.scalar_pair_element_backend_type(
+        cx.layout_of(match kind {
+            // vtable is the second field of `*mut dyn Trait`
+            ty::Dyn => cx.tcx().mk_mut_ptr(target),
+            // vtable is the second field of `dyn* Trait`
+            ty::DynStar => target,
+        }),
+        1,
+        true,
+    )
+}
+
 /// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
 pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
@@ -247,6 +262,26 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     }
 }
 
+/// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type.
+pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &mut Bx,
+    src: Bx::Value,
+    src_ty_and_layout: TyAndLayout<'tcx>,
+    dst_ty: Ty<'tcx>,
+    old_info: Option<Bx::Value>,
+) -> (Bx::Value, Bx::Value) {
+    debug!("unsize_ptr: {:?} => {:?}", src_ty_and_layout.ty, dst_ty);
+    assert!(matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)));
+    // FIXME(dyn-star): this is probably not the best way to check if this is
+    // a pointer, and really we should ensure that the value is a suitable
+    // pointer earlier in the compilation process.
+    let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) {
+        Some(_) => bx.ptrtoint(src, bx.cx().type_isize()),
+        None => bx.bitcast(src, bx.type_isize()),
+    };
+    (src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info))
+}
+
 /// Coerces `src`, which is a reference to a value of type `src_ty`,
 /// to a value of type `dst_ty`, and stores the result in `dst`.
 pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 4ed99df1e81..4aab31fbfe7 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef};
 
 use crate::base;
 use crate::common::{self, IntPredicate};
-use crate::meth::get_vtable;
 use crate::traits::*;
 use crate::MemFlags;
 
@@ -14,7 +13,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
 use rustc_span::source_map::{Span, DUMMY_SP};
-use rustc_target::abi::Size;
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     #[instrument(level = "trace", skip(self, bx))]
@@ -274,27 +272,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         }
                     }
                     mir::CastKind::DynStar => {
-                        let data = match operand.val {
+                        let (lldata, llextra) = match operand.val {
                             OperandValue::Ref(_, _, _) => todo!(),
-                            OperandValue::Immediate(v) => v,
-                            OperandValue::Pair(_, _) => todo!(),
-                        };
-                        let trait_ref =
-                            if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
-                                data.principal()
-                            } else {
-                                bug!("Only valid to do a DynStar cast into a DynStar type")
-                            };
-                        let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
-                        let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
-                        // FIXME(dyn-star): this is probably not the best way to check if this is
-                        // a pointer, and really we should ensure that the value is a suitable
-                        // pointer earlier in the compilation process.
-                        let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
-                            Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
-                            None => data,
+                            OperandValue::Immediate(v) => (v, None),
+                            OperandValue::Pair(v, l) => (v, Some(l)),
                         };
-                        OperandValue::Pair(data, vtable)
+                        let (lldata, llextra) =
+                            base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
+                        OperandValue::Pair(lldata, llextra)
                     }
                     mir::CastKind::Pointer(
                         PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
diff --git a/compiler/rustc_hir_analysis/src/check/coercion.rs b/compiler/rustc_hir_analysis/src/check/coercion.rs
index 44e64382397..a2bb249ba6e 100644
--- a/compiler/rustc_hir_analysis/src/check/coercion.rs
+++ b/compiler/rustc_hir_analysis/src/check/coercion.rs
@@ -764,8 +764,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         {
             if a_data.principal_def_id() == b_data.principal_def_id() {
                 return self.unify_and(a, b, |_| vec![]);
-            } else {
-                bug!("dyn* trait upcasting is not supported");
+            } else if !self.tcx().features().trait_upcasting {
+                let mut err = feature_err(
+                    &self.tcx.sess.parse_sess,
+                    sym::trait_upcasting,
+                    self.cause.span,
+                    &format!(
+                        "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
+                    ),
+                );
+                err.emit();
             }
         }
 
diff --git a/src/test/ui/dyn-star/upcast.rs b/src/test/ui/dyn-star/upcast.rs
new file mode 100644
index 00000000000..cee76ada7df
--- /dev/null
+++ b/src/test/ui/dyn-star/upcast.rs
@@ -0,0 +1,33 @@
+// run-pass
+
+#![feature(dyn_star, trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: Bar {
+    fn hello(&self);
+}
+
+trait Bar {
+    fn world(&self);
+}
+
+struct W(usize);
+
+impl Foo for W {
+    fn hello(&self) {
+        println!("hello!");
+    }
+}
+
+impl Bar for W {
+    fn world(&self) {
+        println!("world!");
+    }
+}
+
+fn main() {
+    let w: dyn* Foo = W(0);
+    w.hello();
+    let w: dyn* Bar = w;
+    w.world();
+}