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
mod legalize;
mod lower;
mod unroll;
mod validate;
use crate::error::{ErrorEmitted, Handler};
use legalize::legalize_vector_accesses;
use lower::{
coalesce_prime_ops, lower_aliases, lower_array_ranges, lower_casts, lower_ifs,
lower_imm_accesses, lower_ins, lower_matches, lower_storage_accesses,
lower_union_variant_paths, replace_const_refs,
};
use unroll::unroll_generators;
use validate::validate;
impl super::Contract {
pub fn flatten(mut self, handler: &Handler) -> Result<Self, ErrorEmitted> {
// Transform each `match` declaration into equivalent `if` declarations, which are then
// lowered along with user defined `if` declarations next. Also transform `match`
// expressions into equivalent `select` expressions, with any inner constraints
// externalised.
let _ = lower_matches(handler, &mut self);
// Transform each `if` declaration into a collection of constraints. We do this early so
// that we don't have to worry about `if` declarations in any of the later passes. All
// other passes are safe to assume that `if` declarations and their content have
// already been converted to raw constraints.
let _ = lower_ifs(handler, &mut self);
// Plug const decls in everywhere so they maybe lowered below.
replace_const_refs(&mut self);
// Unroll each generator into one large conjuction
let _ = handler.scope(|handler| unroll_generators(handler, &mut self));
// Lower `in` expressions into more explicit comparisons.
let _ = lower_ins(handler, &mut self);
// Do some array checks now that generators have been unrolled (and ephemerals aren't
// going to cause problems). We're taking note of whether these fail to avoid superfluous
// array related errors later. Once we can move these checks back into the type-checker it
// shouldn't be necessary.
let mut array_check_failed = false;
let _ = handler.scope(|handler| {
for pred_key in self.preds.keys() {
self.check_array_lengths(handler, pred_key);
self.check_array_indexing(handler, pred_key);
self.check_array_compares(handler, pred_key);
}
if handler.has_errors() {
array_check_failed = true;
Err(handler.cancel())
} else {
Ok(())
}
});
// Lower array types to have simple integer ranges.
if !array_check_failed {
let _ = lower_array_ranges(handler, &mut self);
}
// Lower indexing or field access into immediates to the actual element or field.
let _ = lower_imm_accesses(handler, &mut self);
// Coalesce all prime ops back down to the lowest path expression.
coalesce_prime_ops(&mut self);
// This could be done straight after type checking but any error which prints the
// type until now will have the more informative aliased description. e.g.,
// `Height (int)` rather than just `int`.
lower_aliases(&mut self);
// Lower casts after aliases since we're leaving `int -> real` behind, but it's
// much easier if the `real` isn't still an alias.
let _ = lower_casts(handler, &mut self);
// Convert all paths which are still just references to union variants without a value
// (e.g., `option::none`) from Expr::Path to Expr::UnionVariant.
lower_union_variant_paths(&mut self);
// Insert OOB checks for storage vector accesses
let _ = legalize_vector_accesses(handler, &mut self);
// Lower all storage accesses to __pre_state, __post_state, __pre_state_extern, and
// __post_state_extern intrinsics.
let _ = lower_storage_accesses(handler, &mut self);
// Ensure that the final contract is indeed final
if !handler.has_errors() {
validate(handler, &mut self);
}
if handler.has_errors() {
return Err(handler.cancel());
}
Ok(self)
}
}