Skip to content

Commit 3dbf969

Browse files
committed
For FRU, evaluate field expressions (into scratch temps) before base expression.
Fix #23112.
1 parent 1fe8f22 commit 3dbf969

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
@@ -1494,8 +1494,45 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
14941494
// panic occur before the ADT as a whole is ready.
14951495
let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
14961496

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

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

1532-
for &(i, ref e) in fields {
1533-
let block_datum = trans(bcx, &**e);
1534-
bcx = block_datum.bcx;
1535-
let position = C_uint(bcx.ccx(), i);
1536-
let value = block_datum.datum.to_llscalarish(bcx);
1537-
vec_val = InsertElement(bcx, vec_val, value, position);
1555+
// Finally, move scratch field values into actual field locations
1556+
for (i, datum) in scratch_vals.into_iter() {
1557+
let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
1558+
bcx = datum.store_to(bcx, dest);
15381559
}
1539-
Store(bcx, vec_val, addr);
15401560
} else {
1541-
// Now, we just overwrite the fields we've explicitly specified
1561+
// No base means we can write all fields directly in place.
15421562
for &(i, ref e) in fields {
15431563
let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
15441564
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)