Implement simple codegen for unsized rvalues.
This commit is contained in:
parent
e2b95cb70e
commit
800f2c13a3
@ -189,6 +189,8 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
|
||||
let cx = bx.cx;
|
||||
if self.is_indirect() {
|
||||
OperandValue::Ref(val, self.layout.align).store(bx, dst)
|
||||
} else if self.is_unsized_indirect() {
|
||||
bug!("unsized ArgType must be handled through store_fn_arg");
|
||||
} else if let PassMode::Cast(cast) = self.mode {
|
||||
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
|
||||
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
||||
@ -246,6 +248,9 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
|
||||
PassMode::Pair(..) => {
|
||||
OperandValue::Pair(next(), next()).store(bx, dst);
|
||||
}
|
||||
PassMode::UnsizedIndirect(..) => {
|
||||
OperandValue::UnsizedRef(next(), next()).store(bx, dst);
|
||||
}
|
||||
PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => {
|
||||
self.store(bx, next(), dst);
|
||||
}
|
||||
@ -302,6 +307,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
// Don't pass the vtable, it's not an argument of the virtual fn.
|
||||
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
|
||||
if arg_idx == Some(0) {
|
||||
if layout.is_unsized() {
|
||||
unimplemented!("by-value trait object is not \
|
||||
yet implemented in #![feature(unsized_locals)]");
|
||||
}
|
||||
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
|
||||
// `Box<dyn Trait>` has a few newtype wrappers around the raw
|
||||
// pointer, so we'd have to "dig down" to find `*dyn Trait`.
|
||||
@ -538,7 +547,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
}
|
||||
|
||||
let size = arg.layout.size;
|
||||
if size > layout::Pointer.size(cx) {
|
||||
if arg.layout.is_unsized() {
|
||||
arg.make_unsized_indirect(None);
|
||||
} else if size > layout::Pointer.size(cx) {
|
||||
arg.make_indirect();
|
||||
} else {
|
||||
// We want to pass small aggregates as immediates, but using
|
||||
@ -584,6 +595,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
llargument_tys.push(self.ret.memory_ty(cx).ptr_to());
|
||||
Type::void(cx)
|
||||
}
|
||||
PassMode::UnsizedIndirect(..) => bug!("return type must be sized"),
|
||||
};
|
||||
|
||||
for arg in &self.args {
|
||||
@ -600,6 +612,13 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
|
||||
continue;
|
||||
}
|
||||
PassMode::UnsizedIndirect(..) => {
|
||||
let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
|
||||
let ptr_layout = cx.layout_of(ptr_ty);
|
||||
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
|
||||
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
|
||||
continue;
|
||||
}
|
||||
PassMode::Cast(cast) => cast.llvm_type(cx),
|
||||
PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(),
|
||||
};
|
||||
@ -651,6 +670,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
PassMode::Ignore => {}
|
||||
PassMode::Direct(ref attrs) |
|
||||
PassMode::Indirect(ref attrs) => apply(attrs),
|
||||
PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
|
||||
apply(attrs);
|
||||
apply(extra_attrs);
|
||||
}
|
||||
PassMode::Pair(ref a, ref b) => {
|
||||
apply(a);
|
||||
apply(b);
|
||||
@ -695,6 +718,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
|
||||
PassMode::Ignore => {}
|
||||
PassMode::Direct(ref attrs) |
|
||||
PassMode::Indirect(ref attrs) => apply(attrs),
|
||||
PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
|
||||
apply(attrs);
|
||||
apply(extra_attrs);
|
||||
}
|
||||
PassMode::Pair(ref a, ref b) => {
|
||||
apply(a);
|
||||
apply(b);
|
||||
|
@ -295,7 +295,7 @@ pub fn coerce_unsized_into(
|
||||
OperandValue::Immediate(base) => {
|
||||
unsize_thin_ptr(bx, base, src_ty, dst_ty)
|
||||
}
|
||||
OperandValue::Ref(..) => bug!()
|
||||
OperandValue::Ref(..) | OperandValue::UnsizedRef(..) => bug!()
|
||||
};
|
||||
OperandValue::Pair(base, info).store(bx, dst);
|
||||
};
|
||||
|
@ -32,7 +32,7 @@ use syntax_pos::Pos;
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use super::place::PlaceRef;
|
||||
use super::operand::OperandRef;
|
||||
use super::operand::OperandValue::{Pair, Ref, Immediate};
|
||||
use super::operand::OperandValue::{Pair, Ref, UnsizedRef, Immediate};
|
||||
|
||||
impl FunctionCx<'a, 'll, 'tcx> {
|
||||
pub fn codegen_block(&mut self, bb: mir::BasicBlock) {
|
||||
@ -234,6 +234,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE));
|
||||
if let Ref(llval, align) = op.val {
|
||||
bx.load(llval, align)
|
||||
} else if let UnsizedRef(..) = op.val {
|
||||
bug!("return type must be sized");
|
||||
} else {
|
||||
op.immediate_or_packed_pair(&bx)
|
||||
}
|
||||
@ -249,6 +251,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
layout: cg_place.layout
|
||||
}
|
||||
}
|
||||
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
|
||||
};
|
||||
let llslot = match op.val {
|
||||
Immediate(_) | Pair(..) => {
|
||||
@ -261,11 +264,14 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
"return place is unaligned!");
|
||||
llval
|
||||
}
|
||||
UnsizedRef(..) => bug!("return type must be sized"),
|
||||
};
|
||||
bx.load(
|
||||
bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()),
|
||||
self.fn_ty.ret.layout.align)
|
||||
}
|
||||
|
||||
PassMode::UnsizedIndirect(..) => bug!("return value must be sized"),
|
||||
};
|
||||
bx.ret(llval);
|
||||
}
|
||||
@ -607,6 +613,10 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
op.val.store(&bx, tmp);
|
||||
op.val = Ref(tmp.llval, tmp.align);
|
||||
}
|
||||
(&mir::Operand::Copy(_), UnsizedRef(..)) |
|
||||
(&mir::Operand::Constant(_), UnsizedRef(..)) => {
|
||||
bug!("tried to pass an unsized argument by copy or constant")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -657,6 +667,15 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
}
|
||||
_ => bug!("codegen_argument: {:?} invalid for pair arugment", op)
|
||||
}
|
||||
} else if let PassMode::UnsizedIndirect(..) = arg.mode {
|
||||
match op.val {
|
||||
UnsizedRef(a, b) => {
|
||||
llargs.push(a);
|
||||
llargs.push(b);
|
||||
return;
|
||||
}
|
||||
_ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op)
|
||||
}
|
||||
}
|
||||
|
||||
// Force by-ref if we have to load through a cast pointer.
|
||||
@ -686,6 +705,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
(llval, align, true)
|
||||
}
|
||||
}
|
||||
UnsizedRef(..) =>
|
||||
bug!("codegen_argument: tried to pass unsized operand to sized argument"),
|
||||
};
|
||||
|
||||
if by_ref && !arg.is_indirect() {
|
||||
@ -727,6 +748,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
let field_ptr = tuple_ptr.project_field(bx, i);
|
||||
self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]);
|
||||
}
|
||||
} else if let UnsizedRef(..) = tuple.val {
|
||||
bug!("closure arguments must be sized")
|
||||
} else {
|
||||
// If the tuple is immediate, the elements are as well.
|
||||
for i in 0..tuple.layout.fields.count() {
|
||||
@ -820,6 +843,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
let dest = if let mir::Place::Local(index) = *dest {
|
||||
match self.locals[index] {
|
||||
LocalRef::Place(dest) => dest,
|
||||
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
|
||||
LocalRef::Operand(None) => {
|
||||
// Handle temporary places, specifically Operand ones, as
|
||||
// they don't have allocas
|
||||
@ -871,6 +895,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
if let mir::Place::Local(index) = *dst {
|
||||
match self.locals[index] {
|
||||
LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
|
||||
LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
|
||||
LocalRef::Operand(None) => {
|
||||
let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst));
|
||||
assert!(!dst_layout.ty.has_erasable_regions());
|
||||
|
@ -180,6 +180,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
|
||||
enum LocalRef<'ll, 'tcx> {
|
||||
Place(PlaceRef<'ll, 'tcx>),
|
||||
/// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).
|
||||
/// `*p` is the fat pointer that references the actual unsized place.
|
||||
/// Every time it is initialized, we have to reallocate the place
|
||||
/// and update the fat pointer. That's the reason why it is indirect.
|
||||
UnsizedPlace(PlaceRef<'ll, 'tcx>),
|
||||
Operand(Option<OperandRef<'ll, 'tcx>>),
|
||||
}
|
||||
|
||||
@ -275,17 +280,24 @@ pub fn codegen_mir(
|
||||
}
|
||||
|
||||
debug!("alloc: {:?} ({}) -> place", local, name);
|
||||
let place = PlaceRef::alloca(&bx, layout, &name.as_str());
|
||||
if dbg {
|
||||
let (scope, span) = fx.debug_loc(mir::SourceInfo {
|
||||
span: decl.source_info.span,
|
||||
scope: decl.visibility_scope,
|
||||
});
|
||||
declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
|
||||
VariableAccess::DirectVariable { alloca: place.llval },
|
||||
VariableKind::LocalVariable, span);
|
||||
if layout.is_unsized() {
|
||||
let indirect_place =
|
||||
PlaceRef::alloca_unsized_indirect(&bx, layout, &name.as_str());
|
||||
// FIXME: add an appropriate debuginfo
|
||||
LocalRef::UnsizedPlace(indirect_place)
|
||||
} else {
|
||||
let place = PlaceRef::alloca(&bx, layout, &name.as_str());
|
||||
if dbg {
|
||||
let (scope, span) = fx.debug_loc(mir::SourceInfo {
|
||||
span: decl.source_info.span,
|
||||
scope: decl.visibility_scope,
|
||||
});
|
||||
declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
|
||||
VariableAccess::DirectVariable { alloca: place.llval },
|
||||
VariableKind::LocalVariable, span);
|
||||
}
|
||||
LocalRef::Place(place)
|
||||
}
|
||||
LocalRef::Place(place)
|
||||
} else {
|
||||
// Temporary or return place
|
||||
if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() {
|
||||
@ -294,7 +306,13 @@ pub fn codegen_mir(
|
||||
LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align))
|
||||
} else if memory_locals.contains(local) {
|
||||
debug!("alloc: {:?} -> place", local);
|
||||
LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
|
||||
if layout.is_unsized() {
|
||||
let indirect_place =
|
||||
PlaceRef::alloca_unsized_indirect(&bx, layout, &format!("{:?}", local));
|
||||
LocalRef::UnsizedPlace(indirect_place)
|
||||
} else {
|
||||
LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
|
||||
}
|
||||
} else {
|
||||
// If this is an immediate local, we do not create an
|
||||
// alloca in advance. Instead we wait until we see the
|
||||
@ -531,6 +549,18 @@ fn arg_local_refs(
|
||||
bx.set_value_name(llarg, &name);
|
||||
llarg_idx += 1;
|
||||
PlaceRef::new_sized(llarg, arg.layout, arg.layout.align)
|
||||
} else if arg.is_unsized_indirect() {
|
||||
// As the storage for the indirect argument lives during
|
||||
// the whole function call, we just copy the fat pointer.
|
||||
let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
|
||||
llarg_idx += 1;
|
||||
let llextra = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
|
||||
llarg_idx += 1;
|
||||
let indirect_operand = OperandValue::Pair(llarg, llextra);
|
||||
|
||||
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout, &name);
|
||||
indirect_operand.store(&bx, tmp);
|
||||
tmp
|
||||
} else {
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout, &name);
|
||||
arg.store_fn_arg(bx, &mut llarg_idx, tmp);
|
||||
@ -632,7 +662,11 @@ fn arg_local_refs(
|
||||
);
|
||||
}
|
||||
});
|
||||
LocalRef::Place(place)
|
||||
if arg.is_unsized_indirect() {
|
||||
LocalRef::UnsizedPlace(place)
|
||||
} else {
|
||||
LocalRef::Place(place)
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ use common::{CodegenCx, C_undef, C_usize};
|
||||
use builder::{Builder, MemFlags};
|
||||
use value::Value;
|
||||
use type_of::LayoutLlvmExt;
|
||||
use type_::Type;
|
||||
use glue;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@ -36,6 +38,10 @@ pub enum OperandValue<'ll> {
|
||||
/// A reference to the actual operand. The data is guaranteed
|
||||
/// to be valid for the operand's lifetime.
|
||||
Ref(&'ll Value, Align),
|
||||
/// A reference to the unsized operand. The data is guaranteed
|
||||
/// to be valid for the operand's lifetime.
|
||||
/// The second field is the extra.
|
||||
UnsizedRef(&'ll Value, &'ll Value),
|
||||
/// A single LLVM value.
|
||||
Immediate(&'ll Value),
|
||||
/// A pair of immediate LLVM values. Used by fat pointers too.
|
||||
@ -148,7 +154,8 @@ impl OperandRef<'ll, 'tcx> {
|
||||
let (llptr, llextra) = match self.val {
|
||||
OperandValue::Immediate(llptr) => (llptr, None),
|
||||
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
|
||||
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self)
|
||||
OperandValue::Ref(..) |
|
||||
OperandValue::UnsizedRef(..) => bug!("Deref of by-Ref operand {:?}", self)
|
||||
};
|
||||
let layout = cx.layout_of(projected_ty);
|
||||
PlaceRef {
|
||||
@ -243,7 +250,8 @@ impl OperandRef<'ll, 'tcx> {
|
||||
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0, true));
|
||||
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1, true));
|
||||
}
|
||||
OperandValue::Ref(..) => bug!()
|
||||
OperandValue::Ref(..) |
|
||||
OperandValue::UnsizedRef(..) => bug!()
|
||||
}
|
||||
|
||||
OperandRef {
|
||||
@ -287,6 +295,9 @@ impl OperandValue<'ll> {
|
||||
base::memcpy_ty(bx, dest.llval, r, dest.layout,
|
||||
source_align.min(dest.align), flags)
|
||||
}
|
||||
OperandValue::UnsizedRef(..) => {
|
||||
bug!("cannot directly store unsized values");
|
||||
}
|
||||
OperandValue::Immediate(s) => {
|
||||
let val = base::from_immediate(bx, s);
|
||||
bx.store_with_flags(val, dest.llval, dest.align, flags);
|
||||
@ -300,6 +311,35 @@ impl OperandValue<'ll> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_unsized(self, bx: &Builder<'a, 'll, 'tcx>, indirect_dest: PlaceRef<'ll, 'tcx>) {
|
||||
debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
|
||||
let flags = MemFlags::empty();
|
||||
|
||||
// `indirect_dest` must have `*mut T` type. We extract `T` out of it.
|
||||
let unsized_ty = indirect_dest.layout.ty.builtin_deref(true)
|
||||
.unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)).ty;
|
||||
|
||||
let (llptr, llextra) =
|
||||
if let OperandValue::UnsizedRef(llptr, llextra) = self {
|
||||
(llptr, llextra)
|
||||
} else {
|
||||
bug!("store_unsized called with a sized value")
|
||||
};
|
||||
|
||||
// FIXME: choose an appropriate alignment, or use dynamic align somehow
|
||||
let max_align = Align::from_bits(128, 128).unwrap();
|
||||
let min_align = Align::from_bits(8, 8).unwrap();
|
||||
|
||||
// Allocate an appropriate region on the stack, and copy the value into it
|
||||
let (llsize, _) = glue::size_and_align_of_dst(&bx, unsized_ty, Some(llextra));
|
||||
let lldst = bx.array_alloca(Type::i8(bx.cx), llsize, "unsized_tmp", max_align);
|
||||
base::call_memcpy(&bx, lldst, llptr, llsize, min_align, flags);
|
||||
|
||||
// Store the allocated region and the extra to the indirect place.
|
||||
let indirect_operand = OperandValue::Pair(lldst, llextra);
|
||||
indirect_operand.store(&bx, indirect_dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionCx<'a, 'll, 'tcx> {
|
||||
@ -320,7 +360,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
LocalRef::Operand(None) => {
|
||||
bug!("use of {:?} before def", place);
|
||||
}
|
||||
LocalRef::Place(..) => {
|
||||
LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
|
||||
// use path below
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ impl PlaceRef<'ll, 'tcx> {
|
||||
layout: TyLayout<'tcx>,
|
||||
align: Align,
|
||||
) -> PlaceRef<'ll, 'tcx> {
|
||||
assert!(!layout.is_unsized());
|
||||
PlaceRef {
|
||||
llval,
|
||||
llextra: None,
|
||||
@ -77,10 +78,21 @@ impl PlaceRef<'ll, 'tcx> {
|
||||
pub fn alloca(bx: &Builder<'a, 'll, 'tcx>, layout: TyLayout<'tcx>, name: &str)
|
||||
-> PlaceRef<'ll, 'tcx> {
|
||||
debug!("alloca({:?}: {:?})", name, layout);
|
||||
assert!(!layout.is_unsized(), "tried to statically allocate unsized place");
|
||||
let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align);
|
||||
Self::new_sized(tmp, layout, layout.align)
|
||||
}
|
||||
|
||||
/// Returns a place for an indirect reference to an unsized place.
|
||||
pub fn alloca_unsized_indirect(bx: &Builder<'a, 'll, 'tcx>, layout: TyLayout<'tcx>, name: &str)
|
||||
-> PlaceRef<'ll, 'tcx> {
|
||||
debug!("alloca_unsized_indirect({:?}: {:?})", name, layout);
|
||||
assert!(layout.is_unsized(), "tried to allocate indirect place for sized values");
|
||||
let ptr_ty = bx.cx.tcx.mk_mut_ptr(layout.ty);
|
||||
let ptr_layout = bx.cx.layout_of(ptr_ty);
|
||||
Self::alloca(bx, ptr_layout, name)
|
||||
}
|
||||
|
||||
pub fn len(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Value {
|
||||
if let layout::FieldPlacement::Array { count, .. } = self.layout.fields {
|
||||
if self.layout.is_unsized() {
|
||||
@ -97,7 +109,7 @@ impl PlaceRef<'ll, 'tcx> {
|
||||
pub fn load(&self, bx: &Builder<'a, 'll, 'tcx>) -> OperandRef<'ll, 'tcx> {
|
||||
debug!("PlaceRef::load: {:?}", self);
|
||||
|
||||
assert_eq!(self.llextra, None);
|
||||
assert_eq!(self.llextra.is_some(), self.layout.is_unsized());
|
||||
|
||||
if self.layout.is_zst() {
|
||||
return OperandRef::new_zst(bx.cx, self.layout);
|
||||
@ -119,7 +131,9 @@ impl PlaceRef<'ll, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let val = if self.layout.is_llvm_immediate() {
|
||||
let val = if let Some(llextra) = self.llextra {
|
||||
OperandValue::UnsizedRef(self.llval, llextra)
|
||||
} else if self.layout.is_llvm_immediate() {
|
||||
let mut const_llval = None;
|
||||
unsafe {
|
||||
if let Some(global) = llvm::LLVMIsAGlobalVariable(self.llval) {
|
||||
@ -424,6 +438,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
LocalRef::Place(place) => {
|
||||
return place;
|
||||
}
|
||||
LocalRef::UnsizedPlace(place) => {
|
||||
return place.load(bx).deref(&cx);
|
||||
}
|
||||
LocalRef::Operand(..) => {
|
||||
bug!("using operand local {:?} as place", place);
|
||||
}
|
||||
|
@ -87,6 +87,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
let source = PlaceRef::new_sized(llref, operand.layout, align);
|
||||
base::coerce_unsized_into(&bx, source, dest);
|
||||
}
|
||||
OperandValue::UnsizedRef(..) => {
|
||||
bug!("unsized coercion on an unsized rvalue")
|
||||
}
|
||||
}
|
||||
bx
|
||||
}
|
||||
@ -175,6 +178,26 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn codegen_rvalue_unsized(&mut self,
|
||||
bx: Builder<'a, 'll, 'tcx>,
|
||||
indirect_dest: PlaceRef<'ll, 'tcx>,
|
||||
rvalue: &mir::Rvalue<'tcx>)
|
||||
-> Builder<'a, 'll, 'tcx>
|
||||
{
|
||||
debug!("codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
|
||||
indirect_dest.llval, rvalue);
|
||||
|
||||
match *rvalue {
|
||||
mir::Rvalue::Use(ref operand) => {
|
||||
let cg_operand = self.codegen_operand(&bx, operand);
|
||||
cg_operand.val.store_unsized(&bx, indirect_dest);
|
||||
bx
|
||||
}
|
||||
|
||||
_ => bug!("unsized assignment other than Rvalue::Use"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn codegen_rvalue_operand(&mut self,
|
||||
bx: Builder<'a, 'll, 'tcx>,
|
||||
rvalue: &mir::Rvalue<'tcx>)
|
||||
@ -245,6 +268,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
bug!("by-ref operand {:?} in codegen_rvalue_operand",
|
||||
operand);
|
||||
}
|
||||
OperandValue::UnsizedRef(..) => {
|
||||
bug!("unsized coercion on an unsized rvalue")
|
||||
}
|
||||
}
|
||||
}
|
||||
mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => {
|
||||
|
@ -31,6 +31,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
LocalRef::Place(cg_dest) => {
|
||||
self.codegen_rvalue(bx, cg_dest, rvalue)
|
||||
}
|
||||
LocalRef::UnsizedPlace(cg_indirect_dest) => {
|
||||
self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
|
||||
}
|
||||
LocalRef::Operand(None) => {
|
||||
let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
|
||||
self.locals[index] = LocalRef::Operand(Some(operand));
|
||||
@ -61,12 +64,16 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
||||
mir::StatementKind::StorageLive(local) => {
|
||||
if let LocalRef::Place(cg_place) = self.locals[local] {
|
||||
cg_place.storage_live(&bx);
|
||||
} else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
|
||||
cg_indirect_place.storage_live(&bx);
|
||||
}
|
||||
bx
|
||||
}
|
||||
mir::StatementKind::StorageDead(local) => {
|
||||
if let LocalRef::Place(cg_place) = self.locals[local] {
|
||||
cg_place.storage_dead(&bx);
|
||||
} else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
|
||||
cg_indirect_place.storage_dead(&bx);
|
||||
}
|
||||
bx
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ pub enum PassMode {
|
||||
Cast(CastTarget),
|
||||
/// Pass the argument indirectly via a hidden pointer.
|
||||
Indirect(ArgAttributes),
|
||||
/// Pass the unsized argument indirectly via a hidden pointer.
|
||||
UnsizedIndirect(ArgAttributes, ArgAttributes),
|
||||
}
|
||||
|
||||
// Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
|
||||
@ -381,6 +383,25 @@ impl<'a, Ty> ArgType<'a, Ty> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_unsized_indirect(&mut self, vtable_size: Option<Size>) {
|
||||
self.make_indirect();
|
||||
|
||||
let attrs = if let PassMode::Indirect(attrs) = self.mode {
|
||||
attrs
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let mut extra_attrs = ArgAttributes::new();
|
||||
if let Some(vtable_size) = vtable_size {
|
||||
extra_attrs.set(ArgAttribute::NoAlias)
|
||||
.set(ArgAttribute::NonNull);
|
||||
extra_attrs.pointee_size = vtable_size;
|
||||
}
|
||||
|
||||
self.mode = PassMode::UnsizedIndirect(attrs, extra_attrs);
|
||||
}
|
||||
|
||||
pub fn extend_integer_width_to(&mut self, bits: u64) {
|
||||
// Only integers have signedness
|
||||
if let Abi::Scalar(ref scalar) = self.layout.abi {
|
||||
@ -414,6 +435,13 @@ impl<'a, Ty> ArgType<'a, Ty> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unsized_indirect(&self) -> bool {
|
||||
match self.mode {
|
||||
PassMode::UnsizedIndirect(..) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ignore(&self) -> bool {
|
||||
self.mode == PassMode::Ignore
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: C, fty: &mut FnType<'a, Ty>, flavor: Flav
|
||||
PassMode::Indirect(_) => continue,
|
||||
PassMode::Direct(ref mut attrs) => attrs,
|
||||
PassMode::Pair(..) |
|
||||
PassMode::UnsizedIndirect(..) |
|
||||
PassMode::Cast(_) => {
|
||||
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
fn gen_foo() -> Box<fmt::Display> {
|
||||
Box::new(Box::new("foo"))
|
||||
}
|
||||
|
||||
fn foo(x: fmt::Display) {
|
||||
assert_eq!(x.to_string(), "foo");
|
||||
}
|
||||
|
||||
fn foo_indirect(x: fmt::Display) {
|
||||
foo(x);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo(*gen_foo());
|
||||
foo_indirect(*gen_foo());
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
foo(x);
|
||||
}
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
let y: fmt::Display = *gen_foo();
|
||||
foo(x);
|
||||
foo(y);
|
||||
}
|
||||
|
||||
{
|
||||
let mut cnt: usize = 3;
|
||||
let x = loop {
|
||||
let x: fmt::Display = *gen_foo();
|
||||
if cnt == 0 {
|
||||
break x;
|
||||
} else {
|
||||
cnt -= 1;
|
||||
}
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
let x = if true {
|
||||
x
|
||||
} else {
|
||||
*gen_foo()
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
}
|
17
src/test/run-pass/unsized-locals/reference-unsized-locals.rs
Normal file
17
src/test/run-pass/unsized-locals/reference-unsized-locals.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn main() {
|
||||
let foo: Box<[u8]> = Box::new(*b"foo");
|
||||
let foo: [u8] = *foo;
|
||||
assert_eq!(&foo, b"foo" as &[u8]);
|
||||
}
|
16
src/test/run-pass/unsized-locals/simple-unsized-locals.rs
Normal file
16
src/test/run-pass/unsized-locals/simple-unsized-locals.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn main() {
|
||||
let foo: Box<[u8]> = Box::new(*b"foo");
|
||||
let _foo: [u8] = *foo;
|
||||
}
|
45
src/test/run-pass/unsized-locals/unsized-exprs.rs
Normal file
45
src/test/run-pass/unsized-locals/unsized-exprs.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_tuple_coercion, unsized_locals)]
|
||||
|
||||
struct A<X: ?Sized>(X);
|
||||
|
||||
fn udrop<T: ?Sized>(_x: T) {}
|
||||
fn foo() -> Box<[u8]> {
|
||||
Box::new(*b"foo")
|
||||
}
|
||||
fn tfoo() -> Box<(i32, [u8])> {
|
||||
Box::new((42, *b"foo"))
|
||||
}
|
||||
fn afoo() -> Box<A<[u8]>> {
|
||||
Box::new(A(*b"foo"))
|
||||
}
|
||||
|
||||
impl std::ops::Add<i32> for A<[u8]> {
|
||||
type Output = ();
|
||||
fn add(self, _rhs: i32) -> Self::Output {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
udrop::<[u8]>(loop {
|
||||
break *foo();
|
||||
});
|
||||
udrop::<[u8]>(if true {
|
||||
*foo()
|
||||
} else {
|
||||
*foo()
|
||||
});
|
||||
udrop::<[u8]>({*foo()});
|
||||
#[allow(unused_parens)]
|
||||
udrop::<[u8]>((*foo()));
|
||||
udrop::<[u8]>((*tfoo()).1);
|
||||
*afoo() + 42;
|
||||
}
|
20
src/test/run-pass/unsized-locals/unsized-parameters.rs
Normal file
20
src/test/run-pass/unsized-locals/unsized-parameters.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
pub fn f0(_f: dyn FnOnce()) {}
|
||||
pub fn f1(_s: str) {}
|
||||
pub fn f2((_x, _y): (i32, [i32])) {}
|
||||
|
||||
fn main() {
|
||||
let foo = "foo".to_string().into_boxed_str();
|
||||
f1(*foo);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user