Add 'Pair' pattern and rework internals to use it.
Currently, pattern-matching on 'Pair' is handled by treating Pair as a record, which comes as slightly odd given that it isn't actually a record and isn't user-defined. Thus now, every use of a record must distinguish between Pairs and other kind of records -- which screams for another variant constructor instead. We cannot use `Tuple` either for this, because then we have no ways to tell 2-tuples apart from pairs, which is the whole point here. So the most sensical thing to do is to define a new pattern `Pair` which is akin to tuples, but simpler since we know the number of elements and it's always 2.
This commit is contained in:
parent
897b5d1d7e
commit
91a7e77ab4
|
@ -1243,6 +1243,12 @@ pub enum Pattern<Constructor, Type> {
|
||||||
tipo: Type,
|
tipo: Type,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Pair {
|
||||||
|
location: Span,
|
||||||
|
fst: Box<Self>,
|
||||||
|
snd: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
Tuple {
|
Tuple {
|
||||||
location: Span,
|
location: Span,
|
||||||
elems: Vec<Self>,
|
elems: Vec<Self>,
|
||||||
|
@ -1258,6 +1264,7 @@ impl<A, B> Pattern<A, B> {
|
||||||
| Pattern::List { location, .. }
|
| Pattern::List { location, .. }
|
||||||
| Pattern::Discard { location, .. }
|
| Pattern::Discard { location, .. }
|
||||||
| Pattern::Tuple { location, .. }
|
| Pattern::Tuple { location, .. }
|
||||||
|
| Pattern::Pair { location, .. }
|
||||||
| Pattern::Constructor { location, .. } => *location,
|
| Pattern::Constructor { location, .. } => *location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1327,6 +1334,19 @@ impl TypedPattern {
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Pattern::Pair { fst, snd, .. } => match &**value {
|
||||||
|
Type::Pair {
|
||||||
|
fst: fst_v,
|
||||||
|
snd: snd_v,
|
||||||
|
..
|
||||||
|
} => [fst, snd]
|
||||||
|
.into_iter()
|
||||||
|
.zip([fst_v, snd_v].iter())
|
||||||
|
.find_map(|(e, t)| e.find_node(byte_index, t))
|
||||||
|
.or(Some(Located::Pattern(self, value.clone()))),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
|
||||||
Pattern::Constructor {
|
Pattern::Constructor {
|
||||||
arguments, tipo, ..
|
arguments, tipo, ..
|
||||||
} => match &**tipo {
|
} => match &**tipo {
|
||||||
|
@ -1340,6 +1360,7 @@ impl TypedPattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This function definition is weird, see where this is used and how.
|
||||||
pub fn tipo(&self, value: &TypedExpr) -> Option<Rc<Type>> {
|
pub fn tipo(&self, value: &TypedExpr) -> Option<Rc<Type>> {
|
||||||
match self {
|
match self {
|
||||||
Pattern::Int { .. } => Some(builtins::int()),
|
Pattern::Int { .. } => Some(builtins::int()),
|
||||||
|
@ -1347,7 +1368,7 @@ impl TypedPattern {
|
||||||
Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => {
|
Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => {
|
||||||
Some(value.tipo())
|
Some(value.tipo())
|
||||||
}
|
}
|
||||||
Pattern::List { .. } | Pattern::Tuple { .. } => None,
|
Pattern::List { .. } | Pattern::Tuple { .. } | Pattern::Pair { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1779,6 +1779,14 @@ impl<'comments> Formatter<'comments> {
|
||||||
wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group()
|
wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pattern::Pair { fst, snd, .. } => "Pair"
|
||||||
|
.to_doc()
|
||||||
|
.append("(")
|
||||||
|
.append(self.pattern(fst))
|
||||||
|
.append(",")
|
||||||
|
.append(self.pattern(snd))
|
||||||
|
.append(")"),
|
||||||
|
|
||||||
Pattern::List { elements, tail, .. } => {
|
Pattern::List { elements, tail, .. } => {
|
||||||
let elements_document =
|
let elements_document =
|
||||||
join(elements.iter().map(|e| self.pattern(e)), break_(",", ", "));
|
join(elements.iter().map(|e| self.pattern(e)), break_(",", ", "));
|
||||||
|
|
|
@ -975,6 +975,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::let_assignment(name, value, then)
|
AirTree::let_assignment(name, value, then)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Assign { name, pattern, .. } => {
|
Pattern::Assign { name, pattern, .. } => {
|
||||||
let inner_pattern = self.assignment(
|
let inner_pattern = self.assignment(
|
||||||
pattern,
|
pattern,
|
||||||
|
@ -985,6 +986,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
);
|
);
|
||||||
AirTree::let_assignment(name, value, inner_pattern)
|
AirTree::let_assignment(name, value, inner_pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Discard { name, .. } => {
|
Pattern::Discard { name, .. } => {
|
||||||
if props.full_check {
|
if props.full_check {
|
||||||
let name = &format!("__discard_expect_{}", name);
|
let name = &format!("__discard_expect_{}", name);
|
||||||
|
@ -1015,6 +1017,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::no_op(then)
|
AirTree::no_op(then)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::List { elements, tail, .. } => {
|
Pattern::List { elements, tail, .. } => {
|
||||||
assert!(tipo.is_list());
|
assert!(tipo.is_list());
|
||||||
assert!(props.kind.is_expect());
|
assert!(props.kind.is_expect());
|
||||||
|
@ -1141,47 +1144,33 @@ impl<'a> CodeGenerator<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pairs overlap by using the Constructor pattern type
|
|
||||||
// The logic is slightly different for pairs
|
|
||||||
Pattern::Constructor {
|
|
||||||
arguments,
|
|
||||||
constructor: PatternConstructor::Record { name, field_map },
|
|
||||||
tipo: constr_tipo,
|
|
||||||
..
|
|
||||||
} if tipo.is_pair() => {
|
|
||||||
// Constr and Pair execution branch
|
|
||||||
let field_map = field_map.clone();
|
|
||||||
|
|
||||||
|
Pattern::Pair {
|
||||||
|
fst,
|
||||||
|
snd,
|
||||||
|
location: _,
|
||||||
|
} => {
|
||||||
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
|
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
|
||||||
|
|
||||||
for (index, arg) in constr_tipo.get_inner_types().iter().enumerate() {
|
for (index, arg) in tipo.get_inner_types().iter().enumerate() {
|
||||||
let field_type = arg.clone();
|
let field_type = arg.clone();
|
||||||
|
|
||||||
type_map.insert(index, field_type);
|
type_map.insert(index, field_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(type_map.len() >= arguments.len());
|
assert!(type_map.len() == 2);
|
||||||
|
|
||||||
let mut fields = vec![];
|
let mut fields = vec![];
|
||||||
|
|
||||||
let then = arguments
|
let then = [fst, snd]
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rfold(then, |then, (index, arg)| {
|
.rfold(then, |then, (field_index, arg)| {
|
||||||
let label = arg.label.clone().unwrap_or_default();
|
let field_name = match arg.as_ref() {
|
||||||
|
|
||||||
let field_index = if let Some(field_map) = &field_map {
|
|
||||||
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
|
|
||||||
} else {
|
|
||||||
index
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_name = match &arg.value {
|
|
||||||
Pattern::Var { name, .. } => name.to_string(),
|
Pattern::Var { name, .. } => name.to_string(),
|
||||||
Pattern::Assign { name, .. } => name.to_string(),
|
Pattern::Assign { name, .. } => name.to_string(),
|
||||||
Pattern::Discard { name, .. } => {
|
Pattern::Discard { name, .. } => {
|
||||||
if props.full_check {
|
if props.full_check {
|
||||||
format!("__discard_{}_{}", name, index)
|
format!("__discard_{}_{}", name, field_index)
|
||||||
} else {
|
} else {
|
||||||
"_".to_string()
|
"_".to_string()
|
||||||
}
|
}
|
||||||
|
@ -1189,15 +1178,15 @@ impl<'a> CodeGenerator<'a> {
|
||||||
_ => format!(
|
_ => format!(
|
||||||
"field_{}_span_{}_{}",
|
"field_{}_span_{}_{}",
|
||||||
field_index,
|
field_index,
|
||||||
arg.value.location().start,
|
arg.location().start,
|
||||||
arg.value.location().end
|
arg.location().end
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
|
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
|
||||||
unreachable!(
|
unreachable!(
|
||||||
"Missing type for field {} of constr {}",
|
"Missing type for field {} of constr {}",
|
||||||
field_index, name
|
field_index, field_name
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1205,7 +1194,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let then = if field_name != "_" {
|
let then = if field_name != "_" {
|
||||||
self.assignment(
|
self.assignment(
|
||||||
&arg.value,
|
arg,
|
||||||
val,
|
val,
|
||||||
then,
|
then,
|
||||||
arg_type,
|
arg_type,
|
||||||
|
@ -1232,7 +1221,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
// local var
|
// local var
|
||||||
let constructor_name = format!(
|
let constructor_name = format!(
|
||||||
"__constructor_{}_span_{}_{}",
|
"__constructor_{}_span_{}_{}",
|
||||||
name,
|
"Pair",
|
||||||
pattern.location().start,
|
pattern.location().start,
|
||||||
pattern.location().end
|
pattern.location().end
|
||||||
);
|
);
|
||||||
|
@ -1421,6 +1410,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
AirTree::let_assignment(constructor_name, value, then)
|
AirTree::let_assignment(constructor_name, value, then)
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Tuple {
|
Pattern::Tuple {
|
||||||
elems, location, ..
|
elems, location, ..
|
||||||
} => {
|
} => {
|
||||||
|
@ -2529,75 +2519,40 @@ impl<'a> CodeGenerator<'a> {
|
||||||
(AirTree::void(), list_assign)
|
(AirTree::void(), list_assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Constructor {
|
Pattern::Pair { fst, snd, .. } => {
|
||||||
name,
|
let items_type = subject_tipo.get_inner_types();
|
||||||
arguments,
|
|
||||||
constructor,
|
|
||||||
tipo: function_tipo,
|
|
||||||
..
|
|
||||||
} if subject_tipo.is_pair() => {
|
|
||||||
assert!(
|
|
||||||
matches!(function_tipo.as_ref().clone(), Type::Fn { .. })
|
|
||||||
|| matches!(function_tipo.as_ref().clone(), Type::App { .. })
|
|
||||||
);
|
|
||||||
|
|
||||||
let field_map = match constructor {
|
let mut name_index_assigns = vec![];
|
||||||
PatternConstructor::Record { field_map, .. } => field_map.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
|
|
||||||
|
|
||||||
for (index, arg) in function_tipo.arg_types().unwrap().iter().enumerate() {
|
|
||||||
let field_type = arg.clone();
|
|
||||||
type_map.insert(index, field_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut fields = vec![];
|
|
||||||
|
|
||||||
let next_then =
|
let next_then =
|
||||||
arguments
|
[fst, snd]
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rfold(then, |inner_then, (index, arg)| {
|
.rfold(then, |inner_then, (index, element)| {
|
||||||
let label = arg.label.clone().unwrap_or_default();
|
let elem_name = match element.as_ref() {
|
||||||
|
|
||||||
let field_index = if let Some(field_map) = &field_map {
|
|
||||||
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
|
|
||||||
} else {
|
|
||||||
index
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_name = match &arg.value {
|
|
||||||
Pattern::Var { name, .. } => Some(name.to_string()),
|
Pattern::Var { name, .. } => Some(name.to_string()),
|
||||||
Pattern::Assign { name, .. } => Some(name.to_string()),
|
Pattern::Assign { name, .. } => Some(name.to_string()),
|
||||||
Pattern::Discard { .. } => None,
|
Pattern::Discard { .. } => None,
|
||||||
_ => Some(format!(
|
_ => Some(format!(
|
||||||
"field_{}_span_{}_{}",
|
"pair_index_{}_span_{}_{}",
|
||||||
field_index,
|
index,
|
||||||
arg.value.location().start,
|
element.location().start,
|
||||||
arg.value.location().end
|
element.location().end
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
|
let mut pair_props = ClauseProperties::init_inner(
|
||||||
unreachable!(
|
&items_type[index],
|
||||||
"Missing type for field {} of constr {}",
|
elem_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||||
field_index, name
|
elem_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut field_props = ClauseProperties::init_inner(
|
|
||||||
arg_type,
|
|
||||||
field_name.clone().unwrap_or_else(|| "_".to_string()),
|
|
||||||
field_name.clone().unwrap_or_else(|| "_".to_string()),
|
|
||||||
props.final_clause,
|
props.final_clause,
|
||||||
);
|
);
|
||||||
|
|
||||||
let statement = if field_name.is_some() {
|
let elem = if elem_name.is_some() {
|
||||||
self.nested_clause_condition(
|
self.nested_clause_condition(
|
||||||
&arg.value,
|
element,
|
||||||
arg_type,
|
&items_type[index],
|
||||||
&mut field_props,
|
&mut pair_props,
|
||||||
inner_then,
|
inner_then,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2605,21 +2560,21 @@ impl<'a> CodeGenerator<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
props.complex_clause =
|
props.complex_clause =
|
||||||
props.complex_clause || field_props.complex_clause;
|
props.complex_clause || pair_props.complex_clause;
|
||||||
|
|
||||||
fields.push((field_name, arg_type.clone()));
|
name_index_assigns.push((elem_name, index));
|
||||||
|
|
||||||
statement
|
elem
|
||||||
});
|
});
|
||||||
|
|
||||||
fields.reverse();
|
name_index_assigns.reverse();
|
||||||
|
|
||||||
let field_assign = if fields.iter().all(|s| s.0.is_none()) {
|
let field_assign = if name_index_assigns.iter().all(|s| s.0.is_none()) {
|
||||||
next_then
|
next_then
|
||||||
} else {
|
} else {
|
||||||
AirTree::pair_access(
|
AirTree::pair_access(
|
||||||
fields[0].0.clone(),
|
name_index_assigns[0].0.clone(),
|
||||||
fields[1].0.clone(),
|
name_index_assigns[1].0.clone(),
|
||||||
subject_tipo.clone(),
|
subject_tipo.clone(),
|
||||||
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
|
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
|
||||||
None,
|
None,
|
||||||
|
@ -3020,9 +2975,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern::Constructor { .. } if subject_tipo.is_pair() => {
|
Pattern::Pair { .. } => {
|
||||||
let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then);
|
let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then);
|
||||||
|
|
||||||
assign
|
assign
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -679,24 +679,19 @@ pub fn pattern_has_conditions(
|
||||||
Pattern::Tuple { elems, .. } => elems
|
Pattern::Tuple { elems, .. } => elems
|
||||||
.iter()
|
.iter()
|
||||||
.any(|elem| pattern_has_conditions(elem, data_types)),
|
.any(|elem| pattern_has_conditions(elem, data_types)),
|
||||||
|
Pattern::Pair { fst, snd, .. } => {
|
||||||
|
pattern_has_conditions(fst, data_types) || pattern_has_conditions(snd, data_types)
|
||||||
|
}
|
||||||
Pattern::Constructor {
|
Pattern::Constructor {
|
||||||
arguments, tipo, ..
|
arguments, tipo, ..
|
||||||
} => {
|
} => {
|
||||||
if tipo.is_pair()
|
let data_type = lookup_data_type_by_tipo(data_types, tipo)
|
||||||
|| (tipo.is_function() && tipo.return_type().map(|t| t.is_pair()).unwrap_or(false))
|
.unwrap_or_else(|| panic!("Data type not found: {:#?}", tipo));
|
||||||
{
|
|
||||||
arguments
|
data_type.constructors.len() > 1
|
||||||
|
|| arguments
|
||||||
.iter()
|
.iter()
|
||||||
.any(|arg| pattern_has_conditions(&arg.value, data_types))
|
.any(|arg| pattern_has_conditions(&arg.value, data_types))
|
||||||
} else {
|
|
||||||
let data_type = lookup_data_type_by_tipo(data_types, tipo)
|
|
||||||
.unwrap_or_else(|| panic!("Data type not found: {:#?}", tipo));
|
|
||||||
|
|
||||||
data_type.constructors.len() > 1
|
|
||||||
|| arguments
|
|
||||||
.iter()
|
|
||||||
.any(|arg| pattern_has_conditions(&arg.value, data_types))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types),
|
Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types),
|
||||||
Pattern::Var { .. } | Pattern::Discard { .. } => false,
|
Pattern::Var { .. } | Pattern::Discard { .. } => false,
|
||||||
|
|
|
@ -433,6 +433,10 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Self::App { args, ..} looks fishy, because App's args are referring
|
||||||
|
// to _type parameters_ not to value types unlike Fn's args. So this function
|
||||||
|
// definition is probably wrong. Luckily, we likely never hit the `Self::App`
|
||||||
|
// case at all.
|
||||||
pub fn arg_types(&self) -> Option<Vec<Rc<Self>>> {
|
pub fn arg_types(&self) -> Option<Vec<Rc<Self>>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Fn { args, .. } => Some(args.clone()),
|
Self::Fn { args, .. } => Some(args.clone()),
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::{collections::BTreeMap, iter, ops::Deref};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast,
|
||||||
builtins::{self, PAIR},
|
builtins::{self},
|
||||||
tipo::{self, environment::Environment, error::Error},
|
tipo::{self, environment::Environment, error::Error},
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::{collections::BTreeMap, iter, ops::Deref};
|
||||||
|
|
||||||
const NIL_NAME: &str = "[]";
|
const NIL_NAME: &str = "[]";
|
||||||
const CONS_NAME: &str = "::";
|
const CONS_NAME: &str = "::";
|
||||||
|
@ -88,8 +86,8 @@ impl PatternStack {
|
||||||
Some(self.chain_tail_into_iter(vec![Pattern::Wildcard; arity].into_iter()))
|
Some(self.chain_tail_into_iter(vec![Pattern::Wildcard; arity].into_iter()))
|
||||||
}
|
}
|
||||||
Pattern::Literal(_) => unreachable!(
|
Pattern::Literal(_) => unreachable!(
|
||||||
"constructors and literals should never align in pattern match exhaustiveness checks."
|
"constructors and literals should never align in pattern match exhaustiveness checks."
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,8 +562,6 @@ pub(super) fn simplify(
|
||||||
constructor: super::PatternConstructor::Record { name, .. },
|
constructor: super::PatternConstructor::Record { name, .. },
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (empty, pair) = (&"".to_string(), &PAIR.to_string());
|
|
||||||
|
|
||||||
let (module, type_name, arity) = match tipo.deref() {
|
let (module, type_name, arity) = match tipo.deref() {
|
||||||
tipo::Type::App {
|
tipo::Type::App {
|
||||||
name: type_name,
|
name: type_name,
|
||||||
|
@ -578,13 +574,11 @@ pub(super) fn simplify(
|
||||||
module,
|
module,
|
||||||
..
|
..
|
||||||
} => (module, type_name, args.len()),
|
} => (module, type_name, args.len()),
|
||||||
tipo::Type::Pair { .. } => (empty, pair, 2),
|
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("ret should be a Type::App or Type::Pair")
|
unreachable!("ret should be a Type::App")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tipo::Type::Pair { .. } => (empty, pair, 2),
|
_ => unreachable!("tipo should be a Type::App"),
|
||||||
_ => unreachable!("tipo should be a Type::App or Type::Pair"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let alts = environment.get_constructors_for_type(module, type_name, *location)?;
|
let alts = environment.get_constructors_for_type(module, type_name, *location)?;
|
||||||
|
@ -603,6 +597,13 @@ pub(super) fn simplify(
|
||||||
|
|
||||||
Ok(Pattern::Constructor(name.to_string(), alts, args))
|
Ok(Pattern::Constructor(name.to_string(), alts, args))
|
||||||
}
|
}
|
||||||
|
ast::Pattern::Pair { fst, snd, location } => simplify(
|
||||||
|
environment,
|
||||||
|
&ast::Pattern::Tuple {
|
||||||
|
elems: vec![*fst.clone(), *snd.clone()],
|
||||||
|
location: *location,
|
||||||
|
},
|
||||||
|
),
|
||||||
ast::Pattern::Tuple { elems, .. } => {
|
ast::Pattern::Tuple { elems, .. } => {
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern},
|
ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern},
|
||||||
builtins::{int, list, tuple},
|
builtins::{int, list, pair, tuple},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -236,6 +236,46 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Pattern::Pair { fst, snd, location } => match collapse_links(tipo.clone()).deref() {
|
||||||
|
Type::Pair {
|
||||||
|
fst: t_fst,
|
||||||
|
snd: t_snd,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let fst = Box::new(self.unify(*fst, t_fst.clone(), None, false)?);
|
||||||
|
let snd = Box::new(self.unify(*snd, t_snd.clone(), None, false)?);
|
||||||
|
Ok(Pattern::Pair { fst, snd, location })
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::Var { .. } => {
|
||||||
|
let t_fst = self.environment.new_unbound_var();
|
||||||
|
let t_snd = self.environment.new_unbound_var();
|
||||||
|
|
||||||
|
self.environment.unify(
|
||||||
|
pair(t_fst.clone(), t_snd.clone()),
|
||||||
|
tipo,
|
||||||
|
location,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let fst = Box::new(self.unify(*fst, t_fst, None, false)?);
|
||||||
|
let snd = Box::new(self.unify(*snd, t_snd, None, false)?);
|
||||||
|
|
||||||
|
Ok(Pattern::Pair { fst, snd, location })
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(Error::CouldNotUnify {
|
||||||
|
given: pair(
|
||||||
|
self.environment.new_unbound_var(),
|
||||||
|
self.environment.new_unbound_var(),
|
||||||
|
),
|
||||||
|
expected: tipo,
|
||||||
|
situation: None,
|
||||||
|
location,
|
||||||
|
rigid_type_names: HashMap::new(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() {
|
Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() {
|
||||||
Type::Tuple {
|
Type::Tuple {
|
||||||
elems: type_elems, ..
|
elems: type_elems, ..
|
||||||
|
|
Loading…
Reference in New Issue