Skip to content

Commit b355906

Browse files
committed
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/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>
2 parents 8928de7 + c488d59 commit b355906

40 files changed

+776
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# `unsized_locals`
2+
3+
The tracking issue for this feature is: [#48055]
4+
5+
[#48055]: https://github.com/rust-lang/rust/issues/48055
6+
7+
------------------------
8+
9+
This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
10+
11+
[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-coercions.md
12+
13+
```rust
14+
#![feature(unsized_locals)]
15+
16+
use std::any::Any;
17+
18+
fn main() {
19+
let x: Box<dyn Any> = Box::new(42);
20+
let x: dyn Any = *x;
21+
// ^ unsized local variable
22+
// ^^ unsized temporary
23+
foo(x);
24+
}
25+
26+
fn foo(_: dyn Any) {}
27+
// ^^^^^^ unsized argument
28+
```
29+
30+
The RFC still forbids the following unsized expressions:
31+
32+
```rust,ignore
33+
#![feature(unsized_locals)]
34+
35+
use std::any::Any;
36+
37+
struct MyStruct<T: ?Sized> {
38+
content: T,
39+
}
40+
41+
struct MyTupleStruct<T: ?Sized>(T);
42+
43+
fn answer() -> Box<dyn Any> {
44+
Box::new(42)
45+
}
46+
47+
fn main() {
48+
// You CANNOT have unsized statics.
49+
static X: dyn Any = *answer(); // ERROR
50+
const Y: dyn Any = *answer(); // ERROR
51+
52+
// You CANNOT have struct initialized unsized.
53+
MyStruct { content: *answer() }; // ERROR
54+
MyTupleStruct(*answer()); // ERROR
55+
(42, *answer()); // ERROR
56+
57+
// You CANNOT have unsized return types.
58+
fn my_function() -> dyn Any { *answer() } // ERROR
59+
60+
// You CAN have unsized local variables...
61+
let mut x: dyn Any = *answer(); // OK
62+
// ...but you CANNOT reassign to them.
63+
x = *answer(); // ERROR
64+
65+
// You CANNOT even initialize them separately.
66+
let y: dyn Any; // OK
67+
y = *answer(); // ERROR
68+
69+
// Not mentioned in the RFC, but by-move captured variables are also Sized.
70+
let x: dyn Any = *answer();
71+
(move || { // ERROR
72+
let y = x;
73+
})();
74+
75+
// You CAN create a closure with unsized arguments,
76+
// but you CANNOT call it.
77+
// This is an implementation detail and may be changed in the future.
78+
let f = |x: dyn Any| {};
79+
f(*answer()); // ERROR
80+
}
81+
```
82+
83+
However, the current implementation allows `MyTupleStruct(..)` to be unsized. This will be fixed in the future.
84+
85+
## By-value trait objects
86+
87+
With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
88+
89+
```rust
90+
#![feature(unsized_locals)]
91+
92+
trait Foo {
93+
fn foo(self) {}
94+
}
95+
96+
impl<T: ?Sized> Foo for T {}
97+
98+
fn main() {
99+
let slice: Box<[i32]> = Box::new([1, 2, 3]);
100+
<[i32] as Foo>::foo(*slice);
101+
}
102+
```
103+
104+
And `Foo` will also be object-safe. However, this object-safety is not yet implemented.
105+
106+
```rust,ignore
107+
#![feature(unsized_locals)]
108+
109+
trait Foo {
110+
fn foo(self) {}
111+
}
112+
113+
impl<T: ?Sized> Foo for T {}
114+
115+
fn main () {
116+
let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
117+
// doesn't compile yet
118+
<dyn Foo as Foo>::foo(*slice);
119+
}
120+
```
121+
122+
Unfortunately, this is not implemented yet.
123+
124+
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.
125+
126+
[#28796]: https://github.com/rust-lang/rust/issues/28796
127+
128+
## Variable length arrays
129+
130+
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]`.
131+
132+
```rust,ignore
133+
#![feature(unsized_locals)]
134+
135+
fn mergesort<T: Ord>(a: &mut [T]) {
136+
let mut tmp = [T; dyn a.len()];
137+
// ...
138+
}
139+
140+
fn main() {
141+
let mut a = [3, 1, 5, 6];
142+
mergesort(&mut a);
143+
assert_eq!(a, [1, 3, 5, 6]);
144+
}
145+
```
146+
147+
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]`.
148+
149+
## Advisory on stack usage
150+
151+
It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
152+
153+
- When you need a by-value trait objects.
154+
- When you really need a fast allocation of small temporary arrays.
155+
156+
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
157+
158+
```rust
159+
#![feature(unsized_locals)]
160+
161+
fn main() {
162+
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
163+
let _x = {{{{{{{{{{*x}}}}}}}}}};
164+
}
165+
```
166+
167+
and the code
168+
169+
```rust
170+
#![feature(unsized_locals)]
171+
172+
fn main() {
173+
for _ in 0..10 {
174+
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
175+
let _x = *x;
176+
}
177+
}
178+
```
179+
180+
will unnecessarily extend the stack frame.

src/librustc/traits/error_reporting.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14541454
}
14551455
ObligationCauseCode::VariableType(_) => {
14561456
err.note("all local variables must have a statically known size");
1457+
if !self.tcx.features().unsized_locals {
1458+
err.help("unsized locals are gated as an unstable feature");
1459+
}
1460+
}
1461+
ObligationCauseCode::SizedArgumentType => {
1462+
err.note("all function arguments must have a statically known size");
1463+
if !self.tcx.features().unsized_locals {
1464+
err.help("unsized locals are gated as an unstable feature");
1465+
}
14571466
}
14581467
ObligationCauseCode::SizedReturnType => {
14591468
err.note("the return type of a function must have a \

src/librustc/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ pub enum ObligationCauseCode<'tcx> {
185185
StructInitializerSized,
186186
/// Type of each variable must be Sized
187187
VariableType(ast::NodeId),
188+
/// Argument type must be Sized
189+
SizedArgumentType,
188190
/// Return type must be Sized
189191
SizedReturnType,
190192
/// Yield type must be Sized

src/librustc/traits/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
203203
super::StructInitializerSized => Some(super::StructInitializerSized),
204204
super::VariableType(id) => Some(super::VariableType(id)),
205205
super::ReturnType(id) => Some(super::ReturnType(id)),
206+
super::SizedArgumentType => Some(super::SizedArgumentType),
206207
super::SizedReturnType => Some(super::SizedReturnType),
207208
super::SizedYieldType => Some(super::SizedYieldType),
208209
super::RepeatVec => Some(super::RepeatVec),

src/librustc_codegen_llvm/abi.rs

+36-12
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,10 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
187187
return;
188188
}
189189
let cx = bx.cx;
190-
if self.is_indirect() {
191-
OperandValue::Ref(val, self.layout.align).store(bx, dst)
190+
if self.is_sized_indirect() {
191+
OperandValue::Ref(val, None, self.layout.align).store(bx, dst)
192+
} else if self.is_unsized_indirect() {
193+
bug!("unsized ArgType must be handled through store_fn_arg");
192194
} else if let PassMode::Cast(cast) = self.mode {
193195
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
194196
// 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>> {
246248
PassMode::Pair(..) => {
247249
OperandValue::Pair(next(), next()).store(bx, dst);
248250
}
249-
PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => {
251+
PassMode::Indirect(_, Some(_)) => {
252+
OperandValue::Ref(next(), Some(next()), self.layout.align).store(bx, dst);
253+
}
254+
PassMode::Direct(_) | PassMode::Indirect(_, None) | PassMode::Cast(_) => {
250255
self.store(bx, next(), dst);
251256
}
252257
}
@@ -302,6 +307,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
302307
// Don't pass the vtable, it's not an argument of the virtual fn.
303308
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
304309
if arg_idx == Some(0) {
310+
if layout.is_unsized() {
311+
unimplemented!("by-value trait object is not \
312+
yet implemented in #![feature(unsized_locals)]");
313+
}
305314
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
306315
// `Box<dyn Trait>` has a few newtype wrappers around the raw
307316
// 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>> {
538547
}
539548

540549
let size = arg.layout.size;
541-
if size > layout::Pointer.size(cx) {
550+
if arg.layout.is_unsized() || size > layout::Pointer.size(cx) {
542551
arg.make_indirect();
543552
} else {
544553
// We want to pass small aggregates as immediates, but using
@@ -554,7 +563,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
554563
for arg in &mut self.args {
555564
fixup(arg);
556565
}
557-
if let PassMode::Indirect(ref mut attrs) = self.ret.mode {
566+
if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode {
558567
attrs.set(ArgAttribute::StructRet);
559568
}
560569
return;
@@ -571,7 +580,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
571580
if let PassMode::Pair(_, _) = arg.mode { 2 } else { 1 }
572581
).sum();
573582
let mut llargument_tys = Vec::with_capacity(
574-
if let PassMode::Indirect(_) = self.ret.mode { 1 } else { 0 } + args_capacity
583+
if let PassMode::Indirect(..) = self.ret.mode { 1 } else { 0 } + args_capacity
575584
);
576585

577586
let llreturn_ty = match self.ret.mode {
@@ -580,7 +589,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
580589
self.ret.layout.immediate_llvm_type(cx)
581590
}
582591
PassMode::Cast(cast) => cast.llvm_type(cx),
583-
PassMode::Indirect(_) => {
592+
PassMode::Indirect(..) => {
584593
llargument_tys.push(self.ret.memory_ty(cx).ptr_to());
585594
Type::void(cx)
586595
}
@@ -600,8 +609,15 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
600609
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
601610
continue;
602611
}
612+
PassMode::Indirect(_, Some(_)) => {
613+
let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
614+
let ptr_layout = cx.layout_of(ptr_ty);
615+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
616+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
617+
continue;
618+
}
603619
PassMode::Cast(cast) => cast.llvm_type(cx),
604-
PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(),
620+
PassMode::Indirect(_, None) => arg.memory_ty(cx).ptr_to(),
605621
};
606622
llargument_tys.push(llarg_ty);
607623
}
@@ -640,7 +656,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
640656
PassMode::Direct(ref attrs) => {
641657
attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
642658
}
643-
PassMode::Indirect(ref attrs) => apply(attrs),
659+
PassMode::Indirect(ref attrs, _) => apply(attrs),
644660
_ => {}
645661
}
646662
for arg in &self.args {
@@ -650,7 +666,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
650666
match arg.mode {
651667
PassMode::Ignore => {}
652668
PassMode::Direct(ref attrs) |
653-
PassMode::Indirect(ref attrs) => apply(attrs),
669+
PassMode::Indirect(ref attrs, None) => apply(attrs),
670+
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
671+
apply(attrs);
672+
apply(extra_attrs);
673+
}
654674
PassMode::Pair(ref a, ref b) => {
655675
apply(a);
656676
apply(b);
@@ -670,7 +690,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
670690
PassMode::Direct(ref attrs) => {
671691
attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite);
672692
}
673-
PassMode::Indirect(ref attrs) => apply(attrs),
693+
PassMode::Indirect(ref attrs, _) => apply(attrs),
674694
_ => {}
675695
}
676696
if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi {
@@ -694,7 +714,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
694714
match arg.mode {
695715
PassMode::Ignore => {}
696716
PassMode::Direct(ref attrs) |
697-
PassMode::Indirect(ref attrs) => apply(attrs),
717+
PassMode::Indirect(ref attrs, None) => apply(attrs),
718+
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
719+
apply(attrs);
720+
apply(extra_attrs);
721+
}
698722
PassMode::Pair(ref a, ref b) => {
699723
apply(a);
700724
apply(b);

src/librustc_codegen_llvm/builder.rs

+19
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,25 @@ impl Builder<'a, 'll, 'tcx> {
445445
}
446446
}
447447

448+
pub fn array_alloca(&self,
449+
ty: &'ll Type,
450+
len: &'ll Value,
451+
name: &str,
452+
align: Align) -> &'ll Value {
453+
self.count_insn("alloca");
454+
unsafe {
455+
let alloca = if name.is_empty() {
456+
llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len, noname())
457+
} else {
458+
let name = SmallCStr::new(name);
459+
llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len,
460+
name.as_ptr())
461+
};
462+
llvm::LLVMSetAlignment(alloca, align.abi() as c_uint);
463+
alloca
464+
}
465+
}
466+
448467
pub fn load(&self, ptr: &'ll Value, align: Align) -> &'ll Value {
449468
self.count_insn("load");
450469
unsafe {

src/librustc_codegen_llvm/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ pub fn codegen_intrinsic_call(
605605
// etc.
606606
assert!(!bx.cx.type_needs_drop(arg.layout.ty));
607607
let (ptr, align) = match arg.val {
608-
OperandValue::Ref(ptr, align) => (ptr, align),
608+
OperandValue::Ref(ptr, None, align) => (ptr, align),
609609
_ => bug!()
610610
};
611611
let arg = PlaceRef::new_sized(ptr, arg.layout, align);

src/librustc_codegen_llvm/llvm/ffi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,11 @@ extern "C" {
875875

876876
// Memory
877877
pub fn LLVMBuildAlloca(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
878+
pub fn LLVMBuildArrayAlloca(B: &Builder<'a>,
879+
Ty: &'a Type,
880+
Val: &'a Value,
881+
Name: *const c_char)
882+
-> &'a Value;
878883
pub fn LLVMBuildLoad(B: &Builder<'a>, PointerVal: &'a Value, Name: *const c_char) -> &'a Value;
879884

880885
pub fn LLVMBuildStore(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value;

0 commit comments

Comments
 (0)