summaryrefslogtreecommitdiffstats
path: root/src/test/ui/cleanup-rvalue-scopes.rs
blob: b80f95b79f91d2966ca82abebd03046008f9816a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// run-pass
#![allow(unused_braces)]
#![allow(non_snake_case)]
#![allow(unused_variables)]
// Test that destructors for rvalue temporaries run either at end of
// statement or end of block, as appropriate given the temporary
// lifetime rules.

#![feature(box_patterns)]

use std::ops::Drop;

static mut FLAGS: u64 = 0;

struct Box<T> { f: T }
struct AddFlags { bits: u64 }

fn AddFlags(bits: u64) -> AddFlags {
    AddFlags { bits: bits }
}

fn arg(exp: u64, _x: &AddFlags) {
    check_flags(exp);
}

fn pass<T>(v: T) -> T {
    v
}

fn check_flags(exp: u64) {
    unsafe {
        let x = FLAGS;
        FLAGS = 0;
        println!("flags {}, expected {}", x, exp);
        assert_eq!(x, exp);
    }
}

impl AddFlags {
    fn check_flags<'a>(&'a self, exp: u64) -> &'a AddFlags {
        check_flags(exp);
        self
    }

    fn bits(&self) -> u64 {
        self.bits
    }
}

impl Drop for AddFlags {
    fn drop(&mut self) {
        unsafe {
            FLAGS = FLAGS + self.bits;
        }
    }
}

macro_rules! end_of_block {
    ($pat:pat, $expr:expr) => (
        {
            println!("end_of_block({})", stringify!({let $pat = $expr;}));

            {
                // Destructor here does not run until exit from the block.
                let $pat = $expr;
                check_flags(0);
            }
            check_flags(1);
        }
    )
}

macro_rules! end_of_stmt {
    ($pat:pat, $expr:expr) => (
        {
            println!("end_of_stmt({})", stringify!($expr));

            {
                // Destructor here run after `let` statement
                // terminates.
                let $pat = $expr;
                check_flags(1);
            }

            check_flags(0);
        }
    )
}

pub fn main() {

    // In all these cases, we trip over the rules designed to cover
    // the case where we are taking addr of rvalue and storing that
    // addr into a stack slot, either via `let ref` or via a `&` in
    // the initializer.

    end_of_block!(_x, AddFlags(1));
    end_of_block!(_x, &AddFlags(1));
    end_of_block!(_x, & &AddFlags(1));
    end_of_block!(_x, Box { f: AddFlags(1) });
    end_of_block!(_x, Box { f: &AddFlags(1) });
    end_of_block!(_x, Box { f: &AddFlags(1) });
    end_of_block!(_x, pass(AddFlags(1)));
    end_of_block!(ref _x, AddFlags(1));
    end_of_block!(AddFlags { bits: ref _x }, AddFlags(1));
    end_of_block!(&AddFlags { bits }, &AddFlags(1));
    end_of_block!((_, ref _y), (AddFlags(1), 22));
    end_of_block!(box ref _x, std::boxed::Box::new(AddFlags(1)));
    end_of_block!(box _x, std::boxed::Box::new(AddFlags(1)));
    end_of_block!(_, { { check_flags(0); &AddFlags(1) } });
    end_of_block!(_, &((Box { f: AddFlags(1) }).f));
    end_of_block!(_, &(([AddFlags(1)])[0]));

    // LHS does not create a ref binding, so temporary lives as long
    // as statement, and we do not move the AddFlags out:
    end_of_stmt!(_, AddFlags(1));
    end_of_stmt!((_, _), (AddFlags(1), 22));

    // `&` operator appears inside an arg to a function,
    // so it is not prolonged:
    end_of_stmt!(ref _x, arg(0, &AddFlags(1)));

    // autoref occurs inside receiver, so temp lifetime is not
    // prolonged:
    end_of_stmt!(ref _x, AddFlags(1).check_flags(0).bits());

    // No reference is created on LHS, thus RHS is moved into
    // a temporary that lives just as long as the statement.
    end_of_stmt!(AddFlags { bits }, AddFlags(1));
}