Skip to content

Commit feb4244

Browse files
Allow dyn* upcasting
1 parent 76386bd commit feb4244

File tree

4 files changed

+97
-36
lines changed

4 files changed

+97
-36
lines changed

Diff for: compiler/rustc_codegen_ssa/src/base.rs

+48-13
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use rustc_session::Session;
3838
use rustc_span::symbol::sym;
3939
use rustc_span::Symbol;
4040
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
41-
use rustc_target::abi::{Align, VariantIdx};
41+
use rustc_target::abi::{Align, Size, VariantIdx};
4242

4343
use std::collections::BTreeSet;
4444
use std::convert::TryFrom;
@@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
150150
(&ty::Array(_, len), &ty::Slice(_)) => {
151151
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
152152
}
153-
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
153+
(
154+
&ty::Dynamic(ref data_a, _, src_dyn_kind),
155+
&ty::Dynamic(ref data_b, _, target_dyn_kind),
156+
) => {
157+
assert_eq!(src_dyn_kind, target_dyn_kind);
158+
154159
let old_info =
155160
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
156161
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>>(
166171
if let Some(entry_idx) = vptr_entry_idx {
167172
let ptr_ty = cx.type_i8p();
168173
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
169-
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
170-
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
171-
1,
172-
true,
173-
);
174+
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
174175
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
175176
let gep = bx.inbounds_gep(
176177
ptr_ty,
@@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
186187
old_info
187188
}
188189
}
189-
(_, &ty::Dynamic(ref data, ..)) => {
190-
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
191-
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
192-
1,
193-
true,
194-
);
190+
(_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
191+
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
195192
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
196193
}
197194
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
198195
}
199196
}
200197

198+
// Returns the vtable pointer type of a `dyn` or `dyn*` type
199+
fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
200+
cx: &Cx,
201+
target: Ty<'tcx>,
202+
kind: ty::DynKind,
203+
) -> <Cx as BackendTypes>::Type {
204+
cx.scalar_pair_element_backend_type(
205+
cx.layout_of(match kind {
206+
// vtable is the second field of `*mut dyn Trait`
207+
ty::Dyn => cx.tcx().mk_mut_ptr(target),
208+
// vtable is the second field of `dyn* Trait`
209+
ty::DynStar => target,
210+
}),
211+
1,
212+
true,
213+
)
214+
}
215+
201216
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
202217
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
203218
bx: &mut Bx,
@@ -247,6 +262,26 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
247262
}
248263
}
249264

265+
/// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type.
266+
pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
267+
bx: &mut Bx,
268+
src: Bx::Value,
269+
src_ty_and_layout: TyAndLayout<'tcx>,
270+
dst_ty: Ty<'tcx>,
271+
old_info: Option<Bx::Value>,
272+
) -> (Bx::Value, Bx::Value) {
273+
debug!("unsize_ptr: {:?} => {:?}", src_ty_and_layout.ty, dst_ty);
274+
assert!(matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)));
275+
// FIXME(dyn-star): this is probably not the best way to check if this is
276+
// a pointer, and really we should ensure that the value is a suitable
277+
// pointer earlier in the compilation process.
278+
let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) {
279+
Some(_) => bx.ptrtoint(src, bx.cx().type_isize()),
280+
None => bx.bitcast(src, bx.type_isize()),
281+
};
282+
(src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info))
283+
}
284+
250285
/// Coerces `src`, which is a reference to a value of type `src_ty`,
251286
/// to a value of type `dst_ty`, and stores the result in `dst`.
252287
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

Diff for: compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+6-21
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef};
44

55
use crate::base;
66
use crate::common::{self, IntPredicate};
7-
use crate::meth::get_vtable;
87
use crate::traits::*;
98
use crate::MemFlags;
109

@@ -14,7 +13,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
1413
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
1514
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
1615
use rustc_span::source_map::{Span, DUMMY_SP};
17-
use rustc_target::abi::Size;
1816

1917
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2018
#[instrument(level = "trace", skip(self, bx))]
@@ -274,27 +272,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
274272
}
275273
}
276274
mir::CastKind::DynStar => {
277-
let data = match operand.val {
275+
let (lldata, llextra) = match operand.val {
278276
OperandValue::Ref(_, _, _) => todo!(),
279-
OperandValue::Immediate(v) => v,
280-
OperandValue::Pair(_, _) => todo!(),
281-
};
282-
let trait_ref =
283-
if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
284-
data.principal()
285-
} else {
286-
bug!("Only valid to do a DynStar cast into a DynStar type")
287-
};
288-
let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
289-
let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
290-
// FIXME(dyn-star): this is probably not the best way to check if this is
291-
// a pointer, and really we should ensure that the value is a suitable
292-
// pointer earlier in the compilation process.
293-
let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
294-
Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
295-
None => data,
277+
OperandValue::Immediate(v) => (v, None),
278+
OperandValue::Pair(v, l) => (v, Some(l)),
296279
};
297-
OperandValue::Pair(data, vtable)
280+
let (lldata, llextra) =
281+
base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
282+
OperandValue::Pair(lldata, llextra)
298283
}
299284
mir::CastKind::Pointer(
300285
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,

Diff for: compiler/rustc_hir_analysis/src/check/coercion.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -764,8 +764,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
764764
{
765765
if a_data.principal_def_id() == b_data.principal_def_id() {
766766
return self.unify_and(a, b, |_| vec![]);
767-
} else {
768-
bug!("dyn* trait upcasting is not supported");
767+
} else if !self.tcx().features().trait_upcasting {
768+
let mut err = feature_err(
769+
&self.tcx.sess.parse_sess,
770+
sym::trait_upcasting,
771+
self.cause.span,
772+
&format!(
773+
"cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
774+
),
775+
);
776+
err.emit();
769777
}
770778
}
771779

Diff for: src/test/ui/dyn-star/upcast.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-pass
2+
3+
#![feature(dyn_star, trait_upcasting)]
4+
#![allow(incomplete_features)]
5+
6+
trait Foo: Bar {
7+
fn hello(&self);
8+
}
9+
10+
trait Bar {
11+
fn world(&self);
12+
}
13+
14+
struct W(usize);
15+
16+
impl Foo for W {
17+
fn hello(&self) {
18+
println!("hello!");
19+
}
20+
}
21+
22+
impl Bar for W {
23+
fn world(&self) {
24+
println!("world!");
25+
}
26+
}
27+
28+
fn main() {
29+
let w: dyn* Foo = W(0);
30+
w.hello();
31+
let w: dyn* Bar = w;
32+
w.world();
33+
}

0 commit comments

Comments
 (0)