mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
trans: use sizing_type_of for interacting with potentially incomplete types.
This commit is contained in:
parent
d492d09f31
commit
1d7c9bd137
@ -61,6 +61,21 @@ pub enum ArgKind {
|
||||
pub struct ArgType {
|
||||
pub kind: ArgKind,
|
||||
/// Original LLVM type
|
||||
pub original_ty: Type,
|
||||
/// Sizing LLVM type (pointers are opaque).
|
||||
/// Unlike original_ty, this is guaranteed to be complete.
|
||||
///
|
||||
/// For example, while we're computing the function pointer type in
|
||||
/// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
|
||||
/// The field type will likely end up being `void(%Foo)*`, but we cannot
|
||||
/// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
|
||||
/// until `%Foo` is completed by having all of its field types inserted,
|
||||
/// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
|
||||
/// with opaque ones, resulting in `{i8*}` for `Foo`.
|
||||
/// ABI-specific logic can then look at the size, alignment and fields of
|
||||
/// `{i8*}` in order to determine how the argument will be passed.
|
||||
/// Only later will `original_ty` aka `%Foo` be used in the LLVM function
|
||||
/// pointer type, without ever having introspected it.
|
||||
pub ty: Type,
|
||||
/// Coerced LLVM Type
|
||||
pub cast: Option<Type>,
|
||||
@ -71,9 +86,10 @@ pub struct ArgType {
|
||||
}
|
||||
|
||||
impl ArgType {
|
||||
fn new(ty: Type) -> ArgType {
|
||||
fn new(original_ty: Type, ty: Type) -> ArgType {
|
||||
ArgType {
|
||||
kind: Direct,
|
||||
original_ty: original_ty,
|
||||
ty: ty,
|
||||
cast: None,
|
||||
pad: None,
|
||||
@ -90,14 +106,6 @@ impl ArgType {
|
||||
}
|
||||
}
|
||||
|
||||
fn c_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
|
||||
if ty.is_bool() {
|
||||
Type::i1(cx)
|
||||
} else {
|
||||
type_of::type_of(cx, ty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata describing how the arguments to a native function
|
||||
/// should be passed in order to respect the native ABI.
|
||||
///
|
||||
@ -147,13 +155,6 @@ impl FnType {
|
||||
Aapcs => llvm::CCallConv,
|
||||
};
|
||||
|
||||
let rty = match sig.output {
|
||||
ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
|
||||
c_type_of(ccx, ret_ty)
|
||||
}
|
||||
_ => Type::void(ccx)
|
||||
};
|
||||
|
||||
let mut inputs = &sig.inputs[..];
|
||||
let extra_args = if abi == RustCall {
|
||||
assert!(!sig.variadic && extra_args.is_empty());
|
||||
@ -173,30 +174,45 @@ impl FnType {
|
||||
extra_args
|
||||
};
|
||||
|
||||
let arg_of = |ty: Ty<'tcx>| {
|
||||
if ty.is_bool() {
|
||||
let llty = Type::i1(ccx);
|
||||
let mut arg = ArgType::new(llty, llty);
|
||||
arg.attr = Some(llvm::Attribute::ZExt);
|
||||
arg
|
||||
} else {
|
||||
ArgType::new(type_of::type_of(ccx, ty),
|
||||
type_of::sizing_type_of(ccx, ty))
|
||||
}
|
||||
};
|
||||
|
||||
let ret = match sig.output {
|
||||
ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
|
||||
arg_of(ret_ty)
|
||||
}
|
||||
_ => ArgType::new(Type::void(ccx), Type::void(ccx))
|
||||
};
|
||||
|
||||
let mut args = Vec::with_capacity(inputs.len() + extra_args.len());
|
||||
for ty in inputs.iter().chain(extra_args.iter()) {
|
||||
let llty = c_type_of(ccx, ty);
|
||||
let arg = arg_of(ty);
|
||||
if type_is_fat_ptr(ccx.tcx(), ty) {
|
||||
args.extend(llty.field_types().into_iter().map(ArgType::new));
|
||||
let original = arg.original_ty.field_types();
|
||||
let sizing = arg.ty.field_types();
|
||||
args.extend(original.into_iter().zip(sizing)
|
||||
.map(|(o, s)| ArgType::new(o, s)));
|
||||
} else {
|
||||
args.push(ArgType::new(llty));
|
||||
args.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let mut fty = FnType {
|
||||
args: args,
|
||||
ret: ArgType::new(rty),
|
||||
ret: ret,
|
||||
variadic: sig.variadic,
|
||||
cconv: cconv
|
||||
};
|
||||
|
||||
// Add ZExt attributes to i1 arguments and returns.
|
||||
for arg in Some(&mut fty.ret).into_iter().chain(&mut fty.args) {
|
||||
if arg.ty == Type::i1(ccx) {
|
||||
arg.attr = Some(llvm::Attribute::ZExt);
|
||||
}
|
||||
}
|
||||
|
||||
if abi == Rust || abi == RustCall {
|
||||
let fixup = |arg: &mut ArgType| {
|
||||
if !arg.ty.is_aggregate() {
|
||||
@ -213,13 +229,12 @@ impl FnType {
|
||||
arg.cast = Some(Type::ix(ccx, size * 8));
|
||||
}
|
||||
};
|
||||
if let ty::FnConverging(ret_ty) = sig.output {
|
||||
if fty.ret.ty != Type::void(ccx) {
|
||||
// Fat pointers are returned by-value.
|
||||
if !type_is_fat_ptr(ccx.tcx(), ret_ty) &&
|
||||
fty.ret.ty != Type::void(ccx) {
|
||||
if !type_is_fat_ptr(ccx.tcx(), sig.output.unwrap()) {
|
||||
fixup(&mut fty.ret);
|
||||
}
|
||||
};
|
||||
}
|
||||
for arg in &mut fty.args {
|
||||
fixup(arg);
|
||||
}
|
||||
@ -256,10 +271,10 @@ impl FnType {
|
||||
let mut llargument_tys = Vec::new();
|
||||
|
||||
let llreturn_ty = if self.ret.is_indirect() {
|
||||
llargument_tys.push(self.ret.ty.ptr_to());
|
||||
llargument_tys.push(self.ret.original_ty.ptr_to());
|
||||
Type::void(ccx)
|
||||
} else {
|
||||
self.ret.cast.unwrap_or(self.ret.ty)
|
||||
self.ret.cast.unwrap_or(self.ret.original_ty)
|
||||
};
|
||||
|
||||
for arg in &self.args {
|
||||
@ -272,9 +287,9 @@ impl FnType {
|
||||
}
|
||||
|
||||
let llarg_ty = if arg.is_indirect() {
|
||||
arg.ty.ptr_to()
|
||||
arg.original_ty.ptr_to()
|
||||
} else {
|
||||
arg.cast.unwrap_or(arg.ty)
|
||||
arg.cast.unwrap_or(arg.original_ty)
|
||||
};
|
||||
|
||||
llargument_tys.push(llarg_ty);
|
||||
|
@ -203,7 +203,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
i,
|
||||
Value(llarg_rust),
|
||||
rust_indirect,
|
||||
arg_ty.ty);
|
||||
arg_ty);
|
||||
|
||||
// Ensure that we always have the Rust value indirectly,
|
||||
// because it makes bitcasting easier.
|
||||
@ -261,12 +261,9 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
// type to match because some ABIs will use a different type than
|
||||
// the Rust type. e.g., a {u32,u32} struct could be returned as
|
||||
// u64.
|
||||
if fn_type.ret.ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
|
||||
let llrust_ret_ty = fn_type.ret.ty;
|
||||
let llforeign_ret_ty = match fn_type.ret.cast {
|
||||
Some(ty) => ty,
|
||||
None => fn_type.ret.ty
|
||||
};
|
||||
let llrust_ret_ty = fn_type.ret.original_ty;
|
||||
if llrust_ret_ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
|
||||
let llforeign_ret_ty = fn_type.ret.cast.unwrap_or(llrust_ret_ty);
|
||||
|
||||
debug!("llretptr={:?}", Value(llretptr));
|
||||
debug!("llforeign_retval={:?}", Value(llforeign_retval));
|
||||
@ -625,12 +622,9 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
None, Some(attributes));
|
||||
|
||||
// Get the return value where the foreign fn expects it.
|
||||
let llforeign_ret_ty = match fn_ty.ret.cast {
|
||||
Some(ty) => ty,
|
||||
None => fn_ty.ret.ty
|
||||
};
|
||||
let llforeign_ret_ty = fn_ty.ret.cast.unwrap_or(fn_ty.ret.original_ty);
|
||||
match foreign_outptr {
|
||||
None if fn_ty.ret.ty == Type::void(ccx) => {
|
||||
None if llforeign_ret_ty == Type::void(ccx) => {
|
||||
// Function returns `()` or `bot`, which in Rust is the LLVM
|
||||
// type "{}" but in foreign ABIs is "Void".
|
||||
builder.ret_void();
|
||||
|
@ -108,7 +108,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
|
||||
|
||||
let llsizingty = match t.sty {
|
||||
_ if !type_is_sized(cx.tcx(), t) => {
|
||||
Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
|
||||
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false)
|
||||
}
|
||||
|
||||
ty::TyBool => Type::bool(cx),
|
||||
@ -123,7 +123,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
|
||||
if type_is_sized(cx.tcx(), ty) {
|
||||
Type::i8p(cx)
|
||||
} else {
|
||||
Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
|
||||
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,6 +177,18 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
|
||||
llsizingty
|
||||
}
|
||||
|
||||
fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
|
||||
let unsized_part = ccx.tcx().struct_tail(ty);
|
||||
match unsized_part.sty {
|
||||
ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
|
||||
Type::uint_from_ty(ccx, ast::UintTy::Us)
|
||||
}
|
||||
ty::TyTrait(_) => Type::vtable_ptr(ccx),
|
||||
_ => unreachable!("Unexpected tail in unsized_info_ty: {:?} for ty={:?}",
|
||||
unsized_part, ty)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
|
||||
if t.is_bool() {
|
||||
Type::i1(cx)
|
||||
@ -283,16 +295,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
|
||||
cx.tn().find_type("str_slice").unwrap()
|
||||
} else {
|
||||
let ptr_ty = in_memory_type_of(cx, ty).ptr_to();
|
||||
let unsized_part = cx.tcx().struct_tail(ty);
|
||||
let info_ty = match unsized_part.sty {
|
||||
ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
|
||||
Type::uint_from_ty(cx, ast::UintTy::Us)
|
||||
}
|
||||
ty::TyTrait(_) => Type::vtable_ptr(cx),
|
||||
_ => panic!("Unexpected type returned from \
|
||||
struct_tail: {:?} for ty={:?}",
|
||||
unsized_part, ty)
|
||||
};
|
||||
let info_ty = unsized_info_ty(cx, ty);
|
||||
Type::struct_(cx, &[ptr_ty, info_ty], false)
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user