1 use crate::cdsl::ast::{Def, DefIndex, DefPool, Var, VarIndex, VarPool};
2 use crate::cdsl::typevar::{DerivedFunc, TypeSet, TypeVar};
3 
4 use std::collections::{HashMap, HashSet};
5 use std::iter::FromIterator;
6 
7 #[derive(Debug, Hash, PartialEq, Eq)]
8 pub(crate) enum Constraint {
9     /// Constraint specifying that a type var tv1 must be wider than or equal to type var tv2 at
10     /// runtime. This requires that:
11     /// 1) They have the same number of lanes
12     /// 2) In a lane tv1 has at least as many bits as tv2.
13     WiderOrEq(TypeVar, TypeVar),
14 
15     /// Constraint specifying that two derived type vars must have the same runtime type.
16     Eq(TypeVar, TypeVar),
17 
18     /// Constraint specifying that a type var must belong to some typeset.
19     InTypeset(TypeVar, TypeSet),
20 }
21 
22 impl Constraint {
translate_with<F: Fn(&TypeVar) -> TypeVar>(&self, func: F) -> Constraint23     fn translate_with<F: Fn(&TypeVar) -> TypeVar>(&self, func: F) -> Constraint {
24         match self {
25             Constraint::WiderOrEq(lhs, rhs) => {
26                 let lhs = func(&lhs);
27                 let rhs = func(&rhs);
28                 Constraint::WiderOrEq(lhs, rhs)
29             }
30             Constraint::Eq(lhs, rhs) => {
31                 let lhs = func(&lhs);
32                 let rhs = func(&rhs);
33                 Constraint::Eq(lhs, rhs)
34             }
35             Constraint::InTypeset(tv, ts) => {
36                 let tv = func(&tv);
37                 Constraint::InTypeset(tv, ts.clone())
38             }
39         }
40     }
41 
42     /// Creates a new constraint by replacing type vars by their hashmap equivalent.
translate_with_map( &self, original_to_own_typevar: &HashMap<&TypeVar, TypeVar>, ) -> Constraint43     fn translate_with_map(
44         &self,
45         original_to_own_typevar: &HashMap<&TypeVar, TypeVar>,
46     ) -> Constraint {
47         self.translate_with(|tv| substitute(original_to_own_typevar, tv))
48     }
49 
50     /// Creates a new constraint by replacing type vars by their canonical equivalent.
translate_with_env(&self, type_env: &TypeEnvironment) -> Constraint51     fn translate_with_env(&self, type_env: &TypeEnvironment) -> Constraint {
52         self.translate_with(|tv| type_env.get_equivalent(tv))
53     }
54 
is_trivial(&self) -> bool55     fn is_trivial(&self) -> bool {
56         match self {
57             Constraint::WiderOrEq(lhs, rhs) => {
58                 // Trivially true.
59                 if lhs == rhs {
60                     return true;
61                 }
62 
63                 let ts1 = lhs.get_typeset();
64                 let ts2 = rhs.get_typeset();
65 
66                 // Trivially true.
67                 if ts1.is_wider_or_equal(&ts2) {
68                     return true;
69                 }
70 
71                 // Trivially false.
72                 if ts1.is_narrower(&ts2) {
73                     return true;
74                 }
75 
76                 // Trivially false.
77                 if (&ts1.lanes & &ts2.lanes).is_empty() {
78                     return true;
79                 }
80 
81                 self.is_concrete()
82             }
83             Constraint::Eq(lhs, rhs) => lhs == rhs || self.is_concrete(),
84             Constraint::InTypeset(_, _) => {
85                 // The way InTypeset are made, they would always be trivial if we were applying the
86                 // same logic as the Python code did, so ignore this.
87                 self.is_concrete()
88             }
89         }
90     }
91 
92     /// Returns true iff all the referenced type vars are singletons.
is_concrete(&self) -> bool93     fn is_concrete(&self) -> bool {
94         match self {
95             Constraint::WiderOrEq(lhs, rhs) => {
96                 lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
97             }
98             Constraint::Eq(lhs, rhs) => {
99                 lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
100             }
101             Constraint::InTypeset(tv, _) => tv.singleton_type().is_some(),
102         }
103     }
104 
typevar_args(&self) -> Vec<&TypeVar>105     fn typevar_args(&self) -> Vec<&TypeVar> {
106         match self {
107             Constraint::WiderOrEq(lhs, rhs) => vec![lhs, rhs],
108             Constraint::Eq(lhs, rhs) => vec![lhs, rhs],
109             Constraint::InTypeset(tv, _) => vec![tv],
110         }
111     }
112 }
113 
114 #[derive(Clone, Copy)]
115 enum TypeEnvRank {
116     Singleton = 5,
117     Input = 4,
118     Intermediate = 3,
119     Output = 2,
120     Temp = 1,
121     Internal = 0,
122 }
123 
124 /// Class encapsulating the necessary bookkeeping for type inference.
125 pub(crate) struct TypeEnvironment {
126     vars: HashSet<VarIndex>,
127     ranks: HashMap<TypeVar, TypeEnvRank>,
128     equivalency_map: HashMap<TypeVar, TypeVar>,
129     pub constraints: Vec<Constraint>,
130 }
131 
132 impl TypeEnvironment {
new() -> Self133     fn new() -> Self {
134         TypeEnvironment {
135             vars: HashSet::new(),
136             ranks: HashMap::new(),
137             equivalency_map: HashMap::new(),
138             constraints: Vec::new(),
139         }
140     }
141 
register(&mut self, var_index: VarIndex, var: &mut Var)142     fn register(&mut self, var_index: VarIndex, var: &mut Var) {
143         self.vars.insert(var_index);
144         let rank = if var.is_input() {
145             TypeEnvRank::Input
146         } else if var.is_intermediate() {
147             TypeEnvRank::Intermediate
148         } else if var.is_output() {
149             TypeEnvRank::Output
150         } else {
151             assert!(var.is_temp());
152             TypeEnvRank::Temp
153         };
154         self.ranks.insert(var.get_or_create_typevar(), rank);
155     }
156 
add_constraint(&mut self, constraint: Constraint)157     fn add_constraint(&mut self, constraint: Constraint) {
158         if self.constraints.iter().any(|item| *item == constraint) {
159             return;
160         }
161 
162         // Check extra conditions for InTypeset constraints.
163         if let Constraint::InTypeset(tv, _) = &constraint {
164             assert!(
165                 tv.base.is_none(),
166                 "type variable is {:?}, while expecting none",
167                 tv
168             );
169             assert!(
170                 tv.name.starts_with("typeof_"),
171                 "Name \"{}\" should start with \"typeof_\"",
172                 tv.name
173             );
174         }
175 
176         self.constraints.push(constraint);
177     }
178 
179     /// Returns the canonical representative of the equivalency class of the given argument, or
180     /// duplicates it if it's not there yet.
get_equivalent(&self, tv: &TypeVar) -> TypeVar181     pub fn get_equivalent(&self, tv: &TypeVar) -> TypeVar {
182         let mut tv = tv;
183         while let Some(found) = self.equivalency_map.get(tv) {
184             tv = found;
185         }
186         match &tv.base {
187             Some(parent) => self
188                 .get_equivalent(&parent.type_var)
189                 .derived(parent.derived_func),
190             None => tv.clone(),
191         }
192     }
193 
194     /// Get the rank of tv in the partial order:
195     /// - TVs directly associated with a Var get their rank from the Var (see register()).
196     /// - Internally generated non-derived TVs implicitly get the lowest rank (0).
197     /// - Derived variables get their rank from their free typevar.
198     /// - Singletons have the highest rank.
199     /// - TVs associated with vars in a source pattern have a higher rank than TVs associated with
200     /// temporary vars.
rank(&self, tv: &TypeVar) -> u8201     fn rank(&self, tv: &TypeVar) -> u8 {
202         let actual_tv = match tv.base {
203             Some(_) => tv.free_typevar(),
204             None => Some(tv.clone()),
205         };
206 
207         let rank = match actual_tv {
208             Some(actual_tv) => match self.ranks.get(&actual_tv) {
209                 Some(rank) => Some(*rank),
210                 None => {
211                     assert!(
212                         !actual_tv.name.starts_with("typeof_"),
213                         "variable {} should be explicitly ranked",
214                         actual_tv.name
215                     );
216                     None
217                 }
218             },
219             None => None,
220         };
221 
222         let rank = match rank {
223             Some(rank) => rank,
224             None => {
225                 if tv.singleton_type().is_some() {
226                     TypeEnvRank::Singleton
227                 } else {
228                     TypeEnvRank::Internal
229                 }
230             }
231         };
232 
233         rank as u8
234     }
235 
236     /// Record the fact that the free tv1 is part of the same equivalence class as tv2. The
237     /// canonical representative of the merged class is tv2's canonical representative.
record_equivalent(&mut self, tv1: TypeVar, tv2: TypeVar)238     fn record_equivalent(&mut self, tv1: TypeVar, tv2: TypeVar) {
239         assert!(tv1.base.is_none());
240         assert!(self.get_equivalent(&tv1) == tv1);
241         if let Some(tv2_base) = &tv2.base {
242             // Ensure there are no cycles.
243             assert!(self.get_equivalent(&tv2_base.type_var) != tv1);
244         }
245         self.equivalency_map.insert(tv1, tv2);
246     }
247 
248     /// Get the free typevars in the current type environment.
free_typevars(&self, var_pool: &mut VarPool) -> Vec<TypeVar>249     pub fn free_typevars(&self, var_pool: &mut VarPool) -> Vec<TypeVar> {
250         let mut typevars = Vec::new();
251         typevars.extend(self.equivalency_map.keys().cloned());
252         typevars.extend(
253             self.vars
254                 .iter()
255                 .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
256         );
257 
258         let set: HashSet<TypeVar> = HashSet::from_iter(
259             typevars
260                 .iter()
261                 .map(|tv| self.get_equivalent(tv).free_typevar())
262                 .filter(|opt_tv| {
263                     // Filter out singleton types.
264                     opt_tv.is_some()
265                 })
266                 .map(|tv| tv.unwrap()),
267         );
268         Vec::from_iter(set)
269     }
270 
271     /// Normalize by collapsing any roots that don't correspond to a concrete type var AND have a
272     /// single type var derived from them or equivalent to them.
273     ///
274     /// e.g. if we have a root of the tree that looks like:
275     ///
276     ///   typeof_a   typeof_b
277     ///          \\  /
278     ///       typeof_x
279     ///           |
280     ///         half_width(1)
281     ///           |
282     ///           1
283     ///
284     /// we want to collapse the linear path between 1 and typeof_x. The resulting graph is:
285     ///
286     ///   typeof_a   typeof_b
287     ///          \\  /
288     ///       typeof_x
normalize(&mut self, var_pool: &mut VarPool)289     fn normalize(&mut self, var_pool: &mut VarPool) {
290         let source_tvs: HashSet<TypeVar> = HashSet::from_iter(
291             self.vars
292                 .iter()
293                 .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
294         );
295 
296         let mut children: HashMap<TypeVar, HashSet<TypeVar>> = HashMap::new();
297 
298         // Insert all the parents found by the derivation relationship.
299         for type_var in self.equivalency_map.values() {
300             if type_var.base.is_none() {
301                 continue;
302             }
303 
304             let parent_tv = type_var.free_typevar();
305             if parent_tv.is_none() {
306                 // Ignore this type variable, it's a singleton.
307                 continue;
308             }
309             let parent_tv = parent_tv.unwrap();
310 
311             children
312                 .entry(parent_tv)
313                 .or_insert_with(HashSet::new)
314                 .insert(type_var.clone());
315         }
316 
317         // Insert all the explicit equivalency links.
318         for (equivalent_tv, canon_tv) in self.equivalency_map.iter() {
319             children
320                 .entry(canon_tv.clone())
321                 .or_insert_with(HashSet::new)
322                 .insert(equivalent_tv.clone());
323         }
324 
325         // Remove links that are straight paths up to typevar of variables.
326         for free_root in self.free_typevars(var_pool) {
327             let mut root = &free_root;
328             while !source_tvs.contains(&root)
329                 && children.contains_key(&root)
330                 && children.get(&root).unwrap().len() == 1
331             {
332                 let child = children.get(&root).unwrap().iter().next().unwrap();
333                 assert_eq!(self.equivalency_map[child], root.clone());
334                 self.equivalency_map.remove(child);
335                 root = child;
336             }
337         }
338     }
339 
340     /// Extract a clean type environment from self, that only mentions type vars associated with
341     /// real variables.
extract(self, var_pool: &mut VarPool) -> TypeEnvironment342     fn extract(self, var_pool: &mut VarPool) -> TypeEnvironment {
343         let vars_tv: HashSet<TypeVar> = HashSet::from_iter(
344             self.vars
345                 .iter()
346                 .map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
347         );
348 
349         let mut new_equivalency_map: HashMap<TypeVar, TypeVar> = HashMap::new();
350         for tv in &vars_tv {
351             let canon_tv = self.get_equivalent(tv);
352             if *tv != canon_tv {
353                 new_equivalency_map.insert(tv.clone(), canon_tv.clone());
354             }
355 
356             // Sanity check: the translated type map should only refer to real variables.
357             assert!(vars_tv.contains(tv));
358             let canon_free_tv = canon_tv.free_typevar();
359             assert!(canon_free_tv.is_none() || vars_tv.contains(&canon_free_tv.unwrap()));
360         }
361 
362         let mut new_constraints: HashSet<Constraint> = HashSet::new();
363         for constraint in &self.constraints {
364             let constraint = constraint.translate_with_env(&self);
365             if constraint.is_trivial() || new_constraints.contains(&constraint) {
366                 continue;
367             }
368 
369             // Sanity check: translated constraints should refer only to real variables.
370             for arg in constraint.typevar_args() {
371                 let arg_free_tv = arg.free_typevar();
372                 assert!(arg_free_tv.is_none() || vars_tv.contains(&arg_free_tv.unwrap()));
373             }
374 
375             new_constraints.insert(constraint);
376         }
377 
378         TypeEnvironment {
379             vars: self.vars,
380             ranks: self.ranks,
381             equivalency_map: new_equivalency_map,
382             constraints: Vec::from_iter(new_constraints),
383         }
384     }
385 }
386 
387 /// Replaces an external type variable according to the following rules:
388 /// - if a local copy is present in the map, return it.
389 /// - or if it's derived, create a local derived one that recursively substitutes the parent.
390 /// - or return itself.
substitute(map: &HashMap<&TypeVar, TypeVar>, external_type_var: &TypeVar) -> TypeVar391 fn substitute(map: &HashMap<&TypeVar, TypeVar>, external_type_var: &TypeVar) -> TypeVar {
392     match map.get(&external_type_var) {
393         Some(own_type_var) => own_type_var.clone(),
394         None => match &external_type_var.base {
395             Some(parent) => {
396                 let parent_substitute = substitute(map, &parent.type_var);
397                 TypeVar::derived(&parent_substitute, parent.derived_func)
398             }
399             None => external_type_var.clone(),
400         },
401     }
402 }
403 
404 /// Normalize a (potentially derived) typevar using the following rules:
405 ///
406 /// - vector and width derived functions commute
407 ///     {HALF,DOUBLE}VECTOR({HALF,DOUBLE}WIDTH(base)) ->
408 ///     {HALF,DOUBLE}WIDTH({HALF,DOUBLE}VECTOR(base))
409 ///
410 /// - half/double pairs collapse
411 ///     {HALF,DOUBLE}WIDTH({DOUBLE,HALF}WIDTH(base)) -> base
412 ///     {HALF,DOUBLE}VECTOR({DOUBLE,HALF}VECTOR(base)) -> base
canonicalize_derivations(tv: TypeVar) -> TypeVar413 fn canonicalize_derivations(tv: TypeVar) -> TypeVar {
414     let base = match &tv.base {
415         Some(base) => base,
416         None => return tv,
417     };
418 
419     let derived_func = base.derived_func;
420 
421     if let Some(base_base) = &base.type_var.base {
422         let base_base_tv = &base_base.type_var;
423         match (derived_func, base_base.derived_func) {
424             (DerivedFunc::HalfWidth, DerivedFunc::DoubleWidth)
425             | (DerivedFunc::DoubleWidth, DerivedFunc::HalfWidth)
426             | (DerivedFunc::HalfVector, DerivedFunc::DoubleVector)
427             | (DerivedFunc::DoubleVector, DerivedFunc::HalfVector) => {
428                 // Cancelling bijective transformations. This doesn't hide any overflow issues
429                 // since derived type sets are checked upon derivaion, and base typesets are only
430                 // allowed to shrink.
431                 return canonicalize_derivations(base_base_tv.clone());
432             }
433             (DerivedFunc::HalfWidth, DerivedFunc::HalfVector)
434             | (DerivedFunc::HalfWidth, DerivedFunc::DoubleVector)
435             | (DerivedFunc::DoubleWidth, DerivedFunc::DoubleVector)
436             | (DerivedFunc::DoubleWidth, DerivedFunc::HalfVector) => {
437                 // Arbitrarily put WIDTH derivations before VECTOR derivations, since they commute.
438                 return canonicalize_derivations(
439                     base_base_tv
440                         .derived(derived_func)
441                         .derived(base_base.derived_func),
442                 );
443             }
444             _ => {}
445         };
446     }
447 
448     canonicalize_derivations(base.type_var.clone()).derived(derived_func)
449 }
450 
451 /// Given typevars tv1 and tv2 (which could be derived from one another), constrain their typesets
452 /// to be the same. When one is derived from the other, repeat the constrain process until
453 /// a fixed point is reached.
constrain_fixpoint(tv1: &TypeVar, tv2: &TypeVar)454 fn constrain_fixpoint(tv1: &TypeVar, tv2: &TypeVar) {
455     loop {
456         let old_tv1_ts = tv1.get_typeset().clone();
457         tv2.constrain_types(tv1.clone());
458         if tv1.get_typeset() == old_tv1_ts {
459             break;
460         }
461     }
462 
463     let old_tv2_ts = tv2.get_typeset();
464     tv1.constrain_types(tv2.clone());
465     // The above loop should ensure that all reference cycles have been handled.
466     assert!(old_tv2_ts == tv2.get_typeset());
467 }
468 
469 /// Unify tv1 and tv2 in the given type environment. tv1 must have a rank greater or equal to tv2's
470 /// one, modulo commutations.
unify(tv1: &TypeVar, tv2: &TypeVar, type_env: &mut TypeEnvironment) -> Result<(), String>471 fn unify(tv1: &TypeVar, tv2: &TypeVar, type_env: &mut TypeEnvironment) -> Result<(), String> {
472     let tv1 = canonicalize_derivations(type_env.get_equivalent(tv1));
473     let tv2 = canonicalize_derivations(type_env.get_equivalent(tv2));
474 
475     if tv1 == tv2 {
476         // Already unified.
477         return Ok(());
478     }
479 
480     if type_env.rank(&tv2) < type_env.rank(&tv1) {
481         // Make sure tv1 always has the smallest rank, since real variables have the higher rank
482         // and we want them to be the canonical representatives of their equivalency classes.
483         return unify(&tv2, &tv1, type_env);
484     }
485 
486     constrain_fixpoint(&tv1, &tv2);
487 
488     if tv1.get_typeset().size() == 0 || tv2.get_typeset().size() == 0 {
489         return Err(format!(
490             "Error: empty type created when unifying {} and {}",
491             tv1.name, tv2.name
492         ));
493     }
494 
495     let base = match &tv1.base {
496         Some(base) => base,
497         None => {
498             type_env.record_equivalent(tv1, tv2);
499             return Ok(());
500         }
501     };
502 
503     if let Some(inverse) = base.derived_func.inverse() {
504         return unify(&base.type_var, &tv2.derived(inverse), type_env);
505     }
506 
507     type_env.add_constraint(Constraint::Eq(tv1, tv2));
508     Ok(())
509 }
510 
511 /// Perform type inference on one Def in the current type environment and return an updated type
512 /// environment or error.
513 ///
514 /// At a high level this works by creating fresh copies of each formal type var in the Def's
515 /// instruction's signature, and unifying the formal typevar with the corresponding actual typevar.
infer_definition( def: &Def, var_pool: &mut VarPool, type_env: TypeEnvironment, last_type_index: &mut usize, ) -> Result<TypeEnvironment, String>516 fn infer_definition(
517     def: &Def,
518     var_pool: &mut VarPool,
519     type_env: TypeEnvironment,
520     last_type_index: &mut usize,
521 ) -> Result<TypeEnvironment, String> {
522     let apply = &def.apply;
523     let inst = &apply.inst;
524 
525     let mut type_env = type_env;
526     let free_formal_tvs = inst.all_typevars();
527 
528     let mut original_to_own_typevar: HashMap<&TypeVar, TypeVar> = HashMap::new();
529     for &tv in &free_formal_tvs {
530         assert!(original_to_own_typevar
531             .insert(
532                 tv,
533                 TypeVar::copy_from(tv, format!("own_{}", last_type_index))
534             )
535             .is_none());
536         *last_type_index += 1;
537     }
538 
539     // Update the mapping with any explicity bound type vars:
540     for (i, value_type) in apply.value_types.iter().enumerate() {
541         let singleton = TypeVar::new_singleton(value_type.clone());
542         assert!(original_to_own_typevar
543             .insert(free_formal_tvs[i], singleton)
544             .is_some());
545     }
546 
547     // Get fresh copies for each typevar in the signature (both free and derived).
548     let mut formal_tvs = Vec::new();
549     formal_tvs.extend(inst.value_results.iter().map(|&i| {
550         substitute(
551             &original_to_own_typevar,
552             inst.operands_out[i].type_var().unwrap(),
553         )
554     }));
555     formal_tvs.extend(inst.value_opnums.iter().map(|&i| {
556         substitute(
557             &original_to_own_typevar,
558             inst.operands_in[i].type_var().unwrap(),
559         )
560     }));
561 
562     // Get the list of actual vars.
563     let mut actual_vars = Vec::new();
564     actual_vars.extend(inst.value_results.iter().map(|&i| def.defined_vars[i]));
565     actual_vars.extend(
566         inst.value_opnums
567             .iter()
568             .map(|&i| apply.args[i].unwrap_var()),
569     );
570 
571     // Get the list of the actual TypeVars.
572     let mut actual_tvs = Vec::new();
573     for var_index in actual_vars {
574         let var = var_pool.get_mut(var_index);
575         type_env.register(var_index, var);
576         actual_tvs.push(var.get_or_create_typevar());
577     }
578 
579     // Make sure we start unifying with the control type variable first, by putting it at the
580     // front of both vectors.
581     if let Some(poly) = &inst.polymorphic_info {
582         let own_ctrl_tv = &original_to_own_typevar[&poly.ctrl_typevar];
583         let ctrl_index = formal_tvs.iter().position(|tv| tv == own_ctrl_tv).unwrap();
584         if ctrl_index != 0 {
585             formal_tvs.swap(0, ctrl_index);
586             actual_tvs.swap(0, ctrl_index);
587         }
588     }
589 
590     // Unify each actual type variable with the corresponding formal type variable.
591     for (actual_tv, formal_tv) in actual_tvs.iter().zip(&formal_tvs) {
592         if let Err(msg) = unify(actual_tv, formal_tv, &mut type_env) {
593             return Err(format!(
594                 "fail ti on {} <: {}: {}",
595                 actual_tv.name, formal_tv.name, msg
596             ));
597         }
598     }
599 
600     // Add any instruction specific constraints.
601     for constraint in &inst.constraints {
602         type_env.add_constraint(constraint.translate_with_map(&original_to_own_typevar));
603     }
604 
605     Ok(type_env)
606 }
607 
608 /// Perform type inference on an transformation. Return an updated type environment or error.
infer_transform( src: DefIndex, dst: &[DefIndex], def_pool: &DefPool, var_pool: &mut VarPool, ) -> Result<TypeEnvironment, String>609 pub(crate) fn infer_transform(
610     src: DefIndex,
611     dst: &[DefIndex],
612     def_pool: &DefPool,
613     var_pool: &mut VarPool,
614 ) -> Result<TypeEnvironment, String> {
615     let mut type_env = TypeEnvironment::new();
616     let mut last_type_index = 0;
617 
618     // Execute type inference on the source pattern.
619     type_env = infer_definition(def_pool.get(src), var_pool, type_env, &mut last_type_index)
620         .map_err(|err| format!("In src pattern: {}", err))?;
621 
622     // Collect the type sets once after applying the source patterm; we'll compare the typesets
623     // after we've also considered the destination pattern, and will emit supplementary InTypeset
624     // checks if they don't match.
625     let src_typesets = type_env
626         .vars
627         .iter()
628         .map(|&var_index| {
629             let var = var_pool.get_mut(var_index);
630             let tv = type_env.get_equivalent(&var.get_or_create_typevar());
631             (var_index, tv.get_typeset())
632         })
633         .collect::<Vec<_>>();
634 
635     // Execute type inference on the destination pattern.
636     for (i, &def_index) in dst.iter().enumerate() {
637         let def = def_pool.get(def_index);
638         type_env = infer_definition(def, var_pool, type_env, &mut last_type_index)
639             .map_err(|err| format!("line {}: {}", i, err))?;
640     }
641 
642     for (var_index, src_typeset) in src_typesets {
643         let var = var_pool.get(var_index);
644         if !var.has_free_typevar() {
645             continue;
646         }
647         let tv = type_env.get_equivalent(&var.get_typevar().unwrap());
648         let new_typeset = tv.get_typeset();
649         assert!(
650             new_typeset.is_subset(&src_typeset),
651             "type sets can only get narrower"
652         );
653         if new_typeset != src_typeset {
654             type_env.add_constraint(Constraint::InTypeset(tv.clone(), new_typeset.clone()));
655         }
656     }
657 
658     type_env.normalize(var_pool);
659 
660     Ok(type_env.extract(var_pool))
661 }
662