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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
// SPDX-License-Identifier: CC0-1.0
//! "Finalized" Incomplete Type Data
//!
//! This structure is essentially the same as [types::Final](super::Final) except
//! that it has free variables (represented by strings) and supports self-reference.
//! The purpose of this structure is to provide a useful representation of a type
//! in error messages.
//!
use crate::dag::{Dag, DagLike, NoSharing};
use crate::types::union_bound::PointerLike;
use super::context::BoundRefSharing;
use super::{Bound, BoundRef, Context};
use super::{MAX_DISPLAY_DEPTH, MAX_DISPLAY_LENGTH};
use std::fmt;
use std::sync::Arc;
/// An incomplete type bound for use in error messages.
#[derive(Clone)]
pub enum Incomplete {
/// A free variable.
Free(String),
/// A type containing this type.
Cycle,
/// A sum of two other types
Sum(Arc<Incomplete>, Arc<Incomplete>),
/// A product of two other types
Product(Arc<Incomplete>, Arc<Incomplete>),
/// A complete type (including unit)
Final(Arc<super::Final>),
}
impl Incomplete {
/// Private helper function for `Incomplete::drop`. See `node::Inner::into_dag` which is
/// similar but doesn't require unsafe code.
fn into_dag(mut self) -> Dag<Arc<Self>> {
use core::{mem, ptr};
let ret = match &mut self {
Incomplete::Sum(ref mut left, ref mut right)
| Incomplete::Product(ref mut left, ref mut right) => {
// Because Rust is stupid, we cannot just move 'left' and 'right' out of 'self'.
// We get the error "cannot move out of type that implements Drop". This message
// dates to before Rust 1.0, before mem::forget was marked safe, and has no
// justification that stands up to any scrutiny. There have been mulitple RFCs to
// remove it but for some reason they have never gone anywhere. See for example
//
// https://internals.rust-lang.org/t/destructuring-droppable-structs/20993/41
//
// Anyway, instead we have to use unsafe code here to do this obviously-safe
// operation in an overcomplicated and hard-to-review way.
unsafe {
// SAFETY we are calling `ptr::read` on valid pointers (they come directly
// from references, which are always valid), and we will mem::forget their old
// locations before any early returns or panics.
let left = ptr::read(left);
let right = ptr::read(right);
Dag::Binary(left, right)
}
}
Incomplete::Cycle => Dag::Nullary,
Incomplete::Free(s) => {
// SAFETY: see above. We are manually dropping `s` here, which we have to do
// by reading it out of &mut self, because Rust is stupid.
unsafe {
ptr::read(s);
}
Dag::Nullary
}
Incomplete::Final(fin) => {
// SAFETY: see above
unsafe {
ptr::read(fin);
}
Dag::Nullary
}
};
// Because `Incomplete::drop` calls this method, we cannot allow `self` to be dropped
// under any circumstances, or else we will infinitely recurse and stack-overflow. (This
// is why we had to do ptr::read in every branch above).
mem::forget(self);
ret
}
}
impl DagLike for &'_ Incomplete {
type Node = Incomplete;
fn data(&self) -> &Incomplete {
self
}
fn as_dag_node(&self) -> Dag<Self> {
match *self {
Incomplete::Free(_) | Incomplete::Cycle | Incomplete::Final(_) => Dag::Nullary,
Incomplete::Sum(ref left, ref right) | Incomplete::Product(ref left, ref right) => {
Dag::Binary(left, right)
}
}
}
}
impl fmt::Debug for Incomplete {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Incomplete {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut skip_next = false;
for data in self.verbose_pre_order_iter::<NoSharing>(Some(MAX_DISPLAY_DEPTH)) {
if data.index > MAX_DISPLAY_LENGTH {
write!(f, "... [truncated type after {} nodes]", MAX_DISPLAY_LENGTH)?;
return Ok(());
}
if data.depth == MAX_DISPLAY_DEPTH {
if data.n_children_yielded == 0 {
f.write_str("...")?;
}
continue;
}
if skip_next {
skip_next = false;
continue;
}
match (data.node, data.n_children_yielded) {
(Incomplete::Free(ref s), _) => f.write_str(s)?,
(Incomplete::Cycle, _) => f.write_str("<self-reference>")?,
// special-case 1 + A as A?
(Incomplete::Sum(ref left, _), 0) if left.is_unit() => {
skip_next = true;
}
(Incomplete::Sum(ref left, _), 1) if left.is_unit() => {}
(Incomplete::Sum(ref left, _), 2) if left.is_unit() => {
f.write_str("?")?;
}
// other sums and products
(Incomplete::Sum(..), 0) | (Incomplete::Product(..), 0) => {
if data.index > 0 {
f.write_str("(")?;
}
}
(Incomplete::Sum(..), 2) | (Incomplete::Product(..), 2) => {
if data.index > 0 {
f.write_str(")")?;
}
}
(Incomplete::Sum(..), _) => f.write_str(" + ")?,
(Incomplete::Product(..), _) => f.write_str(" × ")?,
(Incomplete::Final(ref fnl), _) => fnl.fmt(f)?,
}
}
Ok(())
}
}
impl Drop for Incomplete {
fn drop(&mut self) {
// Note: this is basically identical to the drop impl for node::Node.
fn push_children(stack: &mut Vec<Arc<Incomplete>>, inner: Incomplete) {
use crate::dag::Dag;
match inner.into_dag() {
Dag::Nullary => {}
Dag::Unary(child) => stack.push(child),
Dag::Binary(left, right) => {
stack.push(left);
stack.push(right);
}
}
}
let mut stack = Vec::new();
push_children(&mut stack, std::mem::replace(self, Incomplete::Cycle));
while let Some(child) = stack.pop() {
if let Some(mut child) = Arc::into_inner(child) {
push_children(&mut stack, std::mem::replace(&mut child, Incomplete::Cycle));
}
}
}
}
impl Incomplete {
/// Whether this "incomplete bound" is the unit type.
pub fn is_unit(&self) -> bool {
if let Incomplete::Final(ref fnl) = self {
fnl.is_unit()
} else {
false
}
}
/// Does the occurs-check on a type bound.
///
/// Returns None on success, and a Some(Incomplete) indicating the occurs-check
/// failure if there is a cyclic reference.
pub(super) fn occurs_check<'brand>(
ctx: &Context<'brand>,
bound_ref: BoundRef<'brand>,
) -> Option<Arc<Self>> {
use std::collections::HashSet;
use super::context::OccursCheckId;
/// Helper type for the occurs-check.
enum OccursCheckStack<'brand> {
Iterate(BoundRef<'brand>),
Complete(OccursCheckId<'brand>),
}
// First, do occurs-check to ensure that we have no infinitely sized types.
let mut stack = vec![OccursCheckStack::Iterate(bound_ref)];
let mut in_progress = HashSet::new();
let mut completed = HashSet::new();
while let Some(top) = stack.pop() {
let bound = match top {
OccursCheckStack::Complete(id) => {
in_progress.remove(&id);
completed.insert(id);
continue;
}
OccursCheckStack::Iterate(b) => b,
};
let id = bound.occurs_check_id();
if completed.contains(&id) {
// Once we have iterated through a type, we don't need to check it again.
// Without this shortcut the occurs-check would take exponential time.
continue;
}
if !in_progress.insert(id) {
// FIXME unwind the stack to somehow provide a more useful trace of the occurs-check failure
return Some(Arc::new(Self::Cycle));
}
stack.push(OccursCheckStack::Complete(id));
if let Some((_, child)) = (ctx, bound.shallow_clone()).right_child() {
stack.push(OccursCheckStack::Iterate(child));
}
if let Some((_, child)) = (ctx, bound).left_child() {
stack.push(OccursCheckStack::Iterate(child));
}
}
None
}
pub(super) fn from_bound_ref<'brand>(
ctx: &Context<'brand>,
bound_ref: BoundRef<'brand>,
) -> Arc<Self> {
if let Some(err) = Self::occurs_check(ctx, bound_ref.shallow_clone()) {
return err;
}
// Now that we know our bound has finite size, we can safely use a
// post-order iterator on it.
let mut finalized = vec![];
for data in (ctx, bound_ref).post_order_iter::<BoundRefSharing<'_>>() {
let bound_get = data.node.0.get(&data.node.1);
let final_data = match bound_get {
Bound::Free(s) => Incomplete::Free(s),
Bound::Complete(ref arc) => Incomplete::Final(Arc::clone(arc)),
Bound::Sum(..) => Incomplete::Sum(
Arc::clone(&finalized[data.left_index.unwrap()]),
Arc::clone(&finalized[data.right_index.unwrap()]),
),
Bound::Product(..) => Incomplete::Product(
Arc::clone(&finalized[data.left_index.unwrap()]),
Arc::clone(&finalized[data.right_index.unwrap()]),
),
};
finalized.push(Arc::new(final_data));
}
finalized.pop().unwrap()
}
}