Skip to content

Commit 45d050c

Browse files
committed
Auto merge of #71170 - spastorino:dyn-fnonce-alignment, r=nikomatsakis
Make Box<dyn FnOnce> respect self alignment Closes #68304 r? @eddyb @nikomatsakis
2 parents 25f070d + 4e53a9a commit 45d050c

File tree

6 files changed

+166
-16
lines changed

6 files changed

+166
-16
lines changed

src/librustc_mir_build/build/expr/as_operand.rs

+129-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1010
/// Returns an operand suitable for use until the end of the current
1111
/// scope expression.
1212
///
13-
/// The operand returned from this function will *not be valid* after
14-
/// an ExprKind::Scope is passed, so please do *not* return it from
15-
/// functions to avoid bad miscompiles.
13+
/// The operand returned from this function will *not be valid*
14+
/// after the current enclosing `ExprKind::Scope` has ended, so
15+
/// please do *not* return it from functions to avoid bad
16+
/// miscompiles.
1617
crate fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
1718
where
1819
M: Mirror<'tcx, Output = Expr<'tcx>>,
@@ -21,6 +22,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2122
self.as_operand(block, local_scope, expr)
2223
}
2324

25+
/// Returns an operand suitable for use until the end of the current scope expression and
26+
/// suitable also to be passed as function arguments.
27+
///
28+
/// The operand returned from this function will *not be valid* after an ExprKind::Scope is
29+
/// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
30+
/// operand suitable for use as a call argument. This is almost always equivalent to
31+
/// `as_operand`, except for the particular case of passing values of (potentially) unsized
32+
/// types "by value" (see details below).
33+
///
34+
/// The operand returned from this function will *not be valid*
35+
/// after the current enclosing `ExprKind::Scope` has ended, so
36+
/// please do *not* return it from functions to avoid bad
37+
/// miscompiles.
38+
///
39+
/// # Parameters of unsized types
40+
///
41+
/// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
42+
/// local variable of unsized type. For example, consider this program:
43+
///
44+
/// ```rust
45+
/// fn foo(p: dyn Debug) { ... }
46+
///
47+
/// fn bar(box_p: Box<dyn Debug>) { foo(*p); }
48+
/// ```
49+
///
50+
/// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
51+
///
52+
/// ```rust
53+
/// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
54+
/// foo(tmp0)
55+
/// ```
56+
///
57+
/// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
58+
/// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
59+
/// that we create *stores the entire box*, and the parameter to the call itself will be
60+
/// `*tmp0`:
61+
///
62+
/// ```rust
63+
/// let tmp0 = box_p; call foo(*tmp0)
64+
/// ```
65+
///
66+
/// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
67+
/// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
68+
/// calls are compiled means that this parameter will be passed "by reference", meaning that we
69+
/// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
70+
/// value to the stack.
71+
///
72+
/// See #68034 for more details.
73+
crate fn as_local_call_operand<M>(
74+
&mut self,
75+
block: BasicBlock,
76+
expr: M,
77+
) -> BlockAnd<Operand<'tcx>>
78+
where
79+
M: Mirror<'tcx, Output = Expr<'tcx>>,
80+
{
81+
let local_scope = self.local_scope();
82+
self.as_call_operand(block, local_scope, expr)
83+
}
84+
2485
/// Compile `expr` into a value that can be used as an operand.
2586
/// If `expr` is a place like `x`, this will introduce a
2687
/// temporary `tmp = x`, so that we capture the value of `x` at
@@ -40,6 +101,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
40101
self.expr_as_operand(block, scope, expr)
41102
}
42103

