diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs index a0351e32259..8bf4f143faf 100644 --- a/src/librustc_trans/trans/abi.rs +++ b/src/librustc_trans/trans/abi.rs @@ -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, @@ -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); diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 870853af1ee..ed96871002a 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -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(); diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index 634d33d445e..7871ebccbd4 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -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 {