Skip to content

Commit a49b6f8

Browse files
committed
Rollup merge of rust-lang#23201 - pnkfelix:fsk-struct-ooe-23112, r=nikomatsakis
For FRU, eval field exprs (into scratch temps) before base expr Fix rust-lang#23112.
2 parents 621ccf5 + 3dbf969 commit a49b6f8

File tree

5 files changed

+137
-26
lines changed

5 files changed

+137
-26
lines changed

src/librustc_trans/trans/expr.rs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,8 +1500,45 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
15001500
// panic occur before the ADT as a whole is ready.
15011501
let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
15021502

1503-
// First we trans the base, if we have one, to the dest
1504-
if let Some(base) = optbase {
1503+
if ty::type_is_simd(bcx.tcx(), ty) {
1504+
// Issue 23112: The original logic appeared vulnerable to same
1505+
// order-of-eval bug. But, SIMD values are tuple-structs;
1506+
// i.e. functional record update (FRU) syntax is unavailable.
1507+
//
1508+
// To be safe, double-check that we did not get here via FRU.
1509+
assert!(optbase.is_none());
1510+
1511+
// This is the constructor of a SIMD type, such types are
1512+
// always primitive machine types and so do not have a
1513+
// destructor or require any clean-up.
1514+
let llty = type_of::type_of(bcx.ccx(), ty);
1515+
1516+
// keep a vector as a register, and running through the field
1517+
// `insertelement`ing them directly into that register
1518+
// (i.e. avoid GEPi and `store`s to an alloca) .
1519+
let mut vec_val = C_undef(llty);
1520+
1521+
for &(i, ref e) in fields {
1522+
let block_datum = trans(bcx, &**e);
1523+
bcx = block_datum.bcx;
1524+
let position = C_uint(bcx.ccx(), i);
1525+
let value = block_datum.datum.to_llscalarish(bcx);
1526+
vec_val = InsertElement(bcx, vec_val, value, position);
1527+
}
1528+
Store(bcx, vec_val, addr);
1529+
} else if let Some(base) = optbase {
1530+
// Issue 23112: If there is a base, then order-of-eval
1531+
// requires field expressions eval'ed before base expression.
1532+
1533+
// First, trans field expressions to temporary scratch values.
1534+
let scratch_vals: Vec<_> = fields.iter().map(|&(i, ref e)| {
1535+
let datum = unpack_datum!(bcx, trans(bcx, &**e));
1536+
(i, datum)
1537+
}).collect();
1538+
1539+
debug_location.apply(bcx.fcx);
1540+
1541+
// Second, trans the base to the dest.
15051542
assert_eq!(discr, 0);
15061543

15071544
match ty::expr_kind(bcx.tcx(), &*base.expr) {
@@ -1520,31 +1557,14 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
15201557
}
15211558
}
15221559
}
1523-
}
1524-
1525-
debug_location.apply(bcx.fcx);
1526-
1527-
if ty::type_is_simd(bcx.tcx(), ty) {
1528-
// This is the constructor of a SIMD type, such types are
1529-
// always primitive machine types and so do not have a
1530-
// destructor or require any clean-up.
1531-
let llty = type_of::type_of(bcx.ccx(), ty);
1532-
1533-
// keep a vector as a register, and running through the field
1534-
// `insertelement`ing them directly into that register
1535-
// (i.e. avoid GEPi and `store`s to an alloca) .
1536-
let mut vec_val = C_undef(llty);
15371560

1538-
for &(i, ref e) in fields {
1539-
let block_datum = trans(bcx, &**e);
1540-
bcx = block_datum.bcx;
1541-
let position = C_uint(bcx.ccx(), i);
1542-
let value = block_datum.datum.to_llscalarish(bcx);
1543-
vec_val = InsertElement(bcx, vec_val, value, position);
1561+
// Finally, move scratch field values into actual field locations
1562+
for (i, datum) in scratch_vals.into_iter() {
1563+
let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
1564+
bcx = datum.store_to(bcx, dest);
15441565
}
1545-
Store(bcx, vec_val, addr);
15461566
} else {
1547-
// Now, we just overwrite the fields we've explicitly specified
1567+
// No base means we can write all fields directly in place.
15481568
for &(i, ref e) in fields {
15491569
let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
15501570
let e_ty = expr_ty_adjusted(bcx, &**e);

src/test/run-pass/struct-order-of-eval-1.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ struct S { f0: String, f1: int }
1212

1313
pub fn main() {
1414
let s = "Hello, world!".to_string();
15-
let _s = S {
15+
let s = S {
1616
f0: s.to_string(),
1717
..S {
1818
f0: s,
1919
f1: 23
2020
}
2121
};
22+
assert_eq!(s.f0, "Hello, world!");
2223
}

src/test/run-pass/struct-order-of-eval-2.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ struct S {
1515

1616
pub fn main() {
1717
let s = "Hello, world!".to_string();
18-
let _s = S {
18+
let s = S {
1919
f1: s.to_string(),
2020
f0: s
2121
};
22+
assert_eq!(s.f0, "Hello, world!");
2223
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Checks that functional-record-update order-of-eval is as expected
12+
// even when no Drop-implementations are involved.
13+
14+
use std::sync::atomic::{Ordering, AtomicUsize, ATOMIC_USIZE_INIT};
15+
16+
struct W { wrapped: u32 }
17+
struct S { f0: W, _f1: i32 }
18+
19+
pub fn main() {
20+
const VAL: u32 = 0x89AB_CDEF;
21+
let w = W { wrapped: VAL };
22+
let s = S {
23+
f0: { event(0x01); W { wrapped: w.wrapped + 1 } },
24+
..S {
25+
f0: { event(0x02); w},
26+
_f1: 23
27+
}
28+
};
29+
assert_eq!(s.f0.wrapped, VAL + 1);
30+
let actual = event_log();
31+
let expect = 0x01_02;
32+
assert!(expect == actual,
33+
"expect: 0x{:x} actual: 0x{:x}", expect, actual);
34+
}
35+
36+
static LOG: AtomicUsize = ATOMIC_USIZE_INIT;
37+
38+
fn event_log() -> usize {
39+
LOG.load(Ordering::SeqCst)
40+
}
41+
42+
fn event(tag: u8) {
43+
let old_log = LOG.load(Ordering::SeqCst);
44+
let new_log = (old_log << 8) + tag as usize;
45+
LOG.store(new_log, Ordering::SeqCst);
46+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Checks that struct-literal expression order-of-eval is as expected
12+
// even when no Drop-implementations are involved.
13+
14+
use std::sync::atomic::{Ordering, AtomicUsize, ATOMIC_USIZE_INIT};
15+
16+
struct W { wrapped: u32 }
17+
struct S { f0: W, _f1: i32 }
18+
19+
pub fn main() {
20+
const VAL: u32 = 0x89AB_CDEF;
21+
let w = W { wrapped: VAL };
22+
let s = S {
23+
_f1: { event(0x01); 23 },
24+
f0: { event(0x02); w },
25+
};
26+
assert_eq!(s.f0.wrapped, VAL);
27+
let actual = event_log();
28+
let expect = 0x01_02;
29+
assert!(expect == actual,
30+
"expect: 0x{:x} actual: 0x{:x}", expect, actual);
31+
}
32+
33+
static LOG: AtomicUsize = ATOMIC_USIZE_INIT;
34+
35+
fn event_log() -> usize {
36+
LOG.load(Ordering::SeqCst)
37+
}
38+
39+
fn event(tag: u8) {
40+
let old_log = LOG.load(Ordering::SeqCst);
41+
let new_log = (old_log << 8) + tag as usize;
42+
LOG.store(new_log, Ordering::SeqCst);
43+
}

0 commit comments

Comments
 (0)