Skip to content

Commit 3244d53

Browse files
committed
Auto merge of #52089 - eddyb:issue-51907, r=nagisa
rustc_codegen_llvm: replace the first argument early in FnType::new_vtable. Fixes #51907 by removing the vtable pointer before the `ArgType` is even created. This allows any ABI to support trait object method calls, regardless of how it passes `*dyn Trait`. r? @nikomatsakis
2 parents 4ba5ff4 + ce94518 commit 3244d53

File tree

2 files changed

+69
-37
lines changed

2 files changed

+69
-37
lines changed

src/librustc_codegen_llvm/abi.rs

+43-37
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,12 @@ pub trait FnTypeExt<'a, 'tcx> {
261261
fn new_vtable(cx: &CodegenCx<'a, 'tcx>,
262262
sig: ty::FnSig<'tcx>,
263263
extra_args: &[Ty<'tcx>]) -> Self;
264-
fn unadjusted(cx: &CodegenCx<'a, 'tcx>,
265-
sig: ty::FnSig<'tcx>,
266-
extra_args: &[Ty<'tcx>]) -> Self;
264+
fn new_internal(
265+
cx: &CodegenCx<'a, 'tcx>,
266+
sig: ty::FnSig<'tcx>,
267+
extra_args: &[Ty<'tcx>],
268+
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgType<'tcx, Ty<'tcx>>,
269+
) -> Self;
267270
fn adjust_for_abi(&mut self,
268271
cx: &CodegenCx<'a, 'tcx>,
269272
abi: Abi);
@@ -285,40 +288,40 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
285288
fn new(cx: &CodegenCx<'a, 'tcx>,
286289
sig: ty::FnSig<'tcx>,
287290
extra_args: &[Ty<'tcx>]) -> Self {
288-
let mut fn_ty = FnType::unadjusted(cx, sig, extra_args);
289-
fn_ty.adjust_for_abi(cx, sig.abi);
290-
fn_ty
291+
FnType::new_internal(cx, sig, extra_args, |ty, _| {
292+
ArgType::new(cx.layout_of(ty))
293+
})
291294
}
292295

293296
fn new_vtable(cx: &CodegenCx<'a, 'tcx>,
294297
sig: ty::FnSig<'tcx>,
295298
extra_args: &[Ty<'tcx>]) -> Self {
296-
let mut fn_ty = FnType::unadjusted(cx, sig, extra_args);
297-
// Don't pass the vtable, it's not an argument of the virtual fn.
298-
{
299-
let self_arg = &mut fn_ty.args[0];
300-
match self_arg.mode {
301-
PassMode::Pair(data_ptr, _) => {
302-
self_arg.mode = PassMode::Direct(data_ptr);
303-
}
304-
_ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg)
305-
}
306-
307-
let pointee = self_arg.layout.ty.builtin_deref(true)
308-
.unwrap_or_else(|| {
309-
bug!("FnType::new_vtable: non-pointer self {:?}", self_arg)
310-
}).ty;
311-
let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee);
312-
self_arg.layout = cx.layout_of(fat_ptr_ty).field(cx, 0);
313-
}
314-
fn_ty.adjust_for_abi(cx, sig.abi);
315-
fn_ty
299+
FnType::new_internal(cx, sig, extra_args, |ty, arg_idx| {
300+
let mut layout = cx.layout_of(ty);
301+
// Don't pass the vtable, it's not an argument of the virtual fn.
302+
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
303+
if arg_idx == Some(0) {
304+
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
305+
// `Box<dyn Trait>` has a few newtype wrappers around the raw
306+
// pointer, so we'd have to "dig down" to find `*dyn Trait`.
307+
let pointee = layout.ty.builtin_deref(true)
308+
.unwrap_or_else(|| {
309+
bug!("FnType::new_vtable: non-pointer self {:?}", layout)
310+
}).ty;
311+
let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee);
312+
layout = cx.layout_of(fat_ptr_ty).field(cx, 0);
313+
}
314+
ArgType::new(layout)
315+
})
316316
}
317317

318-
fn unadjusted(cx: &CodegenCx<'a, 'tcx>,
319-
sig: ty::FnSig<'tcx>,
320-
extra_args: &[Ty<'tcx>]) -> Self {
321-
debug!("FnType::unadjusted({:?}, {:?})", sig, extra_args);
318+
fn new_internal(
319+
cx: &CodegenCx<'a, 'tcx>,
320+
sig: ty::FnSig<'tcx>,
321+
extra_args: &[Ty<'tcx>],
322+
mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgType<'tcx, Ty<'tcx>>,
323+
) -> Self {
324+
debug!("FnType::new_internal({:?}, {:?})", sig, extra_args);
322325

323326
use self::Abi::*;
324327
let conv = match cx.sess().target.target.adjust_abi(sig.abi) {
@@ -435,8 +438,9 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
435438
}
436439
};
437440

438-
let arg_of = |ty: Ty<'tcx>, is_return: bool| {
439-
let mut arg = ArgType::new(cx.layout_of(ty));
441+
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
442+
let is_return = arg_idx.is_none();
443+
let mut arg = mk_arg_type(ty, arg_idx);
440444
if arg.layout.is_zst() {
441445
// For some forsaken reason, x86_64-pc-windows-gnu
442446
// doesn't ignore zero-sized struct arguments.
@@ -479,14 +483,16 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
479483
arg
480484
};
481485

482-
FnType {
483-
ret: arg_of(sig.output(), true),
484-
args: inputs.iter().chain(extra_args.iter()).map(|ty| {
485-
arg_of(ty, false)
486+
let mut fn_ty = FnType {
487+
ret: arg_of(sig.output(), None),
488+
args: inputs.iter().chain(extra_args).enumerate().map(|(i, ty)| {
489+
arg_of(ty, Some(i))
486490
}).collect(),
487491
variadic: sig.variadic,
488492
conv,
489-
}
493+
};
494+
fn_ty.adjust_for_abi(cx, sig.abi);
495+
fn_ty
490496
}
491497

492498
fn adjust_for_abi(&mut self,

src/test/run-pass/issue-51907.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait Foo {
12+
extern fn borrow(&self);
13+
extern fn take(self: Box<Self>);
14+
}
15+
16+
struct Bar;
17+
impl Foo for Bar {
18+
extern fn borrow(&self) {}
19+
extern fn take(self: Box<Self>) {}
20+
}
21+
22+
fn main() {
23+
let foo: Box<dyn Foo> = Box::new(Bar);
24+
foo.borrow();
25+
foo.take()
26+
}

0 commit comments

Comments
 (0)