Auto merge of #51131 - qnighy:unsized-locals, r=eddyb

Implement Unsized Rvalues

This PR is the first step to implement RFC1909: unsized rvalues (#48055).

## Implemented

- `Sized` is removed for arguments and local bindings. (under `#![feature(unsized_locals)]`)
- Unsized locations are allowed in MIR
- Unsized places and operands are correctly translated at codegen

## Not implemented in this PR

- Additional `Sized` checks:
  - tuple struct constructor (accidentally compiles now)
  - closure arguments at closure generation (accidentally compiles now)
  - upvars (ICEs now)
- Generating vtable for `fn method(self)` (ICEs now)
- VLAs: `[e; n]` where `n` isn't const
- Reduce unnecessary allocations

## Current status

- [x] Fix `__rust_probestack` (rust-lang-nursery/compiler-builtins#244)
  - [x] Get the fix merged
- [x] `#![feature(unsized_locals)]`
  - [x] Give it a tracking issue number
- [x] Lift sized checks in typeck and MIR-borrowck
  - [ ] <del>Forbid `A(unsized-expr)`</del> will be another PR
- [x] Minimum working codegen
- [x] Add more examples and fill in unimplemented codegen paths
- [ ] <del>Loosen object-safety rules (will be another PR)</del>
- [ ] <del>Implement `Box<FnOnce>` (will be another PR)</del>
- [ ] <del>Reduce temporaries (will be another PR)</del>
This commit is contained in:
bors 2018-08-19 12:21:56 +00:00
commit b355906919
40 changed files with 776 additions and 64 deletions

View File

@ -0,0 +1,180 @@
# `unsized_locals`
The tracking issue for this feature is: [#48055]
[#48055]: https://github.com/rust-lang/rust/issues/48055
------------------------
This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-coercions.md
```rust
#![feature(unsized_locals)]
use std::any::Any;
fn main() {
let x: Box<dyn Any> = Box::new(42);
let x: dyn Any = *x;
// ^ unsized local variable
// ^^ unsized temporary
foo(x);
}
fn foo(_: dyn Any) {}
// ^^^^^^ unsized argument
```
The RFC still forbids the following unsized expressions:
```rust,ignore
#![feature(unsized_locals)]
use std::any::Any;
struct MyStruct<T: ?Sized> {
content: T,
}
struct MyTupleStruct<T: ?Sized>(T);
fn answer() -> Box<dyn Any> {
Box::new(42)
}
fn main() {
// You CANNOT have unsized statics.
static X: dyn Any = *answer(); // ERROR
const Y: dyn Any = *answer(); // ERROR
// You CANNOT have struct initialized unsized.
MyStruct { content: *answer() }; // ERROR
MyTupleStruct(*answer()); // ERROR
(42, *answer()); // ERROR
// You CANNOT have unsized return types.
fn my_function() -> dyn Any { *answer() } // ERROR
// You CAN have unsized local variables...
let mut x: dyn Any = *answer(); // OK
// ...but you CANNOT reassign to them.
x = *answer(); // ERROR
// You CANNOT even initialize them separately.
let y: dyn Any; // OK
y = *answer(); // ERROR
// Not mentioned in the RFC, but by-move captured variables are also Sized.
let x: dyn Any = *answer();
(move || { // ERROR
let y = x;
})();
// You CAN create a closure with unsized arguments,
// but you CANNOT call it.
// This is an implementation detail and may be changed in the future.
let f = |x: dyn Any| {};
f(*answer()); // ERROR
}
```
However, the current implementation allows `MyTupleStruct(..)` to be unsized. This will be fixed in the future.
## By-value trait objects
With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
```rust
#![feature(unsized_locals)]
trait Foo {
fn foo(self) {}
}
impl<T: ?Sized> Foo for T {}
fn main() {
let slice: Box<[i32]> = Box::new([1, 2, 3]);
<[i32] as Foo>::foo(*slice);
}
```
And `Foo` will also be object-safe. However, this object-safety is not yet implemented.
```rust,ignore
#![feature(unsized_locals)]
trait Foo {
fn foo(self) {}
}
impl<T: ?Sized> Foo for T {}
fn main () {
let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
// doesn't compile yet
<dyn Foo as Foo>::foo(*slice);
}
```
Unfortunately, this is not implemented yet.
One of the objectives of this feature is to allow `Box<dyn FnOnce>`, instead of `Box<dyn FnBox>` in the future. See [#28796] for details.
[#28796]: https://github.com/rust-lang/rust/issues/28796
## Variable length arrays
The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
```rust,ignore
#![feature(unsized_locals)]
fn mergesort<T: Ord>(a: &mut [T]) {
let mut tmp = [T; dyn a.len()];
// ...
}
fn main() {
let mut a = [3, 1, 5, 6];
mergesort(&mut a);
assert_eq!(a, [1, 3, 5, 6]);
}
```
VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
## Advisory on stack usage
It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
- When you need a by-value trait objects.
- When you really need a fast allocation of small temporary arrays.
Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
```rust
#![feature(unsized_locals)]
fn main() {
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
let _x = {{{{{{{{{{*x}}}}}}}}}};
}
```
and the code
```rust
#![feature(unsized_locals)]
fn main() {
for _ in 0..10 {
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
let _x = *x;
}
}
```
will unnecessarily extend the stack frame.

View File

@ -1454,6 +1454,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
ObligationCauseCode::VariableType(_) => {
err.note("all local variables must have a statically known size");
if !self.tcx.features().unsized_locals {
err.help("unsized locals are gated as an unstable feature");
}
}
ObligationCauseCode::SizedArgumentType => {
err.note("all function arguments must have a statically known size");
if !self.tcx.features().unsized_locals {
err.help("unsized locals are gated as an unstable feature");
}
}
ObligationCauseCode::SizedReturnType => {
err.note("the return type of a function must have a \

View File

@ -185,6 +185,8 @@ pub enum ObligationCauseCode<'tcx> {
StructInitializerSized,
/// Type of each variable must be Sized
VariableType(ast::NodeId),
/// Argument type must be Sized
SizedArgumentType,
/// Return type must be Sized
SizedReturnType,
/// Yield type must be Sized

View File

@ -203,6 +203,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
super::StructInitializerSized => Some(super::StructInitializerSized),
super::VariableType(id) => Some(super::VariableType(id)),
super::ReturnType(id) => Some(super::ReturnType(id)),
super::SizedArgumentType => Some(super::SizedArgumentType),
super::SizedReturnType => Some(super::SizedReturnType),
super::SizedYieldType => Some(super::SizedYieldType),
super::RepeatVec => Some(super::RepeatVec),

View File

@ -187,8 +187,10 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
return;
}
let cx = bx.cx;
if self.is_indirect() {
OperandValue::Ref(val, self.layout.align).store(bx, dst)
if self.is_sized_indirect() {
OperandValue::Ref(val, None, 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,7 +248,10 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
PassMode::Pair(..) => {
OperandValue::Pair(next(), next()).store(bx, dst);
}
PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => {
PassMode::Indirect(_, Some(_)) => {
OperandValue::Ref(next(), Some(next()), self.layout.align).store(bx, dst);
}
PassMode::Direct(_) | PassMode::Indirect(_, None) | 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,7 @@ 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() || size > layout::Pointer.size(cx) {
arg.make_indirect();
} else {
// We want to pass small aggregates as immediates, but using
@ -554,7 +563,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
for arg in &mut self.args {
fixup(arg);
}
if let PassMode::Indirect(ref mut attrs) = self.ret.mode {
if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode {
attrs.set(ArgAttribute::StructRet);
}
return;
@ -571,7 +580,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
if let PassMode::Pair(_, _) = arg.mode { 2 } else { 1 }
).sum();
let mut llargument_tys = Vec::with_capacity(
if let PassMode::Indirect(_) = self.ret.mode { 1 } else { 0 } + args_capacity
if let PassMode::Indirect(..) = self.ret.mode { 1 } else { 0 } + args_capacity
);
let llreturn_ty = match self.ret.mode {
@ -580,7 +589,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
self.ret.layout.immediate_llvm_type(cx)
}
PassMode::Cast(cast) => cast.llvm_type(cx),
PassMode::Indirect(_) => {
PassMode::Indirect(..) => {
llargument_tys.push(self.ret.memory_ty(cx).ptr_to());
Type::void(cx)
}
@ -600,8 +609,15 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
continue;
}
PassMode::Indirect(_, Some(_)) => {
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(),
PassMode::Indirect(_, None) => arg.memory_ty(cx).ptr_to(),
};
llargument_tys.push(llarg_ty);
}
@ -640,7 +656,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
PassMode::Direct(ref attrs) => {
attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
}
PassMode::Indirect(ref attrs) => apply(attrs),
PassMode::Indirect(ref attrs, _) => apply(attrs),
_ => {}
}
for arg in &self.args {
@ -650,7 +666,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
match arg.mode {
PassMode::Ignore => {}
PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs) => apply(attrs),
PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
apply(attrs);
apply(extra_attrs);
}
PassMode::Pair(ref a, ref b) => {
apply(a);
apply(b);
@ -670,7 +690,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
PassMode::Direct(ref attrs) => {
attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite);
}
PassMode::Indirect(ref attrs) => apply(attrs),
PassMode::Indirect(ref attrs, _) => apply(attrs),
_ => {}
}
if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi {
@ -694,7 +714,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
match arg.mode {
PassMode::Ignore => {}
PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs) => apply(attrs),
PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
apply(attrs);
apply(extra_attrs);
}
PassMode::Pair(ref a, ref b) => {
apply(a);
apply(b);

View File

@ -445,6 +445,25 @@ impl Builder<'a, 'll, 'tcx> {
}
}
pub fn array_alloca(&self,
ty: &'ll Type,
len: &'ll Value,
name: &str,
align: Align) -> &'ll Value {
self.count_insn("alloca");
unsafe {
let alloca = if name.is_empty() {
llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len, noname())
} else {
let name = SmallCStr::new(name);
llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len,
name.as_ptr())
};
llvm::LLVMSetAlignment(alloca, align.abi() as c_uint);
alloca
}
}
pub fn load(&self, ptr: &'ll Value, align: Align) -> &'ll Value {
self.count_insn("load");
unsafe {

View File

@ -605,7 +605,7 @@ pub fn codegen_intrinsic_call(
// etc.
assert!(!bx.cx.type_needs_drop(arg.layout.ty));
let (ptr, align) = match arg.val {
OperandValue::Ref(ptr, align) => (ptr, align),
OperandValue::Ref(ptr, None, align) => (ptr, align),
_ => bug!()
};
let arg = PlaceRef::new_sized(ptr, arg.layout, align);

View File

@ -875,6 +875,11 @@ extern "C" {
// Memory
pub fn LLVMBuildAlloca(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildArrayAlloca(B: &Builder<'a>,
Ty: &'a Type,
Val: &'a Value,
Name: *const c_char)
-> &'a Value;
pub fn LLVMBuildLoad(B: &Builder<'a>, PointerVal: &'a Value, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildStore(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value;

View File

@ -225,14 +225,14 @@ impl FunctionCx<'a, 'll, 'tcx> {
mir::TerminatorKind::Return => {
let llval = match self.fn_ty.ret.mode {
PassMode::Ignore | PassMode::Indirect(_) => {
PassMode::Ignore | PassMode::Indirect(..) => {
bx.ret_void();
return;
}
PassMode::Direct(_) | PassMode::Pair(..) => {
let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE));
if let Ref(llval, align) = op.val {
if let Ref(llval, _, align) = op.val {
bx.load(llval, align)
} else {
op.immediate_or_packed_pair(&bx)
@ -245,10 +245,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
LocalRef::Operand(None) => bug!("use of return before def"),
LocalRef::Place(cg_place) => {
OperandRef {
val: Ref(cg_place.llval, cg_place.align),
val: Ref(cg_place.llval, None, cg_place.align),
layout: cg_place.layout
}
}
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
};
let llslot = match op.val {
Immediate(_) | Pair(..) => {
@ -256,7 +257,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
op.val.store(&bx, scratch);
scratch.llval
}
Ref(llval, align) => {
Ref(llval, _, align) => {
assert_eq!(align.abi(), op.layout.align.abi(),
"return place is unaligned!");
llval
@ -601,11 +602,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
// The callee needs to own the argument memory if we pass it
// by-ref, so make a local copy of non-immediate constants.
match (arg, op.val) {
(&mir::Operand::Copy(_), Ref(..)) |
(&mir::Operand::Constant(_), Ref(..)) => {
(&mir::Operand::Copy(_), Ref(_, None, _)) |
(&mir::Operand::Constant(_), Ref(_, None, _)) => {
let tmp = PlaceRef::alloca(&bx, op.layout, "const");
op.val.store(&bx, tmp);
op.val = Ref(tmp.llval, tmp.align);
op.val = Ref(tmp.llval, None, tmp.align);
}
_ => {}
}
@ -657,13 +658,22 @@ impl FunctionCx<'a, 'll, 'tcx> {
}
_ => bug!("codegen_argument: {:?} invalid for pair arugment", op)
}
} else if arg.is_unsized_indirect() {
match op.val {
Ref(a, Some(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.
let (mut llval, align, by_ref) = match op.val {
Immediate(_) | Pair(..) => {
match arg.mode {
PassMode::Indirect(_) | PassMode::Cast(_) => {
PassMode::Indirect(..) | PassMode::Cast(_) => {
let scratch = PlaceRef::alloca(bx, arg.layout, "arg");
op.val.store(bx, scratch);
(scratch.llval, scratch.align, true)
@ -673,7 +683,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
}
}
}
Ref(llval, align) => {
Ref(llval, _, align) => {
if arg.is_indirect() && align.abi() < arg.layout.align.abi() {
// `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I
// think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't
@ -721,12 +731,14 @@ impl FunctionCx<'a, 'll, 'tcx> {
let tuple = self.codegen_operand(bx, operand);
// Handle both by-ref and immediate tuples.
if let Ref(llval, align) = tuple.val {
if let Ref(llval, None, align) = tuple.val {
let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align);
for i in 0..tuple.layout.fields.count() {
let field_ptr = tuple_ptr.project_field(bx, i);
self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]);
}
} else if let Ref(_, Some(_), _) = 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 +832,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 +884,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
@ -523,7 +541,7 @@ fn arg_local_refs(
}
}
let place = if arg.is_indirect() {
let place = if arg.is_sized_indirect() {
// Don't copy an indirect argument to an alloca, the caller
// already put it in a temporary alloca and gave it up.
// FIXME: lifetimes
@ -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;
@ -35,7 +37,9 @@ use super::place::PlaceRef;
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),
/// The second value, if any, is the extra data (vtable or length)
/// which indicates that it refers to an unsized rvalue.
Ref(&'ll Value, Option<&'ll Value>, Align),
/// A single LLVM value.
Immediate(&'ll Value),
/// A pair of immediate LLVM values. Used by fat pointers too.
@ -283,10 +287,13 @@ impl OperandValue<'ll> {
return;
}
match self {
OperandValue::Ref(r, source_align) => {
OperandValue::Ref(r, None, source_align) => {
base::memcpy_ty(bx, dest.llval, r, dest.layout,
source_align.min(dest.align), flags)
}
OperandValue::Ref(_, Some(_), _) => {
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 +307,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::Ref(llptr, Some(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 +356,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::Ref(self.llval, Some(llextra), self.align)
} else if self.layout.is_llvm_immediate() {
let mut const_llval = None;
unsafe {
if let Some(global) = llvm::LLVMIsAGlobalVariable(self.llval) {
@ -149,7 +163,7 @@ impl PlaceRef<'ll, 'tcx> {
};
OperandValue::Pair(load(0, a), load(1, b))
} else {
OperandValue::Ref(self.llval, self.align)
OperandValue::Ref(self.llval, None, self.align)
};
OperandRef { val, layout: self.layout }
@ -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

@ -83,10 +83,13 @@ impl FunctionCx<'a, 'll, 'tcx> {
base::coerce_unsized_into(&bx, scratch, dest);
scratch.storage_dead(&bx);
}
OperandValue::Ref(llref, align) => {
OperandValue::Ref(llref, None, align) => {
let source = PlaceRef::new_sized(llref, operand.layout, align);
base::coerce_unsized_into(&bx, source, dest);
}
OperandValue::Ref(_, Some(_), _) => {
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>)

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

@ -903,11 +903,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
);
}
self.check_rvalue(mir, rv, location);
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().sized_trait().unwrap(),
substs: tcx.mk_substs_trait(place_ty, &[]),
};
self.prove_trait_ref(trait_ref, location.interesting());
if !self.tcx().features().unsized_locals {
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().sized_trait().unwrap(),
substs: tcx.mk_substs_trait(place_ty, &[]),
};
self.prove_trait_ref(trait_ref, location.interesting());
}
}
StatementKind::SetDiscriminant {
ref place,
@ -962,6 +964,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
mir: &Mir<'tcx>,
term: &Terminator<'tcx>,
term_location: Location,
errors_buffer: &mut Option<&mut Vec<Diagnostic>>,
) {
debug!("check_terminator: {:?}", term);
let tcx = self.tcx();
@ -1041,7 +1044,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
&sig,
);
let sig = self.normalize(sig, term_location);
self.check_call_dest(mir, term, &sig, destination, term_location);
self.check_call_dest(mir, term, &sig, destination, term_location, errors_buffer);
self.prove_predicates(
sig.inputs().iter().map(|ty| ty::Predicate::WellFormed(ty)),
@ -1115,6 +1118,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
sig: &ty::FnSig<'tcx>,
destination: &Option<(Place<'tcx>, BasicBlock)>,
term_location: Location,
errors_buffer: &mut Option<&mut Vec<Diagnostic>>,
) {
let tcx = self.tcx();
match *destination {
@ -1143,6 +1147,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
terr
);
}
// When `#![feature(unsized_locals)]` is not enabled,
// this check is done at `check_local`.
if self.tcx().features().unsized_locals {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span, errors_buffer);
}
}
None => {
// FIXME(canndrew): This is_never should probably be an is_uninhabited
@ -1309,14 +1320,26 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
LocalKind::Var | LocalKind::Temp => {}
}
let span = local_decl.source_info.span;
let ty = local_decl.ty;
// When `#![feature(unsized_locals)]` is enabled, only function calls
// are checked in `check_call_dest`.
if !self.tcx().features().unsized_locals {
let span = local_decl.source_info.span;
let ty = local_decl.ty;
self.ensure_place_sized(ty, span, errors_buffer);
}
}
fn ensure_place_sized(&mut self,
ty: Ty<'tcx>,
span: Span,
errors_buffer: &mut Option<&mut Vec<Diagnostic>>) {
let tcx = self.tcx();
// Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let gcx = self.tcx().global_tcx();
let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap();
let gcx = tcx.global_tcx();
let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap();
if !erased_ty.is_sized(gcx.at(span), self.param_env) {
// in current MIR construction, all non-control-flow rvalue
// expressions evaluate through `as_temp` or `into` a return
@ -1838,7 +1861,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
location.statement_index += 1;
}
self.check_terminator(mir, block_data.terminator(), location);
self.check_terminator(mir, block_data.terminator(), location, &mut errors_buffer);
self.check_iscleanup(mir, block_data);
}
}

View File

@ -44,7 +44,9 @@ pub enum PassMode {
/// a single uniform or a pair of registers.
Cast(CastTarget),
/// Pass the argument indirectly via a hidden pointer.
Indirect(ArgAttributes),
/// The second value, if any, is for the extra data (vtable or length)
/// which indicates that it refers to an unsized rvalue.
Indirect(ArgAttributes, Option<ArgAttributes>),
}
// Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
@ -368,13 +370,19 @@ impl<'a, Ty> ArgType<'a, Ty> {
// i686-pc-windows-msvc, it results in wrong stack offsets.
// attrs.pointee_align = Some(self.layout.align);
self.mode = PassMode::Indirect(attrs);
let extra_attrs = if self.layout.is_unsized() {
Some(ArgAttributes::new())
} else {
None
};
self.mode = PassMode::Indirect(attrs, extra_attrs);
}
pub fn make_indirect_byval(&mut self) {
self.make_indirect();
match self.mode {
PassMode::Indirect(ref mut attrs) => {
PassMode::Indirect(ref mut attrs, _) => {
attrs.set(ArgAttribute::ByVal);
}
_ => unreachable!()
@ -409,7 +417,21 @@ impl<'a, Ty> ArgType<'a, Ty> {
pub fn is_indirect(&self) -> bool {
match self.mode {
PassMode::Indirect(_) => true,
PassMode::Indirect(..) => true,
_ => false
}
}
pub fn is_sized_indirect(&self) -> bool {
match self.mode {
PassMode::Indirect(_, None) => true,
_ => false
}
}
pub fn is_unsized_indirect(&self) -> bool {
match self.mode {
PassMode::Indirect(_, Some(_)) => true,
_ => false
}
}
@ -506,7 +528,7 @@ impl<'a, Ty> FnType<'a, Ty> {
a => return Err(format!("unrecognized arch \"{}\" in target specification", a))
}
if let PassMode::Indirect(ref mut attrs) = self.ret.mode {
if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode {
attrs.set(ArgAttribute::StructRet);
}

View File

@ -99,9 +99,10 @@ pub fn compute_abi_info<'a, Ty, C>(cx: C, fty: &mut FnType<'a, Ty>, flavor: Flav
for arg in &mut fty.args {
let attrs = match arg.mode {
PassMode::Ignore |
PassMode::Indirect(_) => continue,
PassMode::Indirect(_, None) => continue,
PassMode::Direct(ref mut attrs) => attrs,
PassMode::Pair(..) |
PassMode::Indirect(_, Some(_)) |
PassMode::Cast(_) => {
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
}

View File

@ -961,8 +961,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
if let PatKind::Binding(_, _, ident, _) = p.node {
let var_ty = self.assign(p.span, p.id, None);
self.fcx.require_type_is_sized(var_ty, p.span,
traits::VariableType(p.id));
if !self.fcx.tcx.features().unsized_locals {
self.fcx.require_type_is_sized(var_ty, p.span,
traits::VariableType(p.id));
}
debug!("Pattern binding {} is assigned to {} with type {:?}",
ident,
@ -1048,8 +1050,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
// The check for a non-trivial pattern is a hack to avoid duplicate warnings
// for simple cases like `fn foo(x: Trait)`,
// where we would error once on the parameter as a whole, and once on the binding `x`.
if arg.pat.simple_ident().is_none() {
fcx.require_type_is_sized(arg_ty, decl.output.span(), traits::MiscObligation);
if arg.pat.simple_ident().is_none() && !fcx.tcx.features().unsized_locals {
fcx.require_type_is_sized(arg_ty, decl.output.span(), traits::SizedArgumentType);
}
fcx.write_ty(arg.hir_id, arg_ty);

View File

@ -503,6 +503,9 @@ declare_features! (
// Allows `Self` in type definitions
(active, self_in_typedefs, "1.30.0", Some(49303), None),
// unsized rvalues at arguments and parameters
(active, unsized_locals, "1.30.0", Some(48055), None),
);
declare_features! (

View File

@ -0,0 +1,36 @@
// 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::<(i32, [u8])>((42, *foo()));
//~^ERROR E0277
udrop::<A<[u8]>>(A { 0: *foo() });
//~^ERROR E0277
}

View File

@ -0,0 +1,36 @@
// 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]>(foo()[..]);
//~^ERROR cannot move out of indexed content
// FIXME: should be error
udrop::<A<[u8]>>(A(*foo()));
}

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

View File

@ -8,6 +8,7 @@ LL | let x = t.get(); //~ ERROR the size for values of type
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where <T as Get>::Value: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -8,6 +8,7 @@ LL | fn f(p: Path) { }
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because it appears within the type `std::path::Path`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the trait bound `i32: Foo` is not satisfied
--> $DIR/E0277.rs:27:5

View File

@ -0,0 +1,15 @@
// 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.
fn f(f: FnOnce()) {}
//~^ ERROR E0277
fn main() {
}

View File

@ -0,0 +1,14 @@
error[E0277]: the size for values of type `(dyn std::ops::FnOnce() + 'static)` cannot be known at compilation time
--> $DIR/feature-gate-unsized_locals.rs:11:6
|
LL | fn f(f: FnOnce()) {}
| ^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce() + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -7,6 +7,7 @@ LL | &mut something
= help: the trait `std::marker::Sized` is not implemented for `[T]`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -8,6 +8,7 @@ LL | fn foo(self) -> &'static i32 {
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where Self: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -6,6 +6,8 @@ LL | fn _test(ref _p: str) {}
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all function arguments must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -6,6 +6,8 @@ LL | pub fn example(ref s: str) {}
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all function arguments must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -7,6 +7,8 @@ LL | fn baz(_: Self::Target) where Self: Deref {}
= help: the trait `std::marker::Sized` is not implemented for `<Self as std::ops::Deref>::Target`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where <Self as std::ops::Deref>::Target: std::marker::Sized` bound
= note: all function arguments must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `(dyn std::string::ToString + 'static)` cannot be known at compilation time
--> $DIR/issue-42312.rs:18:23
@ -16,6 +18,8 @@ LL | pub fn f(_: ToString) {}
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::string::ToString + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all function arguments must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to 2 previous errors

View File

@ -7,6 +7,7 @@ LL | fn new_struct(r: A+'static)
= help: the trait `std::marker::Sized` is not implemented for `(dyn A + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
--> $DIR/issue-5883.rs:18:8

View File

@ -7,6 +7,7 @@ LL | fn foo(_x: K) {}
= help: the trait `std::marker::Sized` is not implemented for `(dyn I + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -30,6 +30,7 @@ LL | let v = s[..2];
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0308]: mismatched types
--> $DIR/str-array-assignment.rs:19:17

View File

@ -7,6 +7,7 @@ LL | fn foo(_x: Foo + Send) {
= help: the trait `std::marker::Sized` is not implemented for `(dyn Foo + std::marker::Send + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to previous error

View File

@ -8,6 +8,7 @@ LL | let y: Y;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where Y: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:17:12
@ -41,6 +42,7 @@ LL | let y: X;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `Y` cannot be known at compilation time
--> $DIR/unsized6.rs:27:12
@ -63,6 +65,7 @@ LL | let y: X = *x1;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:34:9
@ -74,6 +77,7 @@ LL | let y = *x2;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:36:10
@ -85,6 +89,7 @@ LL | let (y, z) = (*x3, 4);
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:40:9
@ -96,6 +101,7 @@ LL | let y: X = *x1;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:42:9
@ -107,6 +113,7 @@ LL | let y = *x2;
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:44:10
@ -118,6 +125,7 @@ LL | let (y, z) = (*x3, 4);
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:48:18
@ -129,6 +137,7 @@ LL | fn g1<X: ?Sized>(x: X) {}
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:50:22
@ -140,6 +149,7 @@ LL | fn g2<X: ?Sized + T>(x: X) {}
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where X: std::marker::Sized` bound
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error: aborting due to 13 previous errors