diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md new file mode 100644 index 00000000000..7a5fe5b7f28 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/unsized-locals.md @@ -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 = 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 { + content: T, +} + +struct MyTupleStruct(T); + +fn answer() -> Box { + 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 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 Foo for T {} + +fn main () { + let slice: Box = Box::new([1, 2, 3]); + // doesn't compile yet + ::foo(*slice); +} +``` + +Unfortunately, this is not implemented yet. + +One of the objectives of this feature is to allow `Box`, instead of `Box` 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(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. diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 113adda3ccb..92c66ef39ea 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -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 \ diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 08434b5f24e..ef14d6d05c2 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -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 diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 544e3f03c03..9292b42eb52 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -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), diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 44982eee86b..79343505c78 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -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` 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); diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index b0f88bd4189..cfbc2ab9007 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -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 { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 099d8562aa5..be3e0d9d4b1 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -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); diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index eec8a3a169c..d3039a05b6d 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -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; diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 4e389c3b915..5f718ae456c 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -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()); diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs index 8bb049be305..5991bb80c30 100644 --- a/src/librustc_codegen_llvm/mir/mod.rs +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -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>), } @@ -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() } diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index f8166ee6491..9537379813d 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -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 } } diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs index 6fa0845dd0c..89d41dcc8e9 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -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); } diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs index 7eac8e735ed..84427d8b40f 100644 --- a/src/librustc_codegen_llvm/mir/rvalue.rs +++ b/src/librustc_codegen_llvm/mir/rvalue.rs @@ -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>) diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index 06340a3e5d8..dd62a12553c 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -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 } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 9f790612124..ab83cfe25b5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -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>, ) { 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>, ) { 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>) { + 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); } } diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index af874b1035b..78ed4b2d615 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -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), } // 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); } diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs index 0c0040de9df..1dcaafcf77f 100644 --- a/src/librustc_target/abi/call/x86.rs +++ b/src/librustc_target/abi/call/x86.rs @@ -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) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 189859aad07..27b427f7f89 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3b9e1b3c265..e8245a553eb 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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! ( diff --git a/src/test/compile-fail/unsized-locals/unsized-exprs.rs b/src/test/compile-fail/unsized-locals/unsized-exprs.rs new file mode 100644 index 00000000000..a09ccbb407e --- /dev/null +++ b/src/test/compile-fail/unsized-locals/unsized-exprs.rs @@ -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 or the MIT license +// , 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); + +fn udrop(_x: T) {} +fn foo() -> Box<[u8]> { + Box::new(*b"foo") +} +fn tfoo() -> Box<(i32, [u8])> { + Box::new((42, *b"foo")) +} +fn afoo() -> Box> { + Box::new(A(*b"foo")) +} + +impl std::ops::Add for A<[u8]> { + type Output = (); + fn add(self, _rhs: i32) -> Self::Output {} +} + +fn main() { + udrop::<(i32, [u8])>((42, *foo())); + //~^ERROR E0277 + udrop::>(A { 0: *foo() }); + //~^ERROR E0277 +} diff --git a/src/test/compile-fail/unsized-locals/unsized-exprs2.rs b/src/test/compile-fail/unsized-locals/unsized-exprs2.rs new file mode 100644 index 00000000000..40d6e54bd89 --- /dev/null +++ b/src/test/compile-fail/unsized-locals/unsized-exprs2.rs @@ -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 or the MIT license +// , 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); + +fn udrop(_x: T) {} +fn foo() -> Box<[u8]> { + Box::new(*b"foo") +} +fn tfoo() -> Box<(i32, [u8])> { + Box::new((42, *b"foo")) +} +fn afoo() -> Box> { + Box::new(A(*b"foo")) +} + +impl std::ops::Add 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(*foo())); +} diff --git a/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs new file mode 100644 index 00000000000..e1fda427b4e --- /dev/null +++ b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs @@ -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 or the MIT license +// , 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 { + 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); + } +} diff --git a/src/test/run-pass/unsized-locals/reference-unsized-locals.rs b/src/test/run-pass/unsized-locals/reference-unsized-locals.rs new file mode 100644 index 00000000000..6ed39a78648 --- /dev/null +++ b/src/test/run-pass/unsized-locals/reference-unsized-locals.rs @@ -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 or the MIT license +// , 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]); +} diff --git a/src/test/run-pass/unsized-locals/simple-unsized-locals.rs b/src/test/run-pass/unsized-locals/simple-unsized-locals.rs new file mode 100644 index 00000000000..0b1aa6225eb --- /dev/null +++ b/src/test/run-pass/unsized-locals/simple-unsized-locals.rs @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-pass/unsized-locals/unsized-exprs.rs b/src/test/run-pass/unsized-locals/unsized-exprs.rs new file mode 100644 index 00000000000..9a5e534db25 --- /dev/null +++ b/src/test/run-pass/unsized-locals/unsized-exprs.rs @@ -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 or the MIT license +// , 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); + +fn udrop(_x: T) {} +fn foo() -> Box<[u8]> { + Box::new(*b"foo") +} +fn tfoo() -> Box<(i32, [u8])> { + Box::new((42, *b"foo")) +} +fn afoo() -> Box> { + Box::new(A(*b"foo")) +} + +impl std::ops::Add 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; +} diff --git a/src/test/run-pass/unsized-locals/unsized-parameters.rs b/src/test/run-pass/unsized-locals/unsized-parameters.rs new file mode 100644 index 00000000000..0314fe1d686 --- /dev/null +++ b/src/test/run-pass/unsized-locals/unsized-parameters.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/ui/associated-types/associated-types-unsized.stderr b/src/test/ui/associated-types/associated-types-unsized.stderr index 0b338c9ad45..09e3cb8c126 100644 --- a/src/test/ui/associated-types/associated-types-unsized.stderr +++ b/src/test/ui/associated-types/associated-types-unsized.stderr @@ -8,6 +8,7 @@ LL | let x = t.get(); //~ ERROR the size for values of type = note: to learn more, visit = help: consider adding a `where ::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 diff --git a/src/test/ui/error-codes/E0277.stderr b/src/test/ui/error-codes/E0277.stderr index e4c2f102267..ab9020222ea 100644 --- a/src/test/ui/error-codes/E0277.stderr +++ b/src/test/ui/error-codes/E0277.stderr @@ -8,6 +8,7 @@ LL | fn f(p: Path) { } = note: to learn more, visit = 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 diff --git a/src/test/ui/feature-gate-unsized_locals.rs b/src/test/ui/feature-gate-unsized_locals.rs new file mode 100644 index 00000000000..7f1f22fa38f --- /dev/null +++ b/src/test/ui/feature-gate-unsized_locals.rs @@ -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 or the MIT license +// , 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() { +} diff --git a/src/test/ui/feature-gate-unsized_locals.stderr b/src/test/ui/feature-gate-unsized_locals.stderr new file mode 100644 index 00000000000..a0440a373d2 --- /dev/null +++ b/src/test/ui/feature-gate-unsized_locals.stderr @@ -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 + = 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`. diff --git a/src/test/ui/issues/issue-15756.stderr b/src/test/ui/issues/issue-15756.stderr index e142a504eb6..877e0eaedf1 100644 --- a/src/test/ui/issues/issue-15756.stderr +++ b/src/test/ui/issues/issue-15756.stderr @@ -7,6 +7,7 @@ LL | &mut something = help: the trait `std::marker::Sized` is not implemented for `[T]` = note: to learn more, visit = 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 diff --git a/src/test/ui/issues/issue-27078.stderr b/src/test/ui/issues/issue-27078.stderr index 65b66997ee8..269a69dde33 100644 --- a/src/test/ui/issues/issue-27078.stderr +++ b/src/test/ui/issues/issue-27078.stderr @@ -8,6 +8,7 @@ LL | fn foo(self) -> &'static i32 { = note: to learn more, visit = 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 diff --git a/src/test/ui/issues/issue-38954.stderr b/src/test/ui/issues/issue-38954.stderr index 2f0e579378f..9bc937b97c9 100644 --- a/src/test/ui/issues/issue-38954.stderr +++ b/src/test/ui/issues/issue-38954.stderr @@ -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 + = 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 diff --git a/src/test/ui/issues/issue-41229-ref-str.stderr b/src/test/ui/issues/issue-41229-ref-str.stderr index effc5f6999e..e4a34fdaf48 100644 --- a/src/test/ui/issues/issue-41229-ref-str.stderr +++ b/src/test/ui/issues/issue-41229-ref-str.stderr @@ -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 + = 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 diff --git a/src/test/ui/issues/issue-42312.stderr b/src/test/ui/issues/issue-42312.stderr index 8c2a0987f2f..912d791b6bc 100644 --- a/src/test/ui/issues/issue-42312.stderr +++ b/src/test/ui/issues/issue-42312.stderr @@ -7,6 +7,8 @@ LL | fn baz(_: Self::Target) where Self: Deref {} = help: the trait `std::marker::Sized` is not implemented for `::Target` = note: to learn more, visit = help: consider adding a `where ::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 + = 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 diff --git a/src/test/ui/issues/issue-5883.stderr b/src/test/ui/issues/issue-5883.stderr index 6a321abeaed..63dabd86ca8 100644 --- a/src/test/ui/issues/issue-5883.stderr +++ b/src/test/ui/issues/issue-5883.stderr @@ -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 = 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 diff --git a/src/test/ui/resolve/issue-5035-2.stderr b/src/test/ui/resolve/issue-5035-2.stderr index 10d7a52297f..9e7fca0508e 100644 --- a/src/test/ui/resolve/issue-5035-2.stderr +++ b/src/test/ui/resolve/issue-5035-2.stderr @@ -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 = 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 diff --git a/src/test/ui/str/str-array-assignment.stderr b/src/test/ui/str/str-array-assignment.stderr index 59521bd2e2f..57eff3fb137 100644 --- a/src/test/ui/str/str-array-assignment.stderr +++ b/src/test/ui/str/str-array-assignment.stderr @@ -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 = 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 diff --git a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr index b0c1c284f5a..ecabf9af27b 100644 --- a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr +++ b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr @@ -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 = 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 diff --git a/src/test/ui/unsized6.stderr b/src/test/ui/unsized6.stderr index 5dc12a344ad..5a095332692 100644 --- a/src/test/ui/unsized6.stderr +++ b/src/test/ui/unsized6.stderr @@ -8,6 +8,7 @@ LL | let y: Y; = note: to learn more, visit = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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: X) {} = note: to learn more, visit = 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: X) {} = note: to learn more, visit = 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