104+
/// Like `as_local_call_operand`, except that the argument will
105+
/// not be valid once `scope` ends.
106+
fn as_call_operand<M>(
107+
&mut self,
108+
block: BasicBlock,
109+
scope: Option<region::Scope>,
110+
expr: M,
111+
) -> BlockAnd<Operand<'tcx>>
112+
where
113+
M: Mirror<'tcx, Output = Expr<'tcx>>,
114+
{
115+
let expr = self.hir.mirror(expr);
116+
self.expr_as_call_operand(block, scope, expr)
117+
}
118+
43119
fn expr_as_operand(
44120
&mut self,
45121
mut block: BasicBlock,
@@ -69,4 +145,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
69145
}
70146
}
71147
}
148+
149+
fn expr_as_call_operand(
150+
&mut self,
151+
mut block: BasicBlock,
152+
scope: Option<region::Scope>,
153+
expr: Expr<'tcx>,
154+
) -> BlockAnd<Operand<'tcx>> {
155+
debug!("expr_as_call_operand(block={:?}, expr={:?})", block, expr);
156+
let this = self;
157+
158+
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
159+
let source_info = this.source_info(expr.span);
160+
let region_scope = (region_scope, source_info);
161+
return this.in_scope(region_scope, lint_level, |this| {
162+
this.as_call_operand(block, scope, value)
163+
});
164+
}
165+
166+
let tcx = this.hir.tcx();
167+
168+
if tcx.features().unsized_locals {
169+
let ty = expr.ty;
170+
let span = expr.span;
171+
let param_env = this.hir.param_env;
172+
173+
if !ty.is_sized(tcx.at(span), param_env) {
174+
// !sized means !copy, so this is an unsized move
175+
assert!(!ty.is_copy_modulo_regions(tcx, param_env, span));
176+
177+
// As described above, detect the case where we are passing a value of unsized
178+
// type, and that value is coming from the deref of a box.
179+
if let ExprKind::Deref { ref arg } = expr.kind {
180+
let arg = this.hir.mirror(arg.clone());
181+
182+
// Generate let tmp0 = arg0
183+
let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
184+
185+
// Return the operand *tmp0 to be used as the call argument
186+
let place = Place {
187+
local: operand,
188+
projection: tcx.intern_place_elems(&[PlaceElem::Deref]),
189+
};
190+
191+
return block.and(Operand::Move(place));
192+
}
193+
}
194+
}
195+
196+
this.expr_as_operand(block, scope, expr)
197+
}
72198
}

src/librustc_mir_build/build/expr/into.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
202202
} else {
203203
let args: Vec<_> = args
204204
.into_iter()
205-
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
205+
.map(|arg| unpack!(block = this.as_local_call_operand(block, arg)))
206206
.collect();
207207

208208
let success = this.cfg.start_new_block();

src/test/ui/fn/dyn-fn-alignment.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-pass
2+
3+
#![feature(unsized_locals)]
4+
#![allow(dead_code)]
5+
#[repr(align(256))]
6+
struct A {
7+
v: u8,
8+
}
9+
10+
impl A {
11+
fn f(&self) -> *const A {
12+
self
13+
}
14+
}
15+
16+
fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> {
17+
let a = A { v };
18+
Box::new(move || a.f())
19+
}
20+
21+
fn main() {
22+
let addr = f2(0)();
23+
assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr);
24+
}

src/test/ui/unsized-locals/borrow-after-move.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ LL | println!("{}", &y);
4545
error[E0382]: borrow of moved value: `x`
4646
--> $DIR/borrow-after-move.rs:39:24
4747
|
48+
LL | let x = "hello".to_owned().into_boxed_str();
49+
| - move occurs because `x` has type `std::boxed::Box<str>`, which does not implement the `Copy` trait
4850
LL | x.foo();
4951
| - value moved here
5052
LL | println!("{}", &x);
51-
| ^^ value borrowed here after partial move
52-
|
53-
= note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
53+
| ^^ value borrowed here after move
5454

5555
error: aborting due to 5 previous errors
5656

src/test/ui/unsized-locals/double-move.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,25 @@ LL | y.foo();
3838
LL | y.foo();
3939
| ^ value used here after move
4040

41-
error[E0382]: use of moved value: `*x`
41+
error[E0382]: use of moved value: `x`
4242
--> $DIR/double-move.rs:45:9
4343
|
4444
LL | let _y = *x;
4545
| -- value moved here
4646
LL | x.foo();
47-
| ^ value used here after move
47+
| ^ value used here after partial move
4848
|
4949
= note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
5050

5151
error[E0382]: use of moved value: `*x`
5252
--> $DIR/double-move.rs:51:18
5353
|
54+
LL | let x = "hello".to_owned().into_boxed_str();
55+
| - move occurs because `x` has type `std::boxed::Box<str>`, which does not implement the `Copy` trait
5456
LL | x.foo();
5557
| - value moved here
5658
LL | let _y = *x;
5759
| ^^ value used here after move
58-
|
59-
= note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
6060

6161
error: aborting due to 6 previous errors
6262

src/test/ui/unsized-locals/unsized-exprs2.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0508]: cannot move out of type `[u8]`, a non-copy slice
2-
--> $DIR/unsized-exprs2.rs:22:19
2+
--> $DIR/unsized-exprs2.rs:22:5
33
|
44
LL | udrop::<[u8]>(foo()[..]);
5-
| ^^^^^^^^^
6-
| |
7-
| cannot move out of here
8-
| move occurs because value has type `[u8]`, which does not implement the `Copy` trait
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| cannot move out of here
8+
| move occurs because value has type `[u8]`, which does not implement the `Copy` trait
99

1010
error: aborting due to previous error
1111

0 commit comments

Comments
 (0)