regression test.
This commit is contained in:
parent
8f987956b4
commit
d5acb55c59
1 changed files with 265 additions and 0 deletions
265
src/test/run-pass/issue-23611-enum-swap-in-drop.rs
Normal file
265
src/test/run-pass/issue-23611-enum-swap-in-drop.rs
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// Issue 23611: this test is ensuring that, for an instance `X` of the
|
||||
// enum `E`, if you swap in a different variant during the execution
|
||||
// of the `<E as Drop>::drop`, then the appropriate substructure will
|
||||
// be torn down after the `<E as Drop>::drop` method returns.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
|
||||
use self::d::D;
|
||||
|
||||
pub fn main() {
|
||||
let log = RefCell::new(vec![]);
|
||||
d::println(&format!("created empty log"));
|
||||
test(&log);
|
||||
|
||||
// println!("log: {:?}", &log.borrow()[..]);
|
||||
assert_eq!(
|
||||
&log.borrow()[..],
|
||||
[
|
||||
// created empty log
|
||||
// +-- Make D(test_1, 10000000)
|
||||
// | +-- Make D(g_b_5, 50000001)
|
||||
// | | in g_B(b2b0) from E::drop, b=b4b0
|
||||
// | +-- Drop D(g_b_5, 50000001)
|
||||
50000001,
|
||||
// |
|
||||
// | +-- Make D(drop_6, 60000002)
|
||||
// | | +-- Make D(g_b_5, 50000003)
|
||||
// | | | in g_B(b2b0) from E::drop, b=b4b1
|
||||
// | | +-- Drop D(g_b_5, 50000003)
|
||||
50000003,
|
||||
// | |
|
||||
// | | +-- Make D(GaspB::drop_3, 30000004)
|
||||
// | | | +-- Make D(g_b_5, 50000005)
|
||||
// | | | | in g_B(b4b2) from GaspB::drop
|
||||
// | | | +-- Drop D(g_b_5, 50000005)
|
||||
50000005,
|
||||
// | | |
|
||||
// | | +-- Drop D(GaspB::drop_3, 30000004)
|
||||
30000004,
|
||||
// | |
|
||||
// +-- Drop D(test_1, 10000000)
|
||||
10000000,
|
||||
// |
|
||||
// +-- Make D(GaspA::drop_2, 20000006)
|
||||
// | | +-- Make D(f_a_4, 40000007)
|
||||
// | | | in f_A(a3a0) from GaspA::drop
|
||||
// | | +-- Drop D(f_a_4, 40000007)
|
||||
40000007,
|
||||
// | |
|
||||
// +-- Drop D(GaspA::drop_2, 20000006)
|
||||
20000006,
|
||||
// |
|
||||
// +-- Drop D(drop_6, 60000002)
|
||||
60000002
|
||||
//
|
||||
]);
|
||||
|
||||
// For reference purposes, the old (incorrect) behavior would produce the following
|
||||
// output, which you can compare to the above:
|
||||
//
|
||||
// created empty log
|
||||
// +-- Make D(test_1, 10000000)
|
||||
// | +-- Make D(g_b_5, 50000001)
|
||||
// | | in g_B(b2b0) from E::drop, b=b4b0
|
||||
// | +-- Drop D(g_b_5, 50000001)
|
||||
// |
|
||||
// | +-- Make D(drop_6, 60000002)
|
||||
// | | +-- Make D(g_b_5, 50000003)
|
||||
// | | | in g_B(b2b0) from E::drop, b=b4b1
|
||||
// | | +-- Drop D(g_b_5, 50000003)
|
||||
// | |
|
||||
// | | +-- Make D(GaspB::drop_3, 30000004)
|
||||
// | | | +-- Make D(g_b_5, 50000005)
|
||||
// | | | | in g_B(b4b2) from GaspB::drop
|
||||
// | | | +-- Drop D(g_b_5, 50000005)
|
||||
// | | |
|
||||
// | | +-- Drop D(GaspB::drop_3, 30000004)
|
||||
// | |
|
||||
// +-- Drop D(test_1, 10000000)
|
||||
// |
|
||||
// +-- Make D(GaspB::drop_3, 30000006)
|
||||
// | | +-- Make D(f_a_4, 40000007)
|
||||
// | | | in f_A(a3a0) from GaspB::drop
|
||||
// | | +-- Drop D(f_a_4, 40000007)
|
||||
// | |
|
||||
// +-- Drop D(GaspB::drop_3, 30000006)
|
||||
// |
|
||||
// +-- Drop D(drop_6, 60000002)
|
||||
|
||||
// Note that this calls f_A from GaspB::drop (and thus creates a D
|
||||
// with a uid incorporating the origin of GaspB's drop that
|
||||
// surrounds the f_A invocation), but the code as written only
|
||||
// ever hands f_A off to instances of GaspA, and thus one should
|
||||
// be able to prove the invariant that f_A is *only* invoked from
|
||||
// from an instance of GaspA (either via the GaspA drop
|
||||
// implementation or the E drop implementaton). Yet the old (bad)
|
||||
// behavior allowed a call to f_A to leak in while we are tearing
|
||||
// down a value of type GaspB.
|
||||
}
|
||||
|
||||
fn test<'a>(log: d::Log<'a>) {
|
||||
let _e = E::B(GaspB(g_b, 0xB4B0, log, D::new("test", 1, log)), true);
|
||||
}
|
||||
|
||||
struct GaspA<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
|
||||
struct GaspB<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
|
||||
|
||||
impl<'a> Drop for GaspA<'a> {
|
||||
fn drop(&mut self) {
|
||||
let _d = d::D::new("GaspA::drop", 2, self.2);
|
||||
(self.0)(self.1, "GaspA::drop", self.2);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for GaspB<'a> {
|
||||
fn drop(&mut self) {
|
||||
let _d = d::D::new("GaspB::drop", 3, self.2);
|
||||
(self.0)(self.1, "GaspB::drop", self.2);
|
||||
}
|
||||
}
|
||||
|
||||
enum E<'a> {
|
||||
A(GaspA<'a>, bool), B(GaspB<'a>, bool),
|
||||
}
|
||||
|
||||
fn f_a(x: u32, ctxt: &str, log: d::Log) {
|
||||
let _d = d::D::new("f_a", 4, log);
|
||||
d::println(&format!("in f_A({:x}) from {}", x, ctxt));
|
||||
}
|
||||
fn g_b(y: u32, ctxt: &str, log: d::Log) {
|
||||
let _d = d::D::new("g_b", 5, log);
|
||||
d::println(&format!("in g_B({:x}) from {}", y, ctxt));
|
||||
}
|
||||
|
||||
impl<'a> Drop for E<'a> {
|
||||
fn drop(&mut self) {
|
||||
let (do_drop, log) = match *self {
|
||||
E::A(GaspA(ref f, ref mut val_a, log, ref _d_a), ref mut do_drop) => {
|
||||
f(0xA1A0, &format!("E::drop, a={:x}", val_a), log);
|
||||
*val_a += 1;
|
||||
// swap in do_drop := false to avoid infinite dtor regress
|
||||
(mem::replace(do_drop, false), log)
|
||||
}
|
||||
E::B(GaspB(ref g, ref mut val_b, log, ref _d_b), ref mut do_drop) => {
|
||||
g(0xB2B0, &format!("E::drop, b={:x}", val_b), log);
|
||||
*val_b += 1;
|
||||
// swap in do_drop := false to avoid infinite dtor regress
|
||||
(mem::replace(do_drop, false), log)
|
||||
}
|
||||
};
|
||||
|
||||
if do_drop {
|
||||
mem::replace(self, E::A(GaspA(f_a, 0xA3A0, log, D::new("drop", 6, log)), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This module provides simultaneous printouts of the dynamic extents
|
||||
// of all of the D values, in addition to logging the order that each
|
||||
// is dropped.
|
||||
//
|
||||
// This code is similar to a support code module embedded within
|
||||
// test/run-pass/issue-123338-ensure-param-drop-order.rs; however,
|
||||
// that (slightly simpler) code only identifies objects in the log via
|
||||
// (creation) time-stamps; this incorporates both timestamping and the
|
||||
// point of origin within the source code into the unique ID (uid).
|
||||
|
||||
const PREF_INDENT: u32 = 20;
|
||||
|
||||
pub mod d {
|
||||
#![allow(unused_parens)]
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::cell::RefCell;
|
||||
|
||||
static mut counter: u16 = 0;
|
||||
static mut trails: u64 = 0;
|
||||
|
||||
pub type Log<'a> = &'a RefCell<Vec<u32>>;
|
||||
|
||||
pub fn current_width() -> u32 {
|
||||
unsafe { max_width() - trails.leading_zeros() }
|
||||
}
|
||||
|
||||
pub fn max_width() -> u32 {
|
||||
unsafe {
|
||||
(mem::size_of_val(&trails)*8) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indent_println(my_trails: u32, s: &str) {
|
||||
let mut indent: String = String::new();
|
||||
for i in 0..my_trails {
|
||||
unsafe {
|
||||
if trails & (1 << i) != 0 {
|
||||
indent = indent + "| ";
|
||||
} else {
|
||||
indent = indent + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{}{}", indent, s);
|
||||
}
|
||||
|
||||
pub fn println(s: &str) {
|
||||
indent_println(super::PREF_INDENT, s);
|
||||
}
|
||||
|
||||
fn first_avail() -> u32 {
|
||||
unsafe {
|
||||
for i in 0..64 {
|
||||
if trails & (1 << i) == 0 {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("exhausted trails");
|
||||
}
|
||||
|
||||
pub struct D<'a> {
|
||||
name: &'static str, i: u8, uid: u32, trail: u32, log: Log<'a>
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for D<'a> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(w, "D({}_{}, {})", self.name, self.i, self.uid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> D<'a> {
|
||||
pub fn new(name: &'static str, i: u8, log: Log<'a>) -> D<'a> {
|
||||
unsafe {
|
||||
let trail = first_avail();
|
||||
let ctr = ((i as u32) * 10_000_000) + (counter as u32);
|
||||
counter += 1;
|
||||
trails |= (1 << trail);
|
||||
let ret = D {
|
||||
name: name, i: i, log: log, uid: ctr, trail: trail
|
||||
};
|
||||
indent_println(trail, &format!("+-- Make {}", ret));
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for D<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { trails &= !(1 << self.trail); };
|
||||
self.log.borrow_mut().push(self.uid);
|
||||
indent_println(self.trail, &format!("+-- Drop {}", self));
|
||||
indent_println(::PREF_INDENT, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue