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
//! Generics: type parameters in scope (`T` in `class Box<T>`'s code, a
//! method's `<T>`), a generic's members read with its arguments
//! (`Box<i32>`'s `Value` is an `i32`), and a generic method's arguments
//! inferred from a call (`Max(1, 2)` is `Max<i32>`).
use std::collections::HashMap;
use super::Checker;
use super::model::Type;
use crate::ast::Generic;
/// Type parameters and what they stand for here.
pub(super) type Bound = HashMap<String, Type>;
impl Checker<'_, '_, '_> {
/// Runs `f` with `bound`'s type parameters in scope.
pub(super) fn with_types<R>(&mut self, bound: Bound, f: impl FnOnce(&mut Self) -> R) -> R {
self.type_params.push(bound);
let out = f(self);
self.type_params.pop();
out
}
/// What type parameter `name` stands for here, if one is in scope.
pub(super) fn type_param(&self, name: &str) -> Option<Type> {
self.type_params
.iter()
.rev()
.find_map(|b| b.get(name))
.cloned()
}
/// Own type `name`'s type parameters bound to `args` (unknown ones as
/// themselves).
pub(super) fn bound_of(&self, name: &str, args: &[Type]) -> Bound {
let generics = self
.own
.get(name, args.len())
.map(|t| t.generics.as_slice())
.unwrap_or_default();
generics
.iter()
.enumerate()
.map(|(i, g)| {
(
g.clone(),
args.get(i)
.cloned()
.unwrap_or_else(|| Type::Param(g.clone())),
)
})
.collect()
}
/// `f` inferring `param`'s type parameters among `free` from `given`
/// into `found`: whether `given` fits.
pub(super) fn unify(
&self,
param: &Type,
given: &Type,
free: &[String],
found: &mut Bound,
) -> bool {
match (param, given) {
(Type::Param(p), _) if free.contains(p) => match found.get(p) {
Some(t) => {
let t = t.clone();
if self.converts(given, &t) {
true
} else if self.converts(&t, given) {
found.insert(p.clone(), given.clone());
true
} else {
false
}
}
None => {
found.insert(p.clone(), given.clone());
true
}
},
// `given` as the type `param` names, among what it inherits:
// a `List<i32>` where an `IEnumerable<T>` is wanted binds `T`.
(Type::Own(b, ys), Type::Own(a, xs)) => {
let as_wanted = self
.lineage_of(a, xs)
.into_iter()
.find(|(k, _)| k.name == *b && k.arity == ys.len());
match as_wanted {
Some((_, xs)) => ys
.iter()
.zip(&xs)
.all(|(y, x)| self.unify(y, x, free, found)),
None => false,
}
}
(Type::Array(x, rx), Type::Array(y, ry)) => rx == ry && self.unify(x, y, free, found),
// An `ItemId` where a `ContentId<T>` is wanted: `T` is `ItemId`.
(Type::Own(b, ys), Type::Content(_) | Type::Tag(_)) if b == yog_script_ids::system::CONTENT_ID && ys.len() == 1 => {
self.unify(&ys[0], given, free, found)
}
// A `TagId<ItemId>` where a `TagId<T>` is wanted: `T` is `ItemId`.
(Type::Own(b, ys), Type::Tag(k)) if b == yog_script_ids::system::TAG_ID && ys.len() == 1 => {
self.unify(&ys[0], &Type::Content(k.clone()), free, found)
}
(Type::Nullable(x), Type::Nullable(y)) => self.unify(x, y, free, found),
(Type::Tuple(xs, _), Type::Tuple(ys, _)) => self.unify_tuples((xs, ys), free, found),
// A `T[]` as one of its interfaces: `IEnumerable<T>`'s `T` its item.
(Type::Own(b, ys), Type::Array(x, 1))
if yog_script_ids::system::ARRAY_INTERFACES.contains(&b.as_str())
&& ys.len() == 1 =>
{
self.unify(&ys[0], x, free, found)
}
// `null` where a nullable is wanted: nothing to bind.
(Type::Nullable(_), Type::Null) => true,
(Type::Nullable(x), y) => self.unify(x, y, free, found),
_ => self.converts(given, param),
}
}
}
/// Type parameters `names` standing for themselves.
pub(super) fn params_of_names(names: &[String]) -> Bound {
names
.iter()
.map(|n| (n.clone(), Type::Param(n.clone())))
.collect()
}
/// A method's or a type's own parameters, standing for themselves.
pub(super) fn params_of(generics: &[Generic]) -> Bound {
generics
.iter()
.filter(|g| !g.lifetime)
.map(|g| (g.name.text.clone(), Type::Param(g.name.text.clone())))
.collect()
}
impl Type {
/// The type with the parameters `bound` names replaced.
pub(super) fn substitute(&self, bound: &Bound) -> Type {
let sub = |t: &Type| t.substitute(bound);
match self {
Type::Param(p) => bound.get(p).cloned().unwrap_or_else(|| self.clone()),
Type::Own(n, args) => Type::Own(n.clone(), args.iter().map(sub).collect()),
Type::Array(t, rank) => Type::Array(Box::new(sub(t)), *rank),
Type::Nullable(t) => Type::Nullable(Box::new(sub(t))),
Type::Tuple(items, names) => {
Type::Tuple(items.iter().map(sub).collect(), names.clone())
}
Type::Fn(params, ret) => Type::Fn(params.iter().map(sub).collect(), Box::new(sub(ret))),
_ => self.clone(),
}
}
}