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:
commit
b355906919
180
src/doc/unstable-book/src/language-features/unsized-locals.md
Normal file
180
src/doc/unstable-book/src/language-features/unsized-locals.md
Normal 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.
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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! (
|
||||
|
36
src/test/compile-fail/unsized-locals/unsized-exprs.rs
Normal file
36
src/test/compile-fail/unsized-locals/unsized-exprs.rs
Normal 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
|
||||
}
|
36
src/test/compile-fail/unsized-locals/unsized-exprs2.rs
Normal file
36
src/test/compile-fail/unsized-locals/unsized-exprs2.rs
Normal 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()));
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
fn gen_foo() -> Box<fmt::Display> {
|
||||
Box::new(Box::new("foo"))
|
||||
}
|
||||
|
||||
fn foo(x: fmt::Display) {
|
||||
assert_eq!(x.to_string(), "foo");
|
||||
}
|
||||
|
||||
fn foo_indirect(x: fmt::Display) {
|
||||
foo(x);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo(*gen_foo());
|
||||
foo_indirect(*gen_foo());
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
foo(x);
|
||||
}
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
let y: fmt::Display = *gen_foo();
|
||||
foo(x);
|
||||
foo(y);
|
||||
}
|
||||
|
||||
{
|
||||
let mut cnt: usize = 3;
|
||||
let x = loop {
|
||||
let x: fmt::Display = *gen_foo();
|
||||
if cnt == 0 {
|
||||
break x;
|
||||
} else {
|
||||
cnt -= 1;
|
||||
}
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
|
||||
{
|
||||
let x: fmt::Display = *gen_foo();
|
||||
let x = if true {
|
||||
x
|
||||
} else {
|
||||
*gen_foo()
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
}
|
17
src/test/run-pass/unsized-locals/reference-unsized-locals.rs
Normal file
17
src/test/run-pass/unsized-locals/reference-unsized-locals.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn main() {
|
||||
let foo: Box<[u8]> = Box::new(*b"foo");
|
||||
let foo: [u8] = *foo;
|
||||
assert_eq!(&foo, b"foo" as &[u8]);
|
||||
}
|
16
src/test/run-pass/unsized-locals/simple-unsized-locals.rs
Normal file
16
src/test/run-pass/unsized-locals/simple-unsized-locals.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn main() {
|
||||
let foo: Box<[u8]> = Box::new(*b"foo");
|
||||
let _foo: [u8] = *foo;
|
||||
}
|
45
src/test/run-pass/unsized-locals/unsized-exprs.rs
Normal file
45
src/test/run-pass/unsized-locals/unsized-exprs.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_tuple_coercion, unsized_locals)]
|
||||
|
||||
struct A<X: ?Sized>(X);
|
||||
|
||||
fn udrop<T: ?Sized>(_x: T) {}
|
||||
fn foo() -> Box<[u8]> {
|
||||
Box::new(*b"foo")
|
||||
}
|
||||
fn tfoo() -> Box<(i32, [u8])> {
|
||||
Box::new((42, *b"foo"))
|
||||
}
|
||||
fn afoo() -> Box<A<[u8]>> {
|
||||
Box::new(A(*b"foo"))
|
||||
}
|
||||
|
||||
impl std::ops::Add<i32> for A<[u8]> {
|
||||
type Output = ();
|
||||
fn add(self, _rhs: i32) -> Self::Output {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
udrop::<[u8]>(loop {
|
||||
break *foo();
|
||||
});
|
||||
udrop::<[u8]>(if true {
|
||||
*foo()
|
||||
} else {
|
||||
*foo()
|
||||
});
|
||||
udrop::<[u8]>({*foo()});
|
||||
#[allow(unused_parens)]
|
||||
udrop::<[u8]>((*foo()));
|
||||
udrop::<[u8]>((*tfoo()).1);
|
||||
*afoo() + 42;
|
||||
}
|
20
src/test/run-pass/unsized-locals/unsized-parameters.rs
Normal file
20
src/test/run-pass/unsized-locals/unsized-parameters.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
pub fn f0(_f: dyn FnOnce()) {}
|
||||
pub fn f1(_s: str) {}
|
||||
pub fn f2((_x, _y): (i32, [i32])) {}
|
||||
|
||||
fn main() {
|
||||
let foo = "foo".to_string().into_boxed_str();
|
||||
f1(*foo);
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
15
src/test/ui/feature-gate-unsized_locals.rs
Normal file
15
src/test/ui/feature-gate-unsized_locals.rs
Normal 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() {
|
||||
}
|
14
src/test/ui/feature-gate-unsized_locals.stderr
Normal file
14
src/test/ui/feature-gate-unsized_locals.stderr
Normal 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`.
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user