Implement simple codegen for unsized rvalues.

This commit is contained in:
Masaki Hara 2018-05-29 00:12:55 +09:00
parent e2b95cb70e
commit 800f2c13a3
15 changed files with 388 additions and 20 deletions

View File

@ -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);

View File

@ -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);
};

View File

@ -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());

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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);
}

View File

@ -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() => {

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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);
}
}

View 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]);
}

View 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;
}

View 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;
}

View 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);
}