aboutsummaryrefslogtreecommitdiff
path: root/src/test/run-pass/destructor-ordering.rs
blob: e8821fbcf03a6b21958412518441634dbe16ed81 (plain) (blame)
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
// xfail-boot
// xfail-stage0
// xfail-stage1
// xfail-stage2
// This test checks that destructors run in the right order.  Because
// stateful objects can't have destructors, we have the destructors
// record their expected order into a channel when they execute (so
// the object becomes 'io' rather than 'state').  Then each test case
// asserts that the channel produces values in ascending order.
//
// FIXME: Write an int->str function and concatenate the whole failure
// message into a single log statement (or, even better, a print).
//
// FIXME: check_order should take only 1 line in a test, not 2+a block
// block. Since destructor-having objects can't refer to mutable state
// (like the port), we'd need a with-like construct to do the same for
// stateful objects within a scope.
//
// FIXME #21: Each test should execute in its own task, so it can fail
// independently, writing its error message to a channel that the
// parent task aggregates.

type order_info = rec(int order, str msg);

io fn check_order(port[order_info] expected_p) {
  chan(expected_p) <| rec(order=-1, msg="");
  let mutable int actual = 0;
  auto expected <- expected_p;  // FIXME #121: Workaround for while(true) bug.
  auto done = -1;  // FIXME: Workaround for typechecking bug.
  while(expected.order != done) {
    if (expected.order != actual) {
      log expected.order;
      log " != ";
      log actual;
      log expected.msg;
      fail;
    }
    actual += 1;
    expected <- expected_p;
  }
}


obj dorder(chan[order_info] expected, int order, str message) {
  drop {
    expected <| rec(order=order, msg=message);
  }
}

io fn test_simple() {
  let port[order_info] tracker_p = port();
  auto tracker = chan(tracker_p);
  dorder(tracker, 1, "Reverse decl order");
  dorder(tracker, 0, "Reverse decl order");
  check_order(tracker_p);
}

io fn test_block() {
  let port[order_info] tracker_p = port();
  auto tracker = chan(tracker_p);
  {
    dorder(tracker, 2, "Before block");
    {
      dorder(tracker, 0, "Inside block");
    }
    dorder(tracker, 1, "After block");
  }
  check_order(tracker_p);
}

io fn test_decl_v_init() {
  let port[order_info] tracker_p = port();
  auto tracker = chan(tracker_p);
  {
    auto var1;
    auto var2;
    var2 = dorder(tracker, 0, "decl, not init");
    var1 = dorder(tracker, 1, "decl, not init");
  }
  check_order(tracker_p);
}

io fn test_overwritten_obj() {
  let port[order_info] tracker_p = port();
  auto tracker = chan(tracker_p);
  {
    auto var1 = dorder(tracker, 0, "overwritten object destroyed first");
    auto var2 = dorder(tracker, 2, "destroyed at end of scope");
    var1 = dorder(tracker, 3, "overwriter deleted in rev decl order");
    {
      dorder(tracker, 1, "overwritten object destroyed before end of scope");
    }
  }
  check_order(tracker_p);
}

// Used to embed dorder objects into an expression.  Note that the
// parameters don't get destroyed.
fn combine_dorders(dorder d1, dorder d2) -> int {
  ret 1;
}
io fn test_expression_destroyed_right_to_left() {
  let port[order_info] tracker_p = port();
  auto tracker = chan(tracker_p);
  {
    combine_dorders(dorder(tracker, 4, ""), dorder(tracker, 3, ""))
      / combine_dorders(dorder(tracker, 2, ""), dorder(tracker, 1, ""));
    {
      dorder(tracker, 0,
             "expression objects live to end of block, not statement");
    }
  }
  check_order(tracker_p);
}

io fn main() {
  test_simple();
  test_block();
  test_decl_v_init();
  test_overwritten_obj();
  test_expression_destroyed_right_to_left();
}