mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge pull request #4940 from joshuawarner32/tuple-solve
Initial implementation of tuples in type checking
This commit is contained in:
commit
a7c415dc35
32 changed files with 1812 additions and 112 deletions
|
@ -90,7 +90,7 @@ impl AbilityMemberData<Resolved> {
|
|||
pub type SpecializationLambdaSets = VecMap<u8, Variable>;
|
||||
|
||||
/// A particular specialization of an ability member.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MemberSpecializationInfo<Phase: ResolvePhase> {
|
||||
_phase: std::marker::PhantomData<Phase>,
|
||||
pub symbol: Symbol,
|
||||
|
|
|
@ -482,7 +482,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
|
|||
// if-condition
|
||||
no_region(
|
||||
// arg_2.b
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var,
|
||||
ext_var: var_store.fresh(),
|
||||
field: "b".into(),
|
||||
|
@ -505,7 +505,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
|
|||
"Ok",
|
||||
vec![
|
||||
// arg_2.a
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var,
|
||||
ext_var: var_store.fresh(),
|
||||
field: "a".into(),
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::{
|
||||
def::Def,
|
||||
expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData, WhenBranchPattern},
|
||||
expr::{
|
||||
ClosureData, Expr, Field, OpaqueWrapFunctionData, RecordAccessorData, TupleAccessorData,
|
||||
WhenBranchPattern,
|
||||
},
|
||||
pattern::{DestructType, ListPatterns, Pattern, RecordDestruct},
|
||||
};
|
||||
use roc_module::{
|
||||
|
@ -10,7 +13,7 @@ use roc_module::{
|
|||
use roc_types::{
|
||||
subs::{
|
||||
self, AliasVariables, Descriptor, GetSubsSlice, OptVariable, RecordFields, Subs, SubsIndex,
|
||||
SubsSlice, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
SubsSlice, TupleElems, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
},
|
||||
types::{RecordField, Uls},
|
||||
};
|
||||
|
@ -62,6 +65,11 @@ trait CopyEnv {
|
|||
|
||||
fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase>;
|
||||
|
||||
fn clone_tuple_elem_indices(
|
||||
&mut self,
|
||||
tuple_elem_indices: SubsSlice<usize>,
|
||||
) -> SubsSlice<usize>;
|
||||
|
||||
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName>;
|
||||
|
||||
fn clone_lambda_names(&mut self, lambda_names: SubsSlice<Symbol>) -> SubsSlice<Symbol>;
|
||||
|
@ -98,6 +106,14 @@ impl CopyEnv for Subs {
|
|||
field_names
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clone_tuple_elem_indices(
|
||||
&mut self,
|
||||
tuple_elem_indices: SubsSlice<usize>,
|
||||
) -> SubsSlice<usize> {
|
||||
tuple_elem_indices
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName> {
|
||||
tag_names
|
||||
|
@ -151,6 +167,20 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clone_tuple_elem_indices(
|
||||
&mut self,
|
||||
tuple_elem_indices: SubsSlice<usize>,
|
||||
) -> SubsSlice<usize> {
|
||||
SubsSlice::extend_new(
|
||||
&mut self.target.tuple_elem_indices,
|
||||
self.source
|
||||
.get_subs_slice(tuple_elem_indices)
|
||||
.iter()
|
||||
.cloned(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName> {
|
||||
SubsSlice::extend_new(
|
||||
|
@ -461,13 +491,21 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
|
||||
EmptyRecord => EmptyRecord,
|
||||
|
||||
Access {
|
||||
Tuple { tuple_var, elems } => Tuple {
|
||||
tuple_var: sub!(*tuple_var),
|
||||
elems: elems
|
||||
.iter()
|
||||
.map(|(var, loc_expr)| (sub!(*var), Box::new(loc_expr.map(|e| go_help!(e)))))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
RecordAccess {
|
||||
record_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
loc_expr,
|
||||
field,
|
||||
} => Access {
|
||||
} => RecordAccess {
|
||||
record_var: sub!(*record_var),
|
||||
ext_var: sub!(*ext_var),
|
||||
field_var: sub!(*field_var),
|
||||
|
@ -475,7 +513,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
field: field.clone(),
|
||||
},
|
||||
|
||||
Accessor(AccessorData {
|
||||
RecordAccessor(RecordAccessorData {
|
||||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
|
@ -483,7 +521,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
ext_var,
|
||||
field_var,
|
||||
field,
|
||||
}) => Accessor(AccessorData {
|
||||
}) => RecordAccessor(RecordAccessorData {
|
||||
name: *name,
|
||||
function_var: sub!(*function_var),
|
||||
record_var: sub!(*record_var),
|
||||
|
@ -493,12 +531,44 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
field: field.clone(),
|
||||
}),
|
||||
|
||||
Update {
|
||||
TupleAccess {
|
||||
tuple_var,
|
||||
ext_var,
|
||||
elem_var,
|
||||
loc_expr,
|
||||
index,
|
||||
} => TupleAccess {
|
||||
tuple_var: sub!(*tuple_var),
|
||||
ext_var: sub!(*ext_var),
|
||||
elem_var: sub!(*elem_var),
|
||||
loc_expr: Box::new(loc_expr.map(|e| go_help!(e))),
|
||||
index: *index,
|
||||
},
|
||||
|
||||
TupleAccessor(TupleAccessorData {
|
||||
name,
|
||||
function_var,
|
||||
tuple_var: record_var,
|
||||
closure_var,
|
||||
ext_var,
|
||||
elem_var: field_var,
|
||||
index,
|
||||
}) => TupleAccessor(TupleAccessorData {
|
||||
name: *name,
|
||||
function_var: sub!(*function_var),
|
||||
tuple_var: sub!(*record_var),
|
||||
closure_var: sub!(*closure_var),
|
||||
ext_var: sub!(*ext_var),
|
||||
elem_var: sub!(*field_var),
|
||||
index: *index,
|
||||
}),
|
||||
|
||||
RecordUpdate {
|
||||
record_var,
|
||||
ext_var,
|
||||
symbol,
|
||||
updates,
|
||||
} => Update {
|
||||
} => RecordUpdate {
|
||||
record_var: sub!(*record_var),
|
||||
ext_var: sub!(*ext_var),
|
||||
symbol: *symbol,
|
||||
|
@ -861,7 +931,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
|||
|
||||
// Everything else is a mechanical descent.
|
||||
Structure(flat_type) => match flat_type {
|
||||
EmptyRecord | EmptyTagUnion => Structure(flat_type),
|
||||
EmptyRecord | EmptyTuple | EmptyTagUnion => Structure(flat_type),
|
||||
Apply(symbol, arguments) => {
|
||||
descend_slice!(arguments);
|
||||
|
||||
|
@ -903,6 +973,26 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
|||
Structure(Record(new_fields, new_ext_var))
|
||||
})
|
||||
}
|
||||
Tuple(elems, ext_var) => {
|
||||
let new_ext_var = descend_var!(ext_var);
|
||||
|
||||
descend_slice!(elems.variables());
|
||||
|
||||
perform_clone!({
|
||||
let new_variables = clone_var_slice!(elems.variables());
|
||||
let new_elem_indices = env.clone_tuple_elem_indices(elems.elem_indices());
|
||||
|
||||
let new_elems = {
|
||||
TupleElems {
|
||||
length: elems.length,
|
||||
variables_start: new_variables.start,
|
||||
elem_index_start: new_elem_indices.start,
|
||||
}
|
||||
};
|
||||
|
||||
Structure(Tuple(new_elems, new_ext_var))
|
||||
})
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
let new_ext_var = ext_var.map(|v| descend_var!(v));
|
||||
|
||||
|
|
|
@ -306,17 +306,37 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
.append(f.line())
|
||||
.append(f.text("}"))
|
||||
.group(),
|
||||
Tuple { elems, .. } => f
|
||||
.reflow("(")
|
||||
.append(
|
||||
f.intersperse(
|
||||
elems.iter().map(|(_var, elem)| {
|
||||
f.line()
|
||||
.append(expr(c, Free, f, &elem.value))
|
||||
.nest(2)
|
||||
.group()
|
||||
}),
|
||||
f.reflow(","),
|
||||
)
|
||||
.nest(2)
|
||||
.group(),
|
||||
)
|
||||
.append(f.line())
|
||||
.append(f.text(")"))
|
||||
.group(),
|
||||
EmptyRecord => f.text("{}"),
|
||||
Access {
|
||||
RecordAccess {
|
||||
loc_expr, field, ..
|
||||
} => expr(c, AppArg, f, &loc_expr.value)
|
||||
.append(f.text(format!(".{}", field.as_str())))
|
||||
.group(),
|
||||
TupleAccess { .. } => todo!(),
|
||||
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
|
||||
f.text(format!("@{}", opaque_name.as_str(c.interns)))
|
||||
}
|
||||
Accessor(_) => todo!(),
|
||||
Update {
|
||||
RecordAccessor(_) => todo!(),
|
||||
TupleAccessor(_) => todo!(),
|
||||
RecordUpdate {
|
||||
symbol, updates, ..
|
||||
} => f
|
||||
.reflow("{")
|
||||
|
|
|
@ -182,6 +182,14 @@ fn index_var(
|
|||
|
||||
return Ok(field_types);
|
||||
}
|
||||
FlatType::Tuple(elems, ext) => {
|
||||
let elem_types = elems
|
||||
.sorted_iterator(subs, *ext)
|
||||
.map(|(_, elem)| elem)
|
||||
.collect();
|
||||
|
||||
return Ok(elem_types);
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => {
|
||||
let tag_ctor = match ctor {
|
||||
IndexCtor::Tag(name) => name,
|
||||
|
@ -213,6 +221,9 @@ fn index_var(
|
|||
};
|
||||
return Ok(std::iter::repeat(Variable::NULL).take(num_fields).collect());
|
||||
}
|
||||
FlatType::EmptyTuple => {
|
||||
return Ok(std::iter::repeat(Variable::NULL).take(0).collect());
|
||||
}
|
||||
FlatType::EmptyTagUnion => {
|
||||
internal_error!("empty tag unions are not indexable")
|
||||
}
|
||||
|
|
|
@ -166,6 +166,11 @@ pub enum Expr {
|
|||
/// Empty record constant
|
||||
EmptyRecord,
|
||||
|
||||
Tuple {
|
||||
tuple_var: Variable,
|
||||
elems: Vec<(Variable, Box<Loc<Expr>>)>,
|
||||
},
|
||||
|
||||
/// The "crash" keyword
|
||||
Crash {
|
||||
msg: Box<Loc<Expr>>,
|
||||
|
@ -173,17 +178,29 @@ pub enum Expr {
|
|||
},
|
||||
|
||||
/// Look up exactly one field on a record, e.g. (expr).foo.
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var: Variable,
|
||||
ext_var: Variable,
|
||||
field_var: Variable,
|
||||
loc_expr: Box<Loc<Expr>>,
|
||||
field: Lowercase,
|
||||
},
|
||||
/// field accessor as a function, e.g. (.foo) expr
|
||||
Accessor(AccessorData),
|
||||
|
||||
Update {
|
||||
/// field accessor as a function, e.g. (.foo) expr
|
||||
RecordAccessor(RecordAccessorData),
|
||||
|
||||
TupleAccess {
|
||||
tuple_var: Variable,
|
||||
ext_var: Variable,
|
||||
elem_var: Variable,
|
||||
loc_expr: Box<Loc<Expr>>,
|
||||
index: usize,
|
||||
},
|
||||
|
||||
/// tuple accessor as a function, e.g. (.1) expr
|
||||
TupleAccessor(TupleAccessorData),
|
||||
|
||||
RecordUpdate {
|
||||
record_var: Variable,
|
||||
ext_var: Variable,
|
||||
symbol: Symbol,
|
||||
|
@ -294,11 +311,14 @@ impl Expr {
|
|||
&Self::RunLowLevel { op, .. } => Category::LowLevelOpResult(op),
|
||||
Self::ForeignCall { .. } => Category::ForeignCall,
|
||||
Self::Closure(..) => Category::Lambda,
|
||||
Self::Tuple { .. } => Category::Tuple,
|
||||
Self::Record { .. } => Category::Record,
|
||||
Self::EmptyRecord => Category::Record,
|
||||
Self::Access { field, .. } => Category::Access(field.clone()),
|
||||
Self::Accessor(data) => Category::Accessor(data.field.clone()),
|
||||
Self::Update { .. } => Category::Record,
|
||||
Self::RecordAccess { field, .. } => Category::RecordAccess(field.clone()),
|
||||
Self::RecordAccessor(data) => Category::RecordAccessor(data.field.clone()),
|
||||
Self::TupleAccess { index, .. } => Category::TupleAccess(*index),
|
||||
Self::TupleAccessor(data) => Category::TupleAccessor(data.index),
|
||||
Self::RecordUpdate { .. } => Category::Record,
|
||||
Self::Tag {
|
||||
name, arguments, ..
|
||||
} => Category::TagApply {
|
||||
|
@ -363,14 +383,31 @@ pub struct ClosureData {
|
|||
pub loc_body: Box<Loc<Expr>>,
|
||||
}
|
||||
|
||||
/// A record accessor like `.foo`, which is equivalent to `\r -> r.foo`
|
||||
/// Accessors are desugared to closures; they need to have a name
|
||||
/// A tuple accessor like `.2`, which is equivalent to `\x -> x.2`
|
||||
/// TupleAccessors are desugared to closures; they need to have a name
|
||||
/// so the closure can have a correct lambda set.
|
||||
///
|
||||
/// We distinguish them from closures so we can have better error messages
|
||||
/// during constraint generation.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AccessorData {
|
||||
pub struct TupleAccessorData {
|
||||
pub name: Symbol,
|
||||
pub function_var: Variable,
|
||||
pub tuple_var: Variable,
|
||||
pub closure_var: Variable,
|
||||
pub ext_var: Variable,
|
||||
pub elem_var: Variable,
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// A record accessor like `.foo`, which is equivalent to `\r -> r.foo`
|
||||
/// RecordAccessors are desugared to closures; they need to have a name
|
||||
/// so the closure can have a correct lambda set.
|
||||
///
|
||||
/// We distinguish them from closures so we can have better error messages
|
||||
/// during constraint generation.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RecordAccessorData {
|
||||
pub name: Symbol,
|
||||
pub function_var: Variable,
|
||||
pub record_var: Variable,
|
||||
|
@ -380,9 +417,9 @@ pub struct AccessorData {
|
|||
pub field: Lowercase,
|
||||
}
|
||||
|
||||
impl AccessorData {
|
||||
impl RecordAccessorData {
|
||||
pub fn to_closure_data(self, record_symbol: Symbol) -> ClosureData {
|
||||
let AccessorData {
|
||||
let RecordAccessorData {
|
||||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
|
@ -399,7 +436,7 @@ impl AccessorData {
|
|||
// into
|
||||
//
|
||||
// (\r -> r.foo)
|
||||
let body = Expr::Access {
|
||||
let body = Expr::RecordAccess {
|
||||
record_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
|
@ -620,9 +657,7 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::Tuple(_fields) => {
|
||||
todo!("canonicalize tuple");
|
||||
}
|
||||
|
||||
ast::Expr::RecordUpdate {
|
||||
fields,
|
||||
update: loc_update,
|
||||
|
@ -634,7 +669,7 @@ pub fn canonicalize_expr<'a>(
|
|||
Ok((can_fields, mut output)) => {
|
||||
output.references.union_mut(&update_out.references);
|
||||
|
||||
let answer = Update {
|
||||
let answer = RecordUpdate {
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
symbol: *symbol,
|
||||
|
@ -670,6 +705,35 @@ pub fn canonicalize_expr<'a>(
|
|||
(answer, Output::default())
|
||||
}
|
||||
}
|
||||
|
||||
ast::Expr::Tuple(fields) => {
|
||||
let mut can_elems = Vec::with_capacity(fields.len());
|
||||
let mut references = References::new();
|
||||
|
||||
for loc_elem in fields.iter() {
|
||||
let (can_expr, elem_out) =
|
||||
canonicalize_expr(env, var_store, scope, loc_elem.region, &loc_elem.value);
|
||||
|
||||
references.union_mut(&elem_out.references);
|
||||
|
||||
can_elems.push((var_store.fresh(), Box::new(can_expr)));
|
||||
}
|
||||
|
||||
let output = Output {
|
||||
references,
|
||||
tail_call: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
(
|
||||
Tuple {
|
||||
tuple_var: var_store.fresh(),
|
||||
elems: can_elems,
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
|
||||
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
|
||||
|
||||
ast::Expr::SingleQuote(string) => {
|
||||
|
@ -1006,7 +1070,7 @@ pub fn canonicalize_expr<'a>(
|
|||
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, record_expr);
|
||||
|
||||
(
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
|
@ -1017,7 +1081,7 @@ pub fn canonicalize_expr<'a>(
|
|||
)
|
||||
}
|
||||
ast::Expr::RecordAccessorFunction(field) => (
|
||||
Accessor(AccessorData {
|
||||
RecordAccessor(RecordAccessorData {
|
||||
name: scope.gen_unique_symbol(),
|
||||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
|
@ -1028,8 +1092,32 @@ pub fn canonicalize_expr<'a>(
|
|||
}),
|
||||
Output::default(),
|
||||
),
|
||||
ast::Expr::TupleAccess(_record_expr, _field) => todo!("handle TupleAccess"),
|
||||
ast::Expr::TupleAccessorFunction(_) => todo!("handle TupleAccessorFunction"),
|
||||
ast::Expr::TupleAccess(tuple_expr, field) => {
|
||||
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, tuple_expr);
|
||||
|
||||
(
|
||||
TupleAccess {
|
||||
tuple_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
elem_var: var_store.fresh(),
|
||||
loc_expr: Box::new(loc_expr),
|
||||
index: field.parse().unwrap(),
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::TupleAccessorFunction(index) => (
|
||||
TupleAccessor(TupleAccessorData {
|
||||
name: scope.gen_unique_symbol(),
|
||||
function_var: var_store.fresh(),
|
||||
tuple_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
elem_var: var_store.fresh(),
|
||||
index: index.parse().unwrap(),
|
||||
}),
|
||||
Output::default(),
|
||||
),
|
||||
ast::Expr::Tag(tag) => {
|
||||
let variant_var = var_store.fresh();
|
||||
let ext_var = var_store.fresh();
|
||||
|
@ -1785,8 +1873,9 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
| other @ SingleQuote(..)
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
| other @ Accessor { .. }
|
||||
| other @ Update { .. }
|
||||
| other @ RecordAccessor { .. }
|
||||
| other @ TupleAccessor { .. }
|
||||
| other @ RecordUpdate { .. }
|
||||
| other @ Var(..)
|
||||
| other @ AbilityMember(..)
|
||||
| other @ RunLowLevel { .. }
|
||||
|
@ -2047,14 +2136,32 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
);
|
||||
}
|
||||
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
loc_expr,
|
||||
field,
|
||||
} => {
|
||||
todo!("Inlining for Access with record_var {:?}, ext_var {:?}, field_var {:?}, loc_expr {:?}, field {:?}", record_var, ext_var, field_var, loc_expr, field);
|
||||
todo!("Inlining for RecordAccess with record_var {:?}, ext_var {:?}, field_var {:?}, loc_expr {:?}, field {:?}", record_var, ext_var, field_var, loc_expr, field);
|
||||
}
|
||||
|
||||
Tuple { tuple_var, elems } => {
|
||||
todo!(
|
||||
"Inlining for Tuple with tuple_var {:?} and elems {:?}",
|
||||
tuple_var,
|
||||
elems
|
||||
);
|
||||
}
|
||||
|
||||
TupleAccess {
|
||||
tuple_var,
|
||||
ext_var,
|
||||
elem_var,
|
||||
loc_expr,
|
||||
index,
|
||||
} => {
|
||||
todo!("Inlining for TupleAccess with tuple_var {:?}, ext_var {:?}, elem_var {:?}, loc_expr {:?}, index {:?}", tuple_var, ext_var, elem_var, loc_expr, index);
|
||||
}
|
||||
|
||||
Tag {
|
||||
|
@ -2772,7 +2879,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
while let Some(expr) = stack.pop() {
|
||||
match expr {
|
||||
Expr::Var(symbol, var)
|
||||
| Expr::Update {
|
||||
| Expr::RecordUpdate {
|
||||
symbol,
|
||||
record_var: var,
|
||||
..
|
||||
|
@ -2863,7 +2970,8 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
Expr::OpaqueRef { argument, .. } => {
|
||||
stack.push(&argument.1.value);
|
||||
}
|
||||
Expr::Access { loc_expr, .. }
|
||||
Expr::RecordAccess { loc_expr, .. }
|
||||
| Expr::TupleAccess { loc_expr, .. }
|
||||
| Expr::Closure(ClosureData {
|
||||
loc_body: loc_expr, ..
|
||||
}) => {
|
||||
|
@ -2872,6 +2980,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
Expr::Record { fields, .. } => {
|
||||
stack.extend(fields.iter().map(|(_, field)| &field.loc_expr.value));
|
||||
}
|
||||
Expr::Tuple { elems, .. } => {
|
||||
stack.extend(elems.iter().map(|(_, elem)| &elem.value));
|
||||
}
|
||||
Expr::Expect {
|
||||
loc_continuation, ..
|
||||
}
|
||||
|
@ -2892,7 +3003,8 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
| Expr::Int(_, _, _, _, _)
|
||||
| Expr::Str(_)
|
||||
| Expr::ZeroArgumentTag { .. }
|
||||
| Expr::Accessor(_)
|
||||
| Expr::RecordAccessor(_)
|
||||
| Expr::TupleAccessor(_)
|
||||
| Expr::SingleQuote(..)
|
||||
| Expr::EmptyRecord
|
||||
| Expr::TypedHole(_)
|
||||
|
|
|
@ -1087,7 +1087,8 @@ fn fix_values_captured_in_closure_expr(
|
|||
| TypedHole { .. }
|
||||
| RuntimeError(_)
|
||||
| ZeroArgumentTag { .. }
|
||||
| Accessor { .. } => {}
|
||||
| RecordAccessor { .. }
|
||||
| TupleAccessor { .. } => {}
|
||||
|
||||
List { loc_elems, .. } => {
|
||||
for elem in loc_elems.iter_mut() {
|
||||
|
@ -1181,7 +1182,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
}
|
||||
|
||||
Record { fields, .. }
|
||||
| Update {
|
||||
| RecordUpdate {
|
||||
updates: fields, ..
|
||||
} => {
|
||||
for (_, field) in fields.iter_mut() {
|
||||
|
@ -1193,7 +1194,17 @@ fn fix_values_captured_in_closure_expr(
|
|||
}
|
||||
}
|
||||
|
||||
Access { loc_expr, .. } => {
|
||||
Tuple { elems, .. } => {
|
||||
for (_var, expr) in elems.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RecordAccess { loc_expr, .. } | TupleAccess { loc_expr, .. } => {
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
no_capture_symbols,
|
||||
|
|
|
@ -141,7 +141,16 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
| OpaqueRef(_)
|
||||
| Crash => loc_expr,
|
||||
|
||||
TupleAccess(_sub_expr, _paths) => todo!("Handle TupleAccess"),
|
||||
TupleAccess(sub_expr, paths) => {
|
||||
let region = loc_expr.region;
|
||||
let loc_sub_expr = Loc {
|
||||
region,
|
||||
value: **sub_expr,
|
||||
};
|
||||
let value = TupleAccess(&desugar_expr(arena, arena.alloc(loc_sub_expr)).value, paths);
|
||||
|
||||
arena.alloc(Loc { region, value })
|
||||
}
|
||||
RecordAccess(sub_expr, paths) => {
|
||||
let region = loc_expr.region;
|
||||
let loc_sub_expr = Loc {
|
||||
|
@ -176,9 +185,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}
|
||||
})),
|
||||
}),
|
||||
Tuple(_fields) => {
|
||||
todo!("desugar_expr: Tuple");
|
||||
}
|
||||
Tuple(fields) => arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Tuple(fields.map_items(arena, |field| desugar_expr(arena, field))),
|
||||
}),
|
||||
RecordUpdate { fields, update } => {
|
||||
// NOTE the `update` field is always a `Var { .. }`, we only desugar it to get rid of
|
||||
// any spaces before/after
|
||||
|
|
|
@ -8,8 +8,8 @@ use crate::{
|
|||
abilities::AbilitiesStore,
|
||||
def::{Annotation, Declaration, Def},
|
||||
expr::{
|
||||
self, AccessorData, AnnotatedMark, ClosureData, Declarations, Expr, Field,
|
||||
OpaqueWrapFunctionData,
|
||||
self, AnnotatedMark, ClosureData, Declarations, Expr, Field, OpaqueWrapFunctionData,
|
||||
RecordAccessorData, TupleAccessorData,
|
||||
},
|
||||
pattern::{DestructType, Pattern, RecordDestruct},
|
||||
};
|
||||
|
@ -228,17 +228,31 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
|||
} => {
|
||||
walk_record_fields(visitor, fields.iter());
|
||||
}
|
||||
Expr::Tuple {
|
||||
tuple_var: _,
|
||||
elems,
|
||||
} => elems
|
||||
.iter()
|
||||
.for_each(|(var, elem)| visitor.visit_expr(&elem.value, elem.region, *var)),
|
||||
Expr::EmptyRecord => { /* terminal */ }
|
||||
Expr::Access {
|
||||
Expr::RecordAccess {
|
||||
field_var,
|
||||
loc_expr,
|
||||
field: _,
|
||||
record_var: _,
|
||||
ext_var: _,
|
||||
} => visitor.visit_expr(&loc_expr.value, loc_expr.region, *field_var),
|
||||
Expr::Accessor(AccessorData { .. }) => { /* terminal */ }
|
||||
Expr::RecordAccessor(RecordAccessorData { .. }) => { /* terminal */ }
|
||||
Expr::TupleAccess {
|
||||
elem_var,
|
||||
loc_expr,
|
||||
index: _,
|
||||
tuple_var: _,
|
||||
ext_var: _,
|
||||
} => visitor.visit_expr(&loc_expr.value, loc_expr.region, *elem_var),
|
||||
Expr::TupleAccessor(TupleAccessorData { .. }) => { /* terminal */ }
|
||||
Expr::OpaqueWrapFunction(OpaqueWrapFunctionData { .. }) => { /* terminal */ }
|
||||
Expr::Update {
|
||||
Expr::RecordUpdate {
|
||||
record_var: _,
|
||||
ext_var: _,
|
||||
symbol: _,
|
||||
|
|
|
@ -279,6 +279,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V> Eq for VecMap<K, V>
|
||||
where
|
||||
K: Eq,
|
||||
V: Eq,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_drain_filter {
|
||||
use crate::VecMap;
|
||||
|
|
|
@ -16,8 +16,8 @@ use roc_can::expected::Expected::{self, *};
|
|||
use roc_can::expected::PExpected;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{
|
||||
AccessorData, AnnotatedMark, ClosureData, DeclarationTag, Declarations, DestructureDef,
|
||||
ExpectLookup, Field, FunctionDef, OpaqueWrapFunctionData, WhenBranch,
|
||||
AnnotatedMark, ClosureData, DeclarationTag, Declarations, DestructureDef, ExpectLookup, Field,
|
||||
FunctionDef, OpaqueWrapFunctionData, RecordAccessorData, TupleAccessorData, WhenBranch,
|
||||
};
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_can::traverse::symbols_introduced_from_pattern;
|
||||
|
@ -252,7 +252,52 @@ pub fn constrain_expr(
|
|||
constraints.exists(field_vars, and_constraint)
|
||||
}
|
||||
}
|
||||
Update {
|
||||
Expr::Tuple { tuple_var, elems } => {
|
||||
let mut elem_types = VecMap::with_capacity(elems.len());
|
||||
let mut elem_vars = Vec::with_capacity(elems.len());
|
||||
|
||||
// Constraints need capacity for each elem
|
||||
// + 1 for the tuple itself + 1 for tuple var
|
||||
let mut tuple_constraints = Vec::with_capacity(2 + elems.len());
|
||||
|
||||
for (i, (elem_var, loc_expr)) in elems.iter().enumerate() {
|
||||
let elem_type = constraints.push_variable(*elem_var);
|
||||
let elem_expected = constraints.push_expected_type(NoExpectation(elem_type));
|
||||
let elem_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
elem_expected,
|
||||
);
|
||||
|
||||
elem_vars.push(*elem_var);
|
||||
elem_types.insert(i, Variable(*elem_var));
|
||||
|
||||
tuple_constraints.push(elem_con);
|
||||
}
|
||||
|
||||
let tuple_type = {
|
||||
let typ = types.from_old_type(&Type::Tuple(elem_types, TypeExtension::Closed));
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
|
||||
let tuple_con = constraints.equal_types_with_storage(
|
||||
tuple_type,
|
||||
expected,
|
||||
Category::Tuple,
|
||||
region,
|
||||
*tuple_var,
|
||||
);
|
||||
|
||||
tuple_constraints.push(tuple_con);
|
||||
elem_vars.push(*tuple_var);
|
||||
|
||||
let and_constraint = constraints.and_constraint(tuple_constraints);
|
||||
constraints.exists(elem_vars, and_constraint)
|
||||
}
|
||||
RecordUpdate {
|
||||
record_var,
|
||||
ext_var,
|
||||
symbol,
|
||||
|
@ -1070,7 +1115,7 @@ pub fn constrain_expr(
|
|||
branch_constraints,
|
||||
)
|
||||
}
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
|
@ -1096,7 +1141,7 @@ pub fn constrain_expr(
|
|||
};
|
||||
let record_expected = constraints.push_expected_type(NoExpectation(record_type));
|
||||
|
||||
let category = Category::Access(field.clone());
|
||||
let category = Category::RecordAccess(field.clone());
|
||||
|
||||
let record_con =
|
||||
constraints.equal_types_var(*record_var, record_expected, category.clone(), region);
|
||||
|
@ -1117,7 +1162,7 @@ pub fn constrain_expr(
|
|||
[constraint, eq, record_con],
|
||||
)
|
||||
}
|
||||
Accessor(AccessorData {
|
||||
RecordAccessor(RecordAccessorData {
|
||||
name: closure_name,
|
||||
function_var,
|
||||
field,
|
||||
|
@ -1143,7 +1188,7 @@ pub fn constrain_expr(
|
|||
constraints.push_type(types, typ)
|
||||
};
|
||||
|
||||
let category = Category::Accessor(field.clone());
|
||||
let category = Category::RecordAccessor(field.clone());
|
||||
|
||||
let record_expected = constraints.push_expected_type(NoExpectation(record_type_index));
|
||||
let record_con =
|
||||
|
@ -1199,6 +1244,132 @@ pub fn constrain_expr(
|
|||
cons,
|
||||
)
|
||||
}
|
||||
TupleAccess {
|
||||
tuple_var,
|
||||
ext_var,
|
||||
elem_var,
|
||||
loc_expr,
|
||||
index,
|
||||
} => {
|
||||
let mut tup_elem_types = VecMap::with_capacity(1);
|
||||
|
||||
let ext_var = *ext_var;
|
||||
let ext_type = Variable(ext_var);
|
||||
|
||||
let elem_var = *elem_var;
|
||||
let elem_type = Type::Variable(elem_var);
|
||||
|
||||
tup_elem_types.insert(*index, elem_type);
|
||||
|
||||
let tuple_type = {
|
||||
let typ = types.from_old_type(&Type::Tuple(
|
||||
tup_elem_types,
|
||||
TypeExtension::from_non_annotation_type(ext_type),
|
||||
));
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let tuple_expected = constraints.push_expected_type(NoExpectation(tuple_type));
|
||||
|
||||
let category = Category::TupleAccess(*index);
|
||||
|
||||
let tuple_con =
|
||||
constraints.equal_types_var(*tuple_var, tuple_expected, category.clone(), region);
|
||||
|
||||
let expected_tuple = constraints.push_expected_type(NoExpectation(tuple_type));
|
||||
let constraint = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
region,
|
||||
&loc_expr.value,
|
||||
expected_tuple,
|
||||
);
|
||||
|
||||
let eq = constraints.equal_types_var(elem_var, expected, category, region);
|
||||
constraints.exists_many([*tuple_var, elem_var, ext_var], [constraint, eq, tuple_con])
|
||||
}
|
||||
TupleAccessor(TupleAccessorData {
|
||||
name: closure_name,
|
||||
function_var,
|
||||
tuple_var,
|
||||
closure_var,
|
||||
ext_var,
|
||||
elem_var,
|
||||
index,
|
||||
}) => {
|
||||
let ext_var = *ext_var;
|
||||
let ext_type = Variable(ext_var);
|
||||
let elem_var = *elem_var;
|
||||
let elem_type = Variable(elem_var);
|
||||
|
||||
let mut elem_types = VecMap::with_capacity(1);
|
||||
elem_types.insert(*index, elem_type.clone());
|
||||
|
||||
let record_type = Type::Tuple(
|
||||
elem_types,
|
||||
TypeExtension::from_non_annotation_type(ext_type),
|
||||
);
|
||||
let record_type_index = {
|
||||
let typ = types.from_old_type(&record_type);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
|
||||
let category = Category::TupleAccessor(*index);
|
||||
|
||||
let record_expected = constraints.push_expected_type(NoExpectation(record_type_index));
|
||||
let record_con =
|
||||
constraints.equal_types_var(*tuple_var, record_expected, category.clone(), region);
|
||||
|
||||
let expected_lambda_set = {
|
||||
let lambda_set_ty = {
|
||||
let typ = types.from_old_type(&Type::ClosureTag {
|
||||
name: *closure_name,
|
||||
captures: vec![],
|
||||
ambient_function: *function_var,
|
||||
});
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
constraints.push_expected_type(NoExpectation(lambda_set_ty))
|
||||
};
|
||||
|
||||
let closure_type = Type::Variable(*closure_var);
|
||||
|
||||
let function_type_index = {
|
||||
let typ = types.from_old_type(&Type::Function(
|
||||
vec![record_type],
|
||||
Box::new(closure_type),
|
||||
Box::new(elem_type),
|
||||
));
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
|
||||
let cons = [
|
||||
constraints.equal_types_var(
|
||||
*closure_var,
|
||||
expected_lambda_set,
|
||||
category.clone(),
|
||||
region,
|
||||
),
|
||||
constraints.equal_types(function_type_index, expected, category.clone(), region),
|
||||
{
|
||||
let store_fn_var_index = constraints.push_variable(*function_var);
|
||||
let store_fn_var_expected =
|
||||
constraints.push_expected_type(NoExpectation(store_fn_var_index));
|
||||
constraints.equal_types(
|
||||
function_type_index,
|
||||
store_fn_var_expected,
|
||||
category,
|
||||
region,
|
||||
)
|
||||
},
|
||||
record_con,
|
||||
];
|
||||
|
||||
constraints.exists_many(
|
||||
[*tuple_var, *function_var, *closure_var, elem_var, ext_var],
|
||||
cons,
|
||||
)
|
||||
}
|
||||
LetRec(defs, loc_ret, cycle_mark) => {
|
||||
let body_con = constrain_expr(
|
||||
types,
|
||||
|
@ -3826,8 +3997,12 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
|
|||
match expr {
|
||||
Num(..) | Int(..) | Float(..) => return true,
|
||||
Closure(_) => return true,
|
||||
Accessor(_) => {
|
||||
// Accessor functions `.field` are equivalent to closures `\r -> r.field`, no need to weaken them.
|
||||
RecordAccessor(_) => {
|
||||
// RecordAccessor functions `.field` are equivalent to closures `\r -> r.field`, no need to weaken them.
|
||||
return true;
|
||||
}
|
||||
TupleAccessor(_) => {
|
||||
// TupleAccessor functions `.0` are equivalent to closures `\r -> r.0`, no need to weaken them.
|
||||
return true;
|
||||
}
|
||||
OpaqueWrapFunction(_) => {
|
||||
|
@ -3852,9 +4027,11 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
|
|||
| ForeignCall { .. }
|
||||
| EmptyRecord
|
||||
| Expr::Record { .. }
|
||||
| Expr::Tuple { .. }
|
||||
| Crash { .. }
|
||||
| Access { .. }
|
||||
| Update { .. }
|
||||
| RecordAccess { .. }
|
||||
| TupleAccess { .. }
|
||||
| RecordUpdate { .. }
|
||||
| Expect { .. }
|
||||
| ExpectFx { .. }
|
||||
| Dbg { .. }
|
||||
|
|
|
@ -363,7 +363,7 @@ fn decoder_record_step_field(
|
|||
},
|
||||
);
|
||||
|
||||
let updated_record = Expr::Update {
|
||||
let updated_record = Expr::RecordUpdate {
|
||||
record_var: state_record_var,
|
||||
ext_var: env.new_ext_var(ExtensionKind::Record),
|
||||
symbol: state_arg_symbol,
|
||||
|
@ -429,7 +429,7 @@ fn decoder_record_step_field(
|
|||
// Ok val -> Ok {state & first: Ok val},
|
||||
// Err err -> Err err
|
||||
Expr::When {
|
||||
loc_cond: Box::new(Loc::at_zero(Expr::Access {
|
||||
loc_cond: Box::new(Loc::at_zero(Expr::RecordAccess {
|
||||
record_var: rec_var,
|
||||
ext_var: env.new_ext_var(ExtensionKind::Record),
|
||||
field_var: rec_dot_result,
|
||||
|
@ -458,7 +458,7 @@ fn decoder_record_step_field(
|
|||
Field {
|
||||
var: Variable::LIST_U8,
|
||||
region: Region::zero(),
|
||||
loc_expr: Box::new(Loc::at_zero(Expr::Access {
|
||||
loc_expr: Box::new(Loc::at_zero(Expr::RecordAccess {
|
||||
record_var: rec_var,
|
||||
ext_var: env.new_ext_var(ExtensionKind::Record),
|
||||
field_var: Variable::LIST_U8,
|
||||
|
@ -829,7 +829,7 @@ fn decoder_record_finalizer(
|
|||
.zip(result_field_vars.iter().rev())
|
||||
{
|
||||
// when rec.first is
|
||||
let cond_expr = Expr::Access {
|
||||
let cond_expr = Expr::RecordAccess {
|
||||
record_var: state_record_var,
|
||||
ext_var: env.new_ext_var(ExtensionKind::Record),
|
||||
field_var: result_field_var,
|
||||
|
|
|
@ -313,7 +313,7 @@ fn to_encoder_record(
|
|||
};
|
||||
|
||||
// rcd.a
|
||||
let field_access = Access {
|
||||
let field_access = RecordAccess {
|
||||
record_var,
|
||||
ext_var: env.subs.fresh_unnamed_flex_var(),
|
||||
field_var,
|
||||
|
|
|
@ -96,7 +96,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec<Lowercase>) -> (V
|
|||
let field_name = env.subs[field_name].clone();
|
||||
let field_var = env.subs[field_var];
|
||||
|
||||
let field_access = Expr::Access {
|
||||
let field_access = Expr::RecordAccess {
|
||||
record_var,
|
||||
field_var,
|
||||
ext_var: env.subs.fresh_unnamed_flex_var(),
|
||||
|
|
|
@ -61,6 +61,9 @@ impl FlatDecodable {
|
|||
|
||||
Ok(Key(FlatDecodableKey::Record(field_names)))
|
||||
}
|
||||
FlatType::Tuple(_elems, _ext) => {
|
||||
todo!()
|
||||
}
|
||||
FlatType::TagUnion(_tags, _ext) | FlatType::RecursiveTagUnion(_, _tags, _ext) => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
|
@ -68,6 +71,7 @@ impl FlatDecodable {
|
|||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::EmptyRecord => Ok(Key(FlatDecodableKey::Record(vec![]))),
|
||||
FlatType::EmptyTuple => todo!(),
|
||||
FlatType::EmptyTagUnion => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
|
|
|
@ -66,6 +66,9 @@ impl FlatEncodable {
|
|||
|
||||
Ok(Key(FlatEncodableKey::Record(field_names)))
|
||||
}
|
||||
FlatType::Tuple(_elems, _ext) => {
|
||||
todo!()
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => {
|
||||
// The recursion var doesn't matter, because the derived implementation will only
|
||||
// look on the surface of the tag union type, and more over the payloads of the
|
||||
|
@ -104,6 +107,7 @@ impl FlatEncodable {
|
|||
)))
|
||||
}
|
||||
FlatType::EmptyRecord => Ok(Key(FlatEncodableKey::Record(vec![]))),
|
||||
FlatType::EmptyTuple => todo!(),
|
||||
FlatType::EmptyTagUnion => Ok(Key(FlatEncodableKey::TagUnion(vec![]))),
|
||||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
|
|
|
@ -65,6 +65,9 @@ impl FlatHash {
|
|||
|
||||
Ok(Key(FlatHashKey::Record(field_names)))
|
||||
}
|
||||
FlatType::Tuple(_elems, _ext) => {
|
||||
todo!();
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => {
|
||||
// The recursion var doesn't matter, because the derived implementation will only
|
||||
// look on the surface of the tag union type, and more over the payloads of the
|
||||
|
@ -101,6 +104,7 @@ impl FlatHash {
|
|||
.collect(),
|
||||
))),
|
||||
FlatType::EmptyRecord => Ok(Key(FlatHashKey::Record(vec![]))),
|
||||
FlatType::EmptyTuple => todo!(),
|
||||
FlatType::EmptyTagUnion => Ok(Key(FlatHashKey::TagUnion(vec![]))),
|
||||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
|
|
|
@ -4330,6 +4330,8 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Tuple { .. } => todo!("implement tuple hole"),
|
||||
|
||||
Record {
|
||||
record_var,
|
||||
mut fields,
|
||||
|
@ -4719,7 +4721,7 @@ pub fn with_hole<'a>(
|
|||
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
||||
}
|
||||
|
||||
Access {
|
||||
RecordAccess {
|
||||
record_var,
|
||||
field_var,
|
||||
field,
|
||||
|
@ -4807,7 +4809,7 @@ pub fn with_hole<'a>(
|
|||
stmt
|
||||
}
|
||||
|
||||
Accessor(accessor_data) => {
|
||||
RecordAccessor(accessor_data) => {
|
||||
let field_var = accessor_data.field_var;
|
||||
let fresh_record_symbol = env.unique_symbol();
|
||||
|
||||
|
@ -4862,6 +4864,9 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
TupleAccess { .. } => todo!(),
|
||||
TupleAccessor(_) => todo!(),
|
||||
|
||||
OpaqueWrapFunction(wrap_fn_data) => {
|
||||
let opaque_var = wrap_fn_data.opaque_var;
|
||||
let arg_symbol = env.unique_symbol();
|
||||
|
@ -4919,7 +4924,7 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Update {
|
||||
RecordUpdate {
|
||||
record_var,
|
||||
symbol: structure,
|
||||
updates,
|
||||
|
|
|
@ -2078,6 +2078,13 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) {
|
|||
}
|
||||
stack.push((*ext, depth_any + 1, depth_lset));
|
||||
}
|
||||
FlatType::Tuple(elems, ext) => {
|
||||
for var_index in elems.iter_variables() {
|
||||
let var = subs[var_index];
|
||||
stack.push((var, depth_any + 1, depth_lset));
|
||||
}
|
||||
stack.push((*ext, depth_any + 1, depth_lset));
|
||||
}
|
||||
FlatType::FunctionOrTagUnion(_, _, ext) => {
|
||||
stack.push((ext.var(), depth_any + 1, depth_lset));
|
||||
}
|
||||
|
@ -2098,7 +2105,7 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) {
|
|||
}
|
||||
stack.push((ext.var(), depth_any + 1, depth_lset));
|
||||
}
|
||||
FlatType::EmptyRecord | FlatType::EmptyTagUnion => {}
|
||||
FlatType::EmptyRecord | FlatType::EmptyTuple | FlatType::EmptyTagUnion => {}
|
||||
},
|
||||
Content::FlexVar(_)
|
||||
| Content::RigidVar(_)
|
||||
|
@ -3176,6 +3183,9 @@ fn layout_from_flat_type<'a>(
|
|||
|
||||
Cacheable(result, criteria)
|
||||
}
|
||||
Tuple(_elems, _ext_var) => {
|
||||
todo!();
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
|
||||
|
||||
|
@ -3205,6 +3215,7 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
EmptyTagUnion => cacheable(Ok(Layout::VOID)),
|
||||
EmptyRecord => cacheable(Ok(Layout::UNIT)),
|
||||
EmptyTuple => cacheable(Ok(Layout::UNIT)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -821,6 +821,9 @@ impl Layout {
|
|||
|
||||
Ok(Layout::Struct(slice))
|
||||
}
|
||||
FlatType::Tuple(_elems, _ext) => {
|
||||
todo!();
|
||||
}
|
||||
FlatType::TagUnion(union_tags, ext) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, *ext));
|
||||
|
||||
|
@ -861,7 +864,7 @@ impl Layout {
|
|||
|
||||
Ok(Layout::UnionRecursive(slices))
|
||||
}
|
||||
FlatType::EmptyRecord => Ok(Layout::UNIT),
|
||||
FlatType::EmptyRecord | FlatType::EmptyTuple => Ok(Layout::UNIT),
|
||||
FlatType::EmptyTagUnion => Ok(Layout::VOID),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use roc_solve_problem::{
|
|||
use roc_types::num::NumericRange;
|
||||
use roc_types::subs::{
|
||||
instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, RecordFields, Subs, SubsSlice,
|
||||
Variable,
|
||||
TupleElems, Variable,
|
||||
};
|
||||
use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory, Polarity, Types};
|
||||
use roc_unify::unify::{Env, MustImplementConstraints};
|
||||
|
@ -542,6 +542,18 @@ trait DerivableVisitor {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_tuple(
|
||||
_subs: &Subs,
|
||||
var: Variable,
|
||||
_elems: TupleElems,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_tag_union(var: Variable) -> Result<Descend, NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
|
@ -574,6 +586,14 @@ trait DerivableVisitor {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_empty_tuple(var: Variable) -> Result<(), NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_empty_tag_union(var: Variable) -> Result<(), NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
|
@ -702,6 +722,18 @@ trait DerivableVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
let descend = Self::visit_tuple(subs, var, elems)?;
|
||||
if descend.0 {
|
||||
push_var_slice!(elems.variables());
|
||||
if !matches!(
|
||||
subs.get_content_without_compacting(ext),
|
||||
Content::FlexVar(_) | Content::RigidVar(_)
|
||||
) {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
let descend = Self::visit_tag_union(var)?;
|
||||
if descend.0 {
|
||||
|
@ -728,6 +760,7 @@ trait DerivableVisitor {
|
|||
}
|
||||
}
|
||||
EmptyRecord => Self::visit_empty_record(var)?,
|
||||
EmptyTuple => Self::visit_empty_tuple(var)?,
|
||||
EmptyTagUnion => Self::visit_empty_tag_union(var)?,
|
||||
},
|
||||
Alias(
|
||||
|
|
|
@ -29,12 +29,13 @@ use roc_region::all::Loc;
|
|||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{
|
||||
self, AliasVariables, Content, Descriptor, FlatType, GetSubsSlice, LambdaSet, Mark,
|
||||
OptVariable, Rank, RecordFields, Subs, SubsSlice, TagExt, UlsOfVar, UnionLabels, UnionLambdas,
|
||||
UnionTags, Variable, VariableSubsSlice,
|
||||
OptVariable, Rank, RecordFields, Subs, SubsSlice, TagExt, TupleElems, UlsOfVar, UnionLabels,
|
||||
UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_types::types::{
|
||||
gather_fields_unsorted_iter, AliasKind, AliasShared, Category, ExtImplicitOpenness, OptAbleVar,
|
||||
Polarity, Reason, RecordField, Type, TypeExtension, TypeTag, Types, Uls,
|
||||
gather_fields_unsorted_iter, gather_tuple_elems_unsorted_iter, AliasKind, AliasShared,
|
||||
Category, ExtImplicitOpenness, OptAbleVar, Polarity, Reason, RecordField, Type, TypeExtension,
|
||||
TypeTag, Types, Uls,
|
||||
};
|
||||
use roc_unify::unify::{
|
||||
unify, unify_introduced_ability_specialization, Env as UEnv, Mode, Obligated,
|
||||
|
@ -2658,6 +2659,39 @@ fn type_to_variable<'a>(
|
|||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
|
||||
Tuple(elems) => {
|
||||
let ext_slice = types.get_type_arguments(typ_index);
|
||||
|
||||
// Elems should never be empty; we don't support empty tuples
|
||||
debug_assert!(!elems.is_empty() || !ext_slice.is_empty());
|
||||
|
||||
let mut elem_vars = Vec::with_capacity_in(elems.len(), arena);
|
||||
|
||||
let (indices, elem_tys) = types.tuple_elems_slices(elems);
|
||||
|
||||
for (index, elem_type) in indices.into_iter().zip(elem_tys.into_iter()) {
|
||||
let elem_var = helper!(elem_type);
|
||||
elem_vars.push((types[index], elem_var));
|
||||
}
|
||||
|
||||
debug_assert!(ext_slice.len() <= 1);
|
||||
let temp_ext_var = match ext_slice.into_iter().next() {
|
||||
None => roc_types::subs::Variable::EMPTY_TUPLE,
|
||||
Some(ext) => helper!(ext),
|
||||
};
|
||||
|
||||
let (it, new_ext_var) =
|
||||
gather_tuple_elems_unsorted_iter(subs, TupleElems::empty(), temp_ext_var)
|
||||
.expect("Something ended up weird in this tuple type");
|
||||
|
||||
elem_vars.extend(it);
|
||||
let tuple_elems = TupleElems::insert_into_subs(subs, elem_vars);
|
||||
|
||||
let content = Content::Structure(FlatType::Tuple(tuple_elems, new_ext_var));
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
|
||||
TagUnion(tags, ext_openness) => {
|
||||
let ext_slice = types.get_type_arguments(typ_index);
|
||||
|
||||
|
@ -3728,7 +3762,7 @@ fn adjust_rank_content(
|
|||
rank
|
||||
}
|
||||
|
||||
EmptyRecord => {
|
||||
EmptyRecord | EmptyTuple => {
|
||||
// from elm-compiler: THEORY: an empty record never needs to get generalized
|
||||
//
|
||||
// But for us, that theory does not hold, because there might be type variables hidden
|
||||
|
@ -3767,6 +3801,17 @@ fn adjust_rank_content(
|
|||
rank
|
||||
}
|
||||
|
||||
Tuple(elems, ext_var) => {
|
||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||
|
||||
for (_, var_index) in elems.iter_all() {
|
||||
let var = subs[var_index];
|
||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||
}
|
||||
|
||||
rank
|
||||
}
|
||||
|
||||
TagUnion(tags, ext_var) => {
|
||||
let mut rank =
|
||||
adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var.var());
|
||||
|
@ -4125,7 +4170,7 @@ fn deep_copy_var_help(
|
|||
Func(new_arguments, new_closure_var, new_ret_var)
|
||||
}
|
||||
|
||||
same @ EmptyRecord | same @ EmptyTagUnion => same,
|
||||
same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same,
|
||||
|
||||
Record(fields, ext_var) => {
|
||||
let record_fields = {
|
||||
|
@ -4168,6 +4213,20 @@ fn deep_copy_var_help(
|
|||
Record(record_fields, work!(ext_var))
|
||||
}
|
||||
|
||||
Tuple(elems, ext_var) => {
|
||||
let tuple_elems = {
|
||||
let new_variables = copy_sequence!(elems.len(), elems.iter_variables());
|
||||
|
||||
TupleElems {
|
||||
length: elems.length,
|
||||
variables_start: new_variables.start,
|
||||
elem_index_start: elems.elem_index_start,
|
||||
}
|
||||
};
|
||||
|
||||
Tuple(tuple_elems, work!(ext_var))
|
||||
}
|
||||
|
||||
TagUnion(tags, ext_var) => {
|
||||
let union_tags = copy_union!(tags);
|
||||
|
||||
|
|
|
@ -1469,6 +1469,34 @@ mod solve_expr {
|
|||
infer_eq("{ x: 5, y : 3.14 }.x", "Num *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_literal_accessor_function() {
|
||||
infer_eq(".x { x: 5, y : 3.14 }", "Num *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_literal_accessor() {
|
||||
infer_eq("(5, 3.14 ).0", "Num *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_literal_accessor_function() {
|
||||
infer_eq(".0 (5, 3.14 )", "Num *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_literal_ty() {
|
||||
infer_eq("(5, 3.14 )", "( Num *, Float * )*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_literal_accessor_ty() {
|
||||
infer_eq(".0", "( a )* -> a");
|
||||
infer_eq(".4", "( _, _, _, _, a )* -> a");
|
||||
infer_eq(".5", "( ... 5 omitted, a )* -> a");
|
||||
infer_eq(".200", "( ... 200 omitted, a )* -> a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_arg() {
|
||||
infer_eq("\\rec -> rec.x", "{ x : a }* -> a");
|
||||
|
|
|
@ -15,6 +15,9 @@ pub static WILDCARD: &str = "*";
|
|||
static EMPTY_RECORD: &str = "{}";
|
||||
static EMPTY_TAG_UNION: &str = "[]";
|
||||
|
||||
// TODO: since we technically don't support empty tuples at the source level, this should probably be removed
|
||||
static EMPTY_TUPLE: &str = "()";
|
||||
|
||||
/// Requirements for parentheses.
|
||||
///
|
||||
/// If we're inside a function (that is, this is either an argument or a return
|
||||
|
@ -225,6 +228,28 @@ fn find_names_needed(
|
|||
find_under_alias,
|
||||
);
|
||||
}
|
||||
Structure(Tuple(elems, ext_var)) => {
|
||||
for index in elems.iter_variables() {
|
||||
let var = subs[index];
|
||||
find_names_needed(
|
||||
var,
|
||||
subs,
|
||||
roots,
|
||||
root_appearances,
|
||||
names_taken,
|
||||
find_under_alias,
|
||||
);
|
||||
}
|
||||
|
||||
find_names_needed(
|
||||
*ext_var,
|
||||
subs,
|
||||
roots,
|
||||
root_appearances,
|
||||
names_taken,
|
||||
find_under_alias,
|
||||
);
|
||||
}
|
||||
Structure(TagUnion(tags, ext_var)) => {
|
||||
for slice_index in tags.variables() {
|
||||
let slice = subs[slice_index];
|
||||
|
@ -372,7 +397,7 @@ fn find_names_needed(
|
|||
find_under_alias,
|
||||
);
|
||||
}
|
||||
Error | Structure(EmptyRecord) | Structure(EmptyTagUnion) => {
|
||||
Error | Structure(EmptyRecord) | Structure(EmptyTuple) | Structure(EmptyTagUnion) => {
|
||||
// Errors and empty records don't need names.
|
||||
}
|
||||
}
|
||||
|
@ -1112,6 +1137,7 @@ fn write_flat_type<'a>(
|
|||
pol,
|
||||
),
|
||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||
EmptyTuple => buf.push_str(EMPTY_TUPLE),
|
||||
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
||||
Func(args, closure, ret) => write_fn(
|
||||
env,
|
||||
|
@ -1187,6 +1213,67 @@ fn write_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext_var) => {
|
||||
use crate::types::{gather_tuple_elems, TupleStructure};
|
||||
|
||||
// If the `ext` has concrete elems (e.g. (I64, I64)(Bool)), merge them
|
||||
let TupleStructure {
|
||||
elems: sorted_elems,
|
||||
ext,
|
||||
} = gather_tuple_elems(subs, *elems, *ext_var)
|
||||
.expect("Something ended up weird in this record type");
|
||||
let ext_var = ext;
|
||||
|
||||
buf.push_str("( ");
|
||||
|
||||
let mut any_written_yet = false;
|
||||
let mut expected_next_index = 0;
|
||||
|
||||
for (index, var) in sorted_elems {
|
||||
if any_written_yet {
|
||||
buf.push_str(", ");
|
||||
} else {
|
||||
any_written_yet = true;
|
||||
}
|
||||
|
||||
if index - expected_next_index > 4 {
|
||||
// Don't write out a large number of _'s - just write out a count
|
||||
buf.push_str(&format!("... {} omitted, ", index - expected_next_index));
|
||||
} else if index - expected_next_index > 1 {
|
||||
// Write out a bunch of _'s
|
||||
for _ in expected_next_index..index {
|
||||
buf.push_str("_, ");
|
||||
}
|
||||
}
|
||||
expected_next_index = index + 1;
|
||||
|
||||
write_content(
|
||||
env,
|
||||
ctx,
|
||||
subs.get_content_without_compacting(var),
|
||||
subs,
|
||||
buf,
|
||||
Parens::Unnecessary,
|
||||
pol,
|
||||
);
|
||||
}
|
||||
|
||||
buf.push_str(" )");
|
||||
|
||||
match subs.get_content_without_compacting(ext_var) {
|
||||
Content::Structure(EmptyTuple) => {
|
||||
// This is a closed tuple. We're done!
|
||||
}
|
||||
content => {
|
||||
// This is an open tuple, so print the variable
|
||||
// right after the ')'
|
||||
//
|
||||
// e.g. the "*" at the end of `( I64, I64 )*`
|
||||
// or the "r" at the end of `( I64, I64 )r`
|
||||
write_content(env, ctx, content, subs, buf, parens, pol)
|
||||
}
|
||||
}
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
buf.push('[');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
use crate::types::{
|
||||
name_type_var, AbilitySet, AliasKind, ErrorType, ExtImplicitOpenness, Polarity, RecordField,
|
||||
RecordFieldsError, TypeExt, Uls,
|
||||
RecordFieldsError, TupleElemsError, TypeExt, Uls,
|
||||
};
|
||||
use roc_collections::all::{FnvMap, ImMap, ImSet, MutSet, SendMap};
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
|
@ -70,6 +70,7 @@ struct SubsHeader {
|
|||
tag_names: u64,
|
||||
symbol_names: u64,
|
||||
field_names: u64,
|
||||
tuple_elem_indices: u64,
|
||||
record_fields: u64,
|
||||
variable_slices: u64,
|
||||
unspecialized_lambda_sets: u64,
|
||||
|
@ -85,6 +86,7 @@ impl SubsHeader {
|
|||
tag_names: subs.tag_names.len() as u64,
|
||||
symbol_names: subs.symbol_names.len() as u64,
|
||||
field_names: subs.field_names.len() as u64,
|
||||
tuple_elem_indices: subs.tuple_elem_indices.len() as u64,
|
||||
record_fields: subs.record_fields.len() as u64,
|
||||
variable_slices: subs.variable_slices.len() as u64,
|
||||
unspecialized_lambda_sets: subs.unspecialized_lambda_sets.len() as u64,
|
||||
|
@ -127,6 +129,7 @@ impl Subs {
|
|||
written = Self::serialize_tag_names(&self.tag_names, writer, written)?;
|
||||
written = bytes::serialize_slice(&self.symbol_names, writer, written)?;
|
||||
written = Self::serialize_field_names(&self.field_names, writer, written)?;
|
||||
written = bytes::serialize_slice(&self.tuple_elem_indices, writer, written)?;
|
||||
written = bytes::serialize_slice(&self.record_fields, writer, written)?;
|
||||
written = bytes::serialize_slice(&self.variable_slices, writer, written)?;
|
||||
written = bytes::serialize_slice(&self.unspecialized_lambda_sets, writer, written)?;
|
||||
|
@ -220,6 +223,8 @@ impl Subs {
|
|||
bytes::deserialize_slice(bytes, header.symbol_names as usize, offset);
|
||||
let (field_names, offset) =
|
||||
Self::deserialize_field_names(bytes, header.field_names as usize, offset);
|
||||
let (tuple_elem_indices, offset) =
|
||||
bytes::deserialize_slice(bytes, header.tuple_elem_indices as usize, offset);
|
||||
let (record_fields, offset) =
|
||||
bytes::deserialize_slice(bytes, header.record_fields as usize, offset);
|
||||
let (variable_slices, offset) =
|
||||
|
@ -239,6 +244,7 @@ impl Subs {
|
|||
tag_names: tag_names.to_vec(),
|
||||
symbol_names: symbol_names.to_vec(),
|
||||
field_names,
|
||||
tuple_elem_indices: tuple_elem_indices.to_vec(),
|
||||
record_fields: record_fields.to_vec(),
|
||||
variable_slices: variable_slices.to_vec(),
|
||||
unspecialized_lambda_sets: unspecialized_lambda_sets.to_vec(),
|
||||
|
@ -368,6 +374,7 @@ impl UlsOfVar {
|
|||
pub struct Subs {
|
||||
utable: UnificationTable,
|
||||
pub variables: Vec<Variable>,
|
||||
pub tuple_elem_indices: Vec<usize>,
|
||||
pub tag_names: Vec<TagName>,
|
||||
pub symbol_names: Vec<Symbol>,
|
||||
pub field_names: Vec<Lowercase>,
|
||||
|
@ -445,6 +452,14 @@ impl std::ops::Index<SubsIndex<Lowercase>> for Subs {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<SubsIndex<usize>> for Subs {
|
||||
type Output = usize;
|
||||
|
||||
fn index(&self, index: SubsIndex<usize>) -> &Self::Output {
|
||||
&self.tuple_elem_indices[index.index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<SubsIndex<TagName>> for Subs {
|
||||
type Output = TagName;
|
||||
|
||||
|
@ -742,6 +757,12 @@ impl GetSubsSlice<Uls> for Subs {
|
|||
}
|
||||
}
|
||||
|
||||
impl GetSubsSlice<usize> for Subs {
|
||||
fn get_subs_slice(&self, subs_slice: SubsSlice<usize>) -> &[usize] {
|
||||
subs_slice.get_slice(&self.tuple_elem_indices)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Subs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
|
@ -928,6 +949,20 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
|
|||
|
||||
write!(f, "}}<{:?}>", new_ext)
|
||||
}
|
||||
FlatType::Tuple(elems, ext) => {
|
||||
write!(f, "( ")?;
|
||||
|
||||
let (it, new_ext) = elems.sorted_iterator_and_ext(subs, *ext);
|
||||
for (_i, content) in it {
|
||||
write!(
|
||||
f,
|
||||
"{:?}, ",
|
||||
SubsFmtContent(subs.get_content_without_compacting(content), subs)
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(f, ")<{:?}>", new_ext)
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) => {
|
||||
write!(f, "[")?;
|
||||
|
||||
|
@ -967,6 +1002,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
|
|||
write!(f, "]<{:?}> as <{:?}>", new_ext, rec)
|
||||
}
|
||||
FlatType::EmptyRecord => write!(f, "EmptyRecord"),
|
||||
FlatType::EmptyTuple => write!(f, "EmptyTuple"),
|
||||
FlatType::EmptyTagUnion => write!(f, "EmptyTagUnion"),
|
||||
}
|
||||
}
|
||||
|
@ -1224,6 +1260,7 @@ define_const_var! {
|
|||
NULL,
|
||||
|
||||
:pub EMPTY_RECORD,
|
||||
:pub EMPTY_TUPLE,
|
||||
:pub EMPTY_TAG_UNION,
|
||||
|
||||
BOOL_ENUM,
|
||||
|
@ -1714,6 +1751,7 @@ impl Subs {
|
|||
symbol_names,
|
||||
field_names: Vec::new(),
|
||||
record_fields: Vec::new(),
|
||||
tuple_elem_indices: Vec::new(),
|
||||
variable_slices: vec![
|
||||
// used for "TagOrFunction"
|
||||
VariableSubsSlice::default(),
|
||||
|
@ -2563,6 +2601,7 @@ pub enum FlatType {
|
|||
Apply(Symbol, VariableSubsSlice),
|
||||
Func(VariableSubsSlice, Variable, Variable),
|
||||
Record(RecordFields, Variable),
|
||||
Tuple(TupleElems, Variable),
|
||||
TagUnion(UnionTags, TagExt),
|
||||
|
||||
/// `A` might either be a function
|
||||
|
@ -2572,6 +2611,7 @@ pub enum FlatType {
|
|||
|
||||
RecursiveTagUnion(Variable, UnionTags, TagExt),
|
||||
EmptyRecord,
|
||||
EmptyTuple,
|
||||
EmptyTagUnion,
|
||||
}
|
||||
|
||||
|
@ -3068,7 +3108,8 @@ fn first<K: Ord, V>(x: &(K, V), y: &(K, V)) -> std::cmp::Ordering {
|
|||
x.0.cmp(&y.0)
|
||||
}
|
||||
|
||||
pub type SortedIterator<'a> = Box<dyn Iterator<Item = (Lowercase, RecordField<Variable>)> + 'a>;
|
||||
pub type SortedFieldIterator<'a> =
|
||||
Box<dyn Iterator<Item = (Lowercase, RecordField<Variable>)> + 'a>;
|
||||
|
||||
impl RecordFields {
|
||||
pub const fn len(&self) -> usize {
|
||||
|
@ -3190,7 +3231,7 @@ impl RecordFields {
|
|||
///
|
||||
/// Hopefully the inline will get rid of the Box in practice
|
||||
#[inline(always)]
|
||||
pub fn sorted_iterator<'a>(&'_ self, subs: &'a Subs, ext: Variable) -> SortedIterator<'a> {
|
||||
pub fn sorted_iterator<'a>(&'_ self, subs: &'a Subs, ext: Variable) -> SortedFieldIterator<'a> {
|
||||
self.sorted_iterator_and_ext(subs, ext).0
|
||||
}
|
||||
|
||||
|
@ -3199,7 +3240,7 @@ impl RecordFields {
|
|||
&'_ self,
|
||||
subs: &'a Subs,
|
||||
ext: Variable,
|
||||
) -> (SortedIterator<'a>, Variable) {
|
||||
) -> (SortedFieldIterator<'a>, Variable) {
|
||||
if is_empty_record(subs, ext) {
|
||||
(
|
||||
Box::new(self.iter_all().map(move |(i1, i2, i3)| {
|
||||
|
@ -3271,6 +3312,128 @@ fn is_empty_record(subs: &Subs, mut var: Variable) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TupleElems {
|
||||
pub length: u16,
|
||||
|
||||
// TODO: make this a Result<u32, u32>, where Ok(x) means that the tuple is
|
||||
// is fully sparse (the current case), and Err(x) means that the tuple is locally dense
|
||||
// (meaning all the non-sparse elements are sequential) where x is the start of the
|
||||
// dense section. This means we can encode both sparse tuple types generated by e.g. `.5`
|
||||
// and dense tuple types, e.g. `(1, 2, 3)` without taking up any extra space.
|
||||
pub elem_index_start: u32,
|
||||
|
||||
pub variables_start: u32,
|
||||
}
|
||||
|
||||
impl TupleElems {
|
||||
pub const fn len(&self) -> usize {
|
||||
self.length as usize
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
length: 0,
|
||||
elem_index_start: 0,
|
||||
variables_start: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn variables(&self) -> SubsSlice<Variable> {
|
||||
SubsSlice::new(self.variables_start, self.length)
|
||||
}
|
||||
|
||||
pub const fn elem_indices(&self) -> SubsSlice<usize> {
|
||||
SubsSlice::new(self.elem_index_start, self.length)
|
||||
}
|
||||
|
||||
pub fn iter_variables(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
||||
let slice = SubsSlice::new(self.variables_start, self.length);
|
||||
slice.into_iter()
|
||||
}
|
||||
|
||||
pub fn iter_all(&self) -> impl Iterator<Item = (SubsIndex<usize>, SubsIndex<Variable>)> {
|
||||
let helper = |start| start..(start + self.length as u32);
|
||||
|
||||
let range1 = helper(self.elem_index_start);
|
||||
let range2 = helper(self.variables_start);
|
||||
|
||||
let it = range1.into_iter().zip(range2.into_iter());
|
||||
|
||||
it.map(|(i1, i2)| (SubsIndex::new(i1), SubsIndex::new(i2)))
|
||||
}
|
||||
|
||||
pub fn insert_into_subs<I>(subs: &mut Subs, input: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = (usize, Variable)>,
|
||||
{
|
||||
let variables_start = subs.variables.len() as u32;
|
||||
let elem_index_start = subs.tuple_elem_indices.len() as u32;
|
||||
|
||||
let it = input.into_iter();
|
||||
let size_hint = it.size_hint().0;
|
||||
|
||||
subs.variables.reserve(size_hint);
|
||||
subs.tuple_elem_indices.reserve(size_hint);
|
||||
|
||||
let mut length = 0;
|
||||
for (index, var) in it {
|
||||
subs.variables.push(var);
|
||||
subs.tuple_elem_indices.push(index);
|
||||
|
||||
length += 1;
|
||||
}
|
||||
|
||||
TupleElems {
|
||||
length,
|
||||
elem_index_start,
|
||||
variables_start,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unsorted_iterator<'a>(
|
||||
&'a self,
|
||||
subs: &'a Subs,
|
||||
ext: Variable,
|
||||
) -> Result<impl Iterator<Item = (usize, Variable)> + 'a, TupleElemsError> {
|
||||
let (it, _) = crate::types::gather_tuple_elems_unsorted_iter(subs, *self, ext)?;
|
||||
|
||||
Ok(it)
|
||||
}
|
||||
|
||||
/// get a sorted iterator over the elems of this tuple type
|
||||
///
|
||||
/// This involves looking at both the type itself and the ext var and unioning the results
|
||||
#[inline(always)]
|
||||
pub fn sorted_iterator<'a>(
|
||||
&'_ self,
|
||||
subs: &'a Subs,
|
||||
ext: Variable,
|
||||
) -> Box<dyn Iterator<Item = (usize, Variable)> + 'a> {
|
||||
self.sorted_iterator_and_ext(subs, ext).0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sorted_iterator_and_ext<'a>(
|
||||
&'_ self,
|
||||
subs: &'a Subs,
|
||||
ext: Variable,
|
||||
) -> (Box<dyn Iterator<Item = (usize, Variable)> + 'a>, Variable) {
|
||||
let tuple_structure = crate::types::gather_tuple_elems(subs, *self, ext)
|
||||
.expect("Something ended up weird in this tuple type");
|
||||
|
||||
(
|
||||
Box::new(tuple_structure.elems.into_iter()),
|
||||
tuple_structure.ext,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static SCRATCHPAD_FOR_OCCURS: RefCell<Option<Vec<Variable>>> = RefCell::new(Some(Vec::with_capacity(1024)));
|
||||
}
|
||||
|
@ -3323,6 +3486,11 @@ fn occurs(
|
|||
let it = once(ext).chain(subs.get_subs_slice(slice).iter());
|
||||
short_circuit(subs, root_var, seen, it)
|
||||
}
|
||||
Tuple(vars_by_elem, ext) => {
|
||||
let slice = SubsSlice::new(vars_by_elem.variables_start, vars_by_elem.length);
|
||||
let it = once(ext).chain(subs.get_subs_slice(slice).iter());
|
||||
short_circuit(subs, root_var, seen, it)
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
occurs_union(subs, root_var, seen, tags)?;
|
||||
|
||||
|
@ -3336,7 +3504,7 @@ fn occurs(
|
|||
|
||||
short_circuit_help(subs, root_var, seen, ext.var())
|
||||
}
|
||||
EmptyRecord | EmptyTagUnion => Ok(()),
|
||||
EmptyRecord | EmptyTuple | EmptyTagUnion => Ok(()),
|
||||
},
|
||||
Alias(_, args, real_var, _) => {
|
||||
for var_index in args.into_iter() {
|
||||
|
@ -3505,8 +3673,19 @@ fn explicit_substitute(
|
|||
|
||||
subs.set_content(in_var, Structure(Record(vars_by_field, new_ext)));
|
||||
}
|
||||
Tuple(vars_by_elem, ext) => {
|
||||
let new_ext = explicit_substitute(subs, from, to, ext, seen);
|
||||
|
||||
EmptyRecord | EmptyTagUnion => {}
|
||||
for index in vars_by_elem.iter_variables() {
|
||||
let var = subs[index];
|
||||
let new_var = explicit_substitute(subs, from, to, var, seen);
|
||||
subs[index] = new_var;
|
||||
}
|
||||
|
||||
subs.set_content(in_var, Structure(Tuple(vars_by_elem, new_ext)));
|
||||
}
|
||||
|
||||
EmptyRecord | EmptyTuple | EmptyTagUnion => {}
|
||||
}
|
||||
|
||||
in_var
|
||||
|
@ -3689,7 +3868,9 @@ fn get_var_names(
|
|||
accum
|
||||
}
|
||||
|
||||
FlatType::EmptyRecord | FlatType::EmptyTagUnion => taken_names,
|
||||
FlatType::EmptyRecord | FlatType::EmptyTuple | FlatType::EmptyTagUnion => {
|
||||
taken_names
|
||||
}
|
||||
|
||||
FlatType::Record(vars_by_field, ext) => {
|
||||
let mut accum = get_var_names(subs, ext, taken_names);
|
||||
|
@ -3702,6 +3883,17 @@ fn get_var_names(
|
|||
|
||||
accum
|
||||
}
|
||||
FlatType::Tuple(vars_by_elems, ext) => {
|
||||
let mut accum = get_var_names(subs, ext, taken_names);
|
||||
|
||||
for var_index in vars_by_elems.iter_variables() {
|
||||
let arg_var = subs[var_index];
|
||||
|
||||
accum = get_var_names(subs, arg_var, accum)
|
||||
}
|
||||
|
||||
accum
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) => {
|
||||
let taken_names = get_var_names(subs, ext.var(), taken_names);
|
||||
get_var_names_union(subs, tags, taken_names)
|
||||
|
@ -3938,6 +4130,55 @@ fn content_to_err_type(
|
|||
}
|
||||
}
|
||||
|
||||
fn sorted_union<T>(a: Vec<(usize, T)>, b: Vec<(usize, T)>) -> Vec<(usize, T)> {
|
||||
// Asuming the two slices are sorted (by the first element), merge them into a single sorted slice
|
||||
// If we see duplicates, panic.
|
||||
|
||||
let mut result = Vec::with_capacity(a.len() + b.len());
|
||||
|
||||
let mut a_iter = a.into_iter();
|
||||
let mut b_iter = b.into_iter();
|
||||
|
||||
let mut a_next = a_iter.next();
|
||||
let mut b_next = b_iter.next();
|
||||
|
||||
loop {
|
||||
match (a_next, b_next) {
|
||||
(Some((a_index, a_value)), Some((b_index, b_value))) => {
|
||||
if a_index == b_index {
|
||||
panic!("Duplicate index in sorted_union");
|
||||
}
|
||||
|
||||
if a_index < b_index {
|
||||
result.push((a_index, a_value));
|
||||
a_next = a_iter.next();
|
||||
b_next = Some((b_index, b_value));
|
||||
} else {
|
||||
result.push((b_index, b_value));
|
||||
b_next = b_iter.next();
|
||||
a_next = Some((a_index, a_value));
|
||||
}
|
||||
}
|
||||
|
||||
(Some((a_index, a_value)), None) => {
|
||||
result.push((a_index, a_value));
|
||||
a_next = a_iter.next();
|
||||
b_next = None;
|
||||
}
|
||||
|
||||
(None, Some((b_index, b_value))) => {
|
||||
result.push((b_index, b_value));
|
||||
b_next = b_iter.next();
|
||||
a_next = None;
|
||||
}
|
||||
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn flat_type_to_err_type(
|
||||
subs: &mut Subs,
|
||||
state: &mut ErrorTypeState,
|
||||
|
@ -3975,6 +4216,7 @@ fn flat_type_to_err_type(
|
|||
}
|
||||
|
||||
EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed),
|
||||
EmptyTuple => ErrorType::Tuple(Vec::default(), TypeExt::Closed),
|
||||
EmptyTagUnion => ErrorType::TagUnion(SendMap::default(), TypeExt::Closed, pol),
|
||||
|
||||
Record(vars_by_field, ext) => {
|
||||
|
@ -4019,6 +4261,38 @@ fn flat_type_to_err_type(
|
|||
}
|
||||
}
|
||||
|
||||
Tuple(vars_by_elems, ext) => {
|
||||
let mut err_elems = Vec::default();
|
||||
|
||||
for (i1, i2) in vars_by_elems.iter_all() {
|
||||
let index = subs[i1];
|
||||
let var = subs[i2];
|
||||
|
||||
let error_type = var_to_err_type(subs, state, var, pol);
|
||||
|
||||
err_elems.push((index, error_type));
|
||||
}
|
||||
|
||||
match var_to_err_type(subs, state, ext, pol).unwrap_structural_alias() {
|
||||
ErrorType::Tuple(sub_elems, sub_ext) => {
|
||||
ErrorType::Tuple(sorted_union(sub_elems, err_elems), sub_ext)
|
||||
}
|
||||
|
||||
ErrorType::FlexVar(var) => {
|
||||
ErrorType::Tuple(err_elems, TypeExt::FlexOpen(var))
|
||||
}
|
||||
|
||||
ErrorType::RigidVar(var) => {
|
||||
ErrorType::Tuple(err_elems, TypeExt::RigidOpen(var))
|
||||
}
|
||||
|
||||
ErrorType::Error => ErrorType::Tuple(err_elems, TypeExt::Closed),
|
||||
|
||||
other =>
|
||||
panic!("Tried to convert a record extension to an error, but the record extension had the ErrorType of {:?}", other)
|
||||
}
|
||||
}
|
||||
|
||||
TagUnion(tags, ext) => {
|
||||
let err_tags = union_tags_to_err_tags(subs, state, tags, pol);
|
||||
|
||||
|
@ -4369,6 +4643,10 @@ impl StorageSubs {
|
|||
Self::offset_record_fields(offsets, *record_fields),
|
||||
Self::offset_variable(offsets, *ext),
|
||||
),
|
||||
FlatType::Tuple(tuple_elems, ext) => FlatType::Tuple(
|
||||
Self::offset_tuple_elems(offsets, *tuple_elems),
|
||||
Self::offset_variable(offsets, *ext),
|
||||
),
|
||||
FlatType::TagUnion(union_tags, ext) => FlatType::TagUnion(
|
||||
Self::offset_tag_union(offsets, *union_tags),
|
||||
ext.map(|v| Self::offset_variable(offsets, v)),
|
||||
|
@ -4384,6 +4662,7 @@ impl StorageSubs {
|
|||
ext.map(|v| Self::offset_variable(offsets, v)),
|
||||
),
|
||||
FlatType::EmptyRecord => FlatType::EmptyRecord,
|
||||
FlatType::EmptyTuple => FlatType::EmptyTuple,
|
||||
FlatType::EmptyTagUnion => FlatType::EmptyTagUnion,
|
||||
}
|
||||
}
|
||||
|
@ -4476,6 +4755,12 @@ impl StorageSubs {
|
|||
record_fields
|
||||
}
|
||||
|
||||
fn offset_tuple_elems(offsets: &StorageSubsOffsets, mut tuple_elems: TupleElems) -> TupleElems {
|
||||
tuple_elems.variables_start += offsets.variables;
|
||||
|
||||
tuple_elems
|
||||
}
|
||||
|
||||
fn offset_tag_name_slice(
|
||||
offsets: &StorageSubsOffsets,
|
||||
mut tag_names: SubsSlice<TagName>,
|
||||
|
@ -4670,7 +4955,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
|||
Func(new_arguments, new_closure_var, new_ret_var)
|
||||
}
|
||||
|
||||
same @ EmptyRecord | same @ EmptyTagUnion => same,
|
||||
same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same,
|
||||
|
||||
Record(fields, ext) => {
|
||||
let record_fields = {
|
||||
|
@ -4707,6 +4992,33 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
|||
Record(record_fields, storage_copy_var_to_help(env, ext))
|
||||
}
|
||||
|
||||
Tuple(elems, ext) => {
|
||||
let tuple_elems = {
|
||||
let new_variables =
|
||||
VariableSubsSlice::reserve_into_subs(env.target, elems.len());
|
||||
|
||||
let it = (new_variables.indices()).zip(elems.iter_variables());
|
||||
for (target_index, var_index) in it {
|
||||
let var = env.source[var_index];
|
||||
let copy_var = storage_copy_var_to_help(env, var);
|
||||
env.target.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
let elem_index_start = env.target.tuple_elem_indices.len() as u32;
|
||||
|
||||
// TODO: introduce a dense variant of TupleElems, by making the indices a result
|
||||
env.target.tuple_elem_indices.extend(0..elems.len());
|
||||
|
||||
TupleElems {
|
||||
length: elems.len() as _,
|
||||
variables_start: new_variables.start,
|
||||
elem_index_start,
|
||||
}
|
||||
};
|
||||
|
||||
Tuple(tuple_elems, storage_copy_var_to_help(env, ext))
|
||||
}
|
||||
|
||||
TagUnion(tags, ext) => {
|
||||
let new_ext = ext.map(|v| storage_copy_var_to_help(env, v));
|
||||
let union_tags = storage_copy_union(env, tags);
|
||||
|
@ -5118,7 +5430,7 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
|||
Func(new_arguments, new_closure_var, new_ret_var)
|
||||
}
|
||||
|
||||
same @ EmptyRecord | same @ EmptyTagUnion => same,
|
||||
same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same,
|
||||
|
||||
Record(fields, ext) => {
|
||||
let record_fields = {
|
||||
|
@ -5155,6 +5467,33 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
|||
Record(record_fields, copy_import_to_help(env, max_rank, ext))
|
||||
}
|
||||
|
||||
Tuple(elems, ext) => {
|
||||
let tuple_elems = {
|
||||
let new_variables =
|
||||
VariableSubsSlice::reserve_into_subs(env.target, elems.len());
|
||||
|
||||
let it = (new_variables.indices()).zip(elems.iter_variables());
|
||||
for (target_index, var_index) in it {
|
||||
let var = env.source[var_index];
|
||||
let copy_var = copy_import_to_help(env, max_rank, var);
|
||||
env.target.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
let elem_index_start = env.target.tuple_elem_indices.len() as u32;
|
||||
|
||||
// TODO: introduce a dense variant of TupleElems, by making the indices a result
|
||||
env.target.tuple_elem_indices.extend(0..elems.len());
|
||||
|
||||
TupleElems {
|
||||
length: elems.len() as _,
|
||||
variables_start: new_variables.start,
|
||||
elem_index_start,
|
||||
}
|
||||
};
|
||||
|
||||
Tuple(tuple_elems, copy_import_to_help(env, max_rank, ext))
|
||||
}
|
||||
|
||||
TagUnion(tags, ext) => {
|
||||
let new_ext = ext.map(|v| copy_import_to_help(env, max_rank, v));
|
||||
|
||||
|
@ -5450,8 +5789,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
|||
stack.push(closure_var);
|
||||
}
|
||||
|
||||
EmptyRecord => (),
|
||||
EmptyTagUnion => (),
|
||||
EmptyRecord | EmptyTuple | EmptyTagUnion => (),
|
||||
|
||||
Record(fields, ext) => {
|
||||
let fields = *fields;
|
||||
|
@ -5460,6 +5798,14 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
|||
|
||||
stack.push(ext);
|
||||
}
|
||||
|
||||
Tuple(elems, ext) => {
|
||||
let elems = *elems;
|
||||
let ext = *ext;
|
||||
stack.extend(var_slice!(elems.variables()));
|
||||
|
||||
stack.push(ext);
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
let tags = *tags;
|
||||
let ext = *ext;
|
||||
|
@ -5570,6 +5916,10 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio
|
|||
stack.extend(subs.get_subs_slice(fields.variables()));
|
||||
stack.push(*ext);
|
||||
}
|
||||
FlatType::Tuple(elems, ext) => {
|
||||
stack.extend(subs.get_subs_slice(elems.variables()));
|
||||
stack.push(*ext);
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) => {
|
||||
stack.extend(
|
||||
subs.get_subs_slice(tags.variables())
|
||||
|
@ -5590,7 +5940,7 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio
|
|||
);
|
||||
stack.push(ext.var());
|
||||
}
|
||||
FlatType::EmptyRecord | FlatType::EmptyTagUnion => {}
|
||||
FlatType::EmptyRecord | FlatType::EmptyTuple | FlatType::EmptyTagUnion => {}
|
||||
},
|
||||
Content::Alias(_, _, real_var, _) => {
|
||||
stack.push(*real_var);
|
||||
|
@ -5642,6 +5992,12 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool {
|
|||
stack.extend(field_vars)
|
||||
}
|
||||
}
|
||||
FlatType::Tuple(elems, ext) => {
|
||||
if let Ok(iter) = elems.unsorted_iterator(subs, *ext) {
|
||||
let elem_vars = iter.map(|(_, elem)| elem);
|
||||
stack.extend(elem_vars)
|
||||
}
|
||||
}
|
||||
FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => {
|
||||
let mut is_uninhabited = true;
|
||||
// If any tag is inhabited, the union is inhabited!
|
||||
|
@ -5658,6 +6014,7 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool {
|
|||
}
|
||||
FlatType::FunctionOrTagUnion(_, _, _) => {}
|
||||
FlatType::EmptyRecord => {}
|
||||
FlatType::EmptyTuple => {}
|
||||
FlatType::EmptyTagUnion => {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::num::NumericRange;
|
||||
use crate::pretty_print::Parens;
|
||||
use crate::subs::{
|
||||
GetSubsSlice, RecordFields, Subs, TagExt, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||
GetSubsSlice, RecordFields, Subs, TagExt, TupleElems, UnionTags, VarStore, Variable,
|
||||
VariableSubsSlice,
|
||||
};
|
||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_collections::soa::{Index, Slice};
|
||||
|
@ -436,6 +437,7 @@ pub enum TypeTag {
|
|||
TagUnion(UnionTags, ExtImplicitOpenness),
|
||||
RecursiveTagUnion(Variable, UnionTags, ExtImplicitOpenness),
|
||||
Record(RecordFields),
|
||||
Tuple(TupleElems),
|
||||
}
|
||||
|
||||
/// Look-aside slice of types used in [Types], when the slice does not correspond to the direct
|
||||
|
@ -485,6 +487,9 @@ pub struct Types {
|
|||
field_types: Vec<RecordField<()>>,
|
||||
field_names: Vec<Lowercase>,
|
||||
|
||||
// tuples
|
||||
tuple_elem_indices: Vec<usize>,
|
||||
|
||||
// aliases
|
||||
type_arg_abilities: Vec<AbilitySet>, // TODO: structural sharing for `AbilitySet`s themselves
|
||||
aliases: Vec<AliasShared>,
|
||||
|
@ -533,6 +538,7 @@ impl Types {
|
|||
tag_names: Default::default(),
|
||||
field_types: Default::default(),
|
||||
field_names: Default::default(),
|
||||
tuple_elem_indices: Default::default(),
|
||||
type_arg_abilities: Default::default(),
|
||||
aliases: Default::default(),
|
||||
single_tag_union_tag_names: Default::default(),
|
||||
|
@ -573,6 +579,19 @@ impl Types {
|
|||
(names, fields, tys)
|
||||
}
|
||||
|
||||
pub fn tuple_elems_slices(&self, elems: TupleElems) -> (Slice<usize>, Slice<TypeTag>) {
|
||||
let TupleElems {
|
||||
length,
|
||||
variables_start,
|
||||
elem_index_start,
|
||||
} = elems;
|
||||
|
||||
let index = Slice::new(elem_index_start, length);
|
||||
let tys = Slice::new(variables_start, length);
|
||||
|
||||
(index, tys)
|
||||
}
|
||||
|
||||
pub fn union_tag_slices(&self, union: UnionTags) -> (Slice<TagName>, Slice<AsideTypeSlice>) {
|
||||
let UnionTags {
|
||||
length,
|
||||
|
@ -853,6 +872,35 @@ impl Types {
|
|||
let tag = TypeTag::Record(record_fields);
|
||||
self.set_type_tag(index, tag, type_slice)
|
||||
}
|
||||
Type::Tuple(elems, extension) => {
|
||||
let type_slice = match extension {
|
||||
TypeExtension::Open(ext, _) => self.from_old_type(ext).as_slice(),
|
||||
TypeExtension::Closed => Slice::default(),
|
||||
};
|
||||
|
||||
// should we sort at this point?
|
||||
let elem_type_slice = {
|
||||
let slice = self.reserve_type_tags(elems.len());
|
||||
|
||||
for (index, (_elem_index, argument)) in slice.into_iter().zip(elems.iter()) {
|
||||
self.from_old_type_at(index, argument);
|
||||
}
|
||||
|
||||
slice
|
||||
};
|
||||
|
||||
let elem_index_slice =
|
||||
Slice::extend_new(&mut self.tuple_elem_indices, elems.iter().map(|(i, _)| *i));
|
||||
|
||||
let tuple_elems = TupleElems {
|
||||
length: elems.len() as u16,
|
||||
variables_start: elem_type_slice.start() as u32,
|
||||
elem_index_start: elem_index_slice.start() as u32,
|
||||
};
|
||||
|
||||
let tag = TypeTag::Tuple(tuple_elems);
|
||||
self.set_type_tag(index, tag, type_slice)
|
||||
}
|
||||
Type::ClosureTag {
|
||||
name,
|
||||
captures,
|
||||
|
@ -1262,6 +1310,21 @@ impl Types {
|
|||
|
||||
(Record(new_record_fields), new_ext_slice)
|
||||
}
|
||||
Tuple(elems) => {
|
||||
let ext_slice = self.get_type_arguments(typ);
|
||||
let (indices, tys) = self.tuple_elems_slices(elems);
|
||||
|
||||
let new_tys = defer_slice!(tys);
|
||||
let new_ext_slice = defer_slice!(ext_slice);
|
||||
|
||||
let new_tuple_elems = TupleElems {
|
||||
length: new_tys.len() as _,
|
||||
variables_start: new_tys.start() as _,
|
||||
elem_index_start: indices.start() as _,
|
||||
};
|
||||
|
||||
(Tuple(new_tuple_elems), new_ext_slice)
|
||||
}
|
||||
RangedNumber(range) => (RangedNumber(range), Default::default()),
|
||||
Error => (Error, Default::default()),
|
||||
};
|
||||
|
@ -1448,6 +1511,20 @@ mod debug_types {
|
|||
.align(),
|
||||
)
|
||||
}
|
||||
TypeTag::Tuple(elems) => {
|
||||
let (_indices, tys) = types.tuple_elems_slices(elems);
|
||||
let fmt_fields = tys.into_iter().map(|ty| typ(types, f, Free, ty));
|
||||
|
||||
f.text("(").append(
|
||||
f.intersperse(fmt_fields, f.reflow(", "))
|
||||
.append(
|
||||
f.text(")")
|
||||
.append(ext(types, f, types.get_type_arguments(tag))),
|
||||
)
|
||||
.group()
|
||||
.align(),
|
||||
)
|
||||
}
|
||||
};
|
||||
group.group()
|
||||
}
|
||||
|
@ -1616,6 +1693,7 @@ impl_types_index! {
|
|||
tag_names, TagName
|
||||
field_types, RecordField<()>
|
||||
field_names, Lowercase
|
||||
tuple_elem_indices, usize
|
||||
}
|
||||
|
||||
impl_types_index_slice! {
|
||||
|
@ -1645,6 +1723,7 @@ pub enum Type {
|
|||
/// A function. The types of its arguments, size of its closure, then the type of its return value.
|
||||
Function(Vec<Type>, Box<Type>, Box<Type>),
|
||||
Record(SendMap<Lowercase, RecordField<Type>>, TypeExtension),
|
||||
Tuple(VecMap<usize, Type>, TypeExtension),
|
||||
TagUnion(Vec<(TagName, Vec<Type>)>, TypeExtension),
|
||||
FunctionOrTagUnion(TagName, Symbol, TypeExtension),
|
||||
/// A function name that is used in our defunctionalization algorithm. For example in
|
||||
|
@ -1727,6 +1806,7 @@ impl Clone for Type {
|
|||
Self::Function(arg0.clone(), arg1.clone(), arg2.clone())
|
||||
}
|
||||
Self::Record(arg0, arg1) => Self::Record(arg0.clone(), arg1.clone()),
|
||||
Self::Tuple(arg0, arg1) => Self::Tuple(arg0.clone(), arg1.clone()),
|
||||
Self::TagUnion(arg0, arg1) => Self::TagUnion(arg0.clone(), arg1.clone()),
|
||||
Self::FunctionOrTagUnion(arg0, arg1, arg2) => {
|
||||
Self::FunctionOrTagUnion(arg0.clone(), *arg1, arg2.clone())
|
||||
|
@ -2042,6 +2122,46 @@ impl fmt::Debug for Type {
|
|||
}
|
||||
}
|
||||
}
|
||||
Type::Tuple(elems, ext) => {
|
||||
write!(f, "(")?;
|
||||
|
||||
if !elems.is_empty() {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
|
||||
let mut any_written_yet = false;
|
||||
|
||||
for (_, field_type) in elems.iter() {
|
||||
write!(f, "{:?}", field_type)?;
|
||||
|
||||
if any_written_yet {
|
||||
write!(f, ", ")?;
|
||||
} else {
|
||||
any_written_yet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !elems.is_empty() {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
|
||||
write!(f, ")")?;
|
||||
|
||||
match ext {
|
||||
TypeExtension::Closed => {
|
||||
// This is a closed record. We're done!
|
||||
Ok(())
|
||||
}
|
||||
TypeExtension::Open(other, _) => {
|
||||
// This is an open record, so print the variable
|
||||
// right after the '}'
|
||||
//
|
||||
// e.g. the "*" at the end of `{ x: Int }*`
|
||||
// or the "r" at the end of `{ x: Int }r`
|
||||
other.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::TagUnion(tags, ext) => {
|
||||
write_tags(f, tags.iter())?;
|
||||
|
||||
|
@ -2212,6 +2332,15 @@ impl Type {
|
|||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, x) in elems.iter_mut() {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
|
@ -2344,6 +2473,14 @@ impl Type {
|
|||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, x) in elems.iter_mut() {
|
||||
stack.push(x);
|
||||
}
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
|
@ -2463,6 +2600,18 @@ impl Type {
|
|||
TypeExtension::Closed => Ok(()),
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, x) in elems.iter_mut() {
|
||||
x.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||
}
|
||||
|
||||
match ext {
|
||||
TypeExtension::Open(ext, _) => {
|
||||
ext.substitute_alias(rep_symbol, rep_args, actual)
|
||||
}
|
||||
TypeExtension::Closed => Ok(()),
|
||||
}
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables: _no_aliases_in_lambda_sets,
|
||||
|
@ -2550,6 +2699,10 @@ impl Type {
|
|||
Self::contains_symbol_ext(ext, rep_symbol)
|
||||
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
Self::contains_symbol_ext(ext, rep_symbol)
|
||||
|| elems.iter().any(|(_, arg)| arg.contains_symbol(rep_symbol))
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
symbol,
|
||||
type_arguments,
|
||||
|
@ -2623,6 +2776,12 @@ impl Type {
|
|||
.values()
|
||||
.any(|arg| arg.contains_variable(rep_variable))
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
Self::contains_variable_ext(ext, rep_variable)
|
||||
|| elems
|
||||
.iter()
|
||||
.any(|(_i, arg)| arg.contains_variable(rep_variable))
|
||||
}
|
||||
DelayedAlias(AliasCommon { .. }) => {
|
||||
todo!()
|
||||
}
|
||||
|
@ -2758,6 +2917,27 @@ impl Type {
|
|||
);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, x) in elems.iter_mut() {
|
||||
x.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
ext.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
|
@ -3111,6 +3291,10 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
|||
stack.extend(ext);
|
||||
stack.extend(fields.values().map(|field| field.as_inner()));
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
stack.extend(ext);
|
||||
stack.extend(elems.iter().map(|(_, t)| t));
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
symbol,
|
||||
type_arguments,
|
||||
|
@ -3181,6 +3365,15 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
variables_help(ext, accum);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, elem) in elems.iter() {
|
||||
variables_help(elem, accum);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
variables_help(ext, accum);
|
||||
}
|
||||
}
|
||||
ClosureTag {
|
||||
name: _,
|
||||
captures,
|
||||
|
@ -3316,6 +3509,15 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, elem) in elems.iter() {
|
||||
variables_help_detailed(elem, accum);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
ClosureTag {
|
||||
name: _,
|
||||
captures,
|
||||
|
@ -3420,6 +3622,13 @@ pub struct RecordStructure {
|
|||
pub ext: Variable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TupleStructure {
|
||||
/// Invariant: these should be sorted!
|
||||
pub elems: Vec<(usize, Variable)>,
|
||||
pub ext: Variable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TagUnionStructure<'a> {
|
||||
/// Invariant: these should be sorted!
|
||||
|
@ -3566,8 +3775,11 @@ pub enum Category {
|
|||
|
||||
// records
|
||||
Record,
|
||||
Accessor(Lowercase),
|
||||
Access(Lowercase),
|
||||
RecordAccessor(Lowercase),
|
||||
RecordAccess(Lowercase),
|
||||
Tuple,
|
||||
TupleAccessor(usize),
|
||||
TupleAccess(usize),
|
||||
DefaultValue(Lowercase), // for setting optional fields
|
||||
|
||||
AbilityMemberSpecialization(Symbol),
|
||||
|
@ -3706,6 +3918,7 @@ pub enum ErrorType {
|
|||
FlexAbleVar(Lowercase, AbilitySet),
|
||||
RigidAbleVar(Lowercase, AbilitySet),
|
||||
Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt),
|
||||
Tuple(Vec<(usize, ErrorType)>, TypeExt),
|
||||
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt, Polarity),
|
||||
RecursiveTagUnion(
|
||||
Box<ErrorType>,
|
||||
|
@ -3749,6 +3962,10 @@ impl ErrorType {
|
|||
.for_each(|(_, t)| t.as_inner().add_names(taken));
|
||||
ext.add_names(taken);
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
elems.iter().for_each(|(_, t)| t.add_names(taken));
|
||||
ext.add_names(taken);
|
||||
}
|
||||
TagUnion(tags, ext, _) => {
|
||||
tags.iter()
|
||||
.for_each(|(_, ts)| ts.iter().for_each(|t| t.add_names(taken)));
|
||||
|
@ -4045,6 +4262,16 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
buf.push('}');
|
||||
write_type_ext(ext, buf);
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
buf.push('(');
|
||||
|
||||
for (_index, elem) in elems {
|
||||
write_debug_error_type_help(elem, buf, Parens::Unnecessary);
|
||||
}
|
||||
|
||||
buf.push(')');
|
||||
write_type_ext(ext, buf);
|
||||
}
|
||||
TagUnion(tags, ext, _pol) => {
|
||||
buf.push('[');
|
||||
|
||||
|
@ -4256,6 +4483,61 @@ pub fn gather_fields_unsorted_iter(
|
|||
Ok((it, var))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TupleElemsError;
|
||||
|
||||
pub fn gather_tuple_elems_unsorted_iter(
|
||||
subs: &Subs,
|
||||
other_elems: TupleElems,
|
||||
mut var: Variable,
|
||||
) -> Result<(impl Iterator<Item = (usize, Variable)> + '_, Variable), TupleElemsError> {
|
||||
use crate::subs::Content::*;
|
||||
use crate::subs::FlatType::*;
|
||||
|
||||
let mut stack = vec![other_elems];
|
||||
|
||||
loop {
|
||||
match subs.get_content_without_compacting(var) {
|
||||
Structure(Tuple(sub_elems, sub_ext)) => {
|
||||
stack.push(*sub_elems);
|
||||
|
||||
if var == Variable::EMPTY_TUPLE {
|
||||
break;
|
||||
} else {
|
||||
var = *sub_ext;
|
||||
}
|
||||
}
|
||||
|
||||
Alias(_, _, actual_var, _) => {
|
||||
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
|
||||
var = *actual_var;
|
||||
}
|
||||
|
||||
FlexVar(_) | FlexAbleVar(..) => break,
|
||||
|
||||
// TODO investigate apparently this one pops up in the reporting tests!
|
||||
RigidVar(_) | RigidAbleVar(..) => break,
|
||||
|
||||
// Stop on errors in the record
|
||||
Error => break,
|
||||
|
||||
_ => return Err(TupleElemsError),
|
||||
}
|
||||
}
|
||||
|
||||
let it = stack
|
||||
.into_iter()
|
||||
.flat_map(|elems| elems.iter_all())
|
||||
.map(move |(i1, i2)| {
|
||||
let elem_index: &usize = &subs[i1];
|
||||
let variable = subs[i2];
|
||||
|
||||
(*elem_index, variable)
|
||||
});
|
||||
|
||||
Ok((it, var))
|
||||
}
|
||||
|
||||
pub fn gather_fields(
|
||||
subs: &Subs,
|
||||
other_fields: RecordFields,
|
||||
|
@ -4275,6 +4557,20 @@ pub fn gather_fields(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn gather_tuple_elems(
|
||||
subs: &Subs,
|
||||
other_elems: TupleElems,
|
||||
var: Variable,
|
||||
) -> Result<TupleStructure, TupleElemsError> {
|
||||
let (it, ext) = gather_tuple_elems_unsorted_iter(subs, other_elems, var)?;
|
||||
|
||||
let mut result: Vec<_> = it.collect();
|
||||
|
||||
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
Ok(TupleStructure { elems: result, ext })
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GatherTagsError {
|
||||
NotATagUnion(Variable),
|
||||
|
@ -4446,6 +4742,12 @@ fn instantiate_lambda_sets_as_unspecialized(
|
|||
stack.push(x.as_inner_mut());
|
||||
}
|
||||
}
|
||||
Type::Tuple(elems, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, x) in elems.iter_mut() {
|
||||
stack.push(x);
|
||||
}
|
||||
}
|
||||
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, ts) in tags {
|
||||
|
|
|
@ -10,8 +10,8 @@ use roc_types::num::{FloatWidth, IntLitWidth, NumericRange};
|
|||
use roc_types::subs::Content::{self, *};
|
||||
use roc_types::subs::{
|
||||
AliasVariables, Descriptor, ErrorTypeContext, FlatType, GetSubsSlice, LambdaSet, Mark,
|
||||
OptVariable, RecordFields, Subs, SubsIndex, SubsSlice, TagExt, UlsOfVar, UnionLabels,
|
||||
UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
OptVariable, RecordFields, Subs, SubsIndex, SubsSlice, TagExt, TupleElems, UlsOfVar,
|
||||
UnionLabels, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_types::types::{
|
||||
AliasKind, DoesNotImplementAbility, ErrorType, Mismatch, Polarity, RecordField, Uls,
|
||||
|
@ -2029,6 +2029,118 @@ fn unify_record<M: MetaCollector>(
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn unify_tuple<M: MetaCollector>(
|
||||
env: &mut Env,
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
elems1: TupleElems,
|
||||
ext1: Variable,
|
||||
elems2: TupleElems,
|
||||
ext2: Variable,
|
||||
) -> Outcome<M> {
|
||||
let subs = &mut env.subs;
|
||||
|
||||
let (separate, ext1, ext2) = separate_tuple_elems(subs, elems1, ext1, elems2, ext2);
|
||||
|
||||
let shared_elems = separate.in_both;
|
||||
|
||||
if separate.only_in_1.is_empty() {
|
||||
if separate.only_in_2.is_empty() {
|
||||
// these variable will be the empty tuple, but we must still unify them
|
||||
let ext_outcome = unify_pool(env, pool, ext1, ext2, ctx.mode);
|
||||
|
||||
if !ext_outcome.mismatches.is_empty() {
|
||||
return ext_outcome;
|
||||
}
|
||||
|
||||
let mut field_outcome =
|
||||
unify_shared_tuple_elems(env, pool, ctx, shared_elems, OtherTupleElems::None, ext1);
|
||||
|
||||
field_outcome.union(ext_outcome);
|
||||
|
||||
field_outcome
|
||||
} else {
|
||||
let only_in_2 = TupleElems::insert_into_subs(subs, separate.only_in_2);
|
||||
let flat_type = FlatType::Tuple(only_in_2, ext2);
|
||||
let sub_record = fresh(env, pool, ctx, Structure(flat_type));
|
||||
let ext_outcome = unify_pool(env, pool, ext1, sub_record, ctx.mode);
|
||||
|
||||
if !ext_outcome.mismatches.is_empty() {
|
||||
return ext_outcome;
|
||||
}
|
||||
|
||||
let mut field_outcome = unify_shared_tuple_elems(
|
||||
env,
|
||||
pool,
|
||||
ctx,
|
||||
shared_elems,
|
||||
OtherTupleElems::None,
|
||||
sub_record,
|
||||
);
|
||||
|
||||
field_outcome.union(ext_outcome);
|
||||
|
||||
field_outcome
|
||||
}
|
||||
} else if separate.only_in_2.is_empty() {
|
||||
let only_in_1 = TupleElems::insert_into_subs(subs, separate.only_in_1);
|
||||
let flat_type = FlatType::Tuple(only_in_1, ext1);
|
||||
let sub_record = fresh(env, pool, ctx, Structure(flat_type));
|
||||
let ext_outcome = unify_pool(env, pool, sub_record, ext2, ctx.mode);
|
||||
|
||||
if !ext_outcome.mismatches.is_empty() {
|
||||
return ext_outcome;
|
||||
}
|
||||
|
||||
let mut field_outcome = unify_shared_tuple_elems(
|
||||
env,
|
||||
pool,
|
||||
ctx,
|
||||
shared_elems,
|
||||
OtherTupleElems::None,
|
||||
sub_record,
|
||||
);
|
||||
|
||||
field_outcome.union(ext_outcome);
|
||||
|
||||
field_outcome
|
||||
} else {
|
||||
let only_in_1 = TupleElems::insert_into_subs(subs, separate.only_in_1);
|
||||
let only_in_2 = TupleElems::insert_into_subs(subs, separate.only_in_2);
|
||||
|
||||
let other_fields = OtherTupleElems::Other(only_in_1, only_in_2);
|
||||
|
||||
let ext = fresh(env, pool, ctx, Content::FlexVar(None));
|
||||
let flat_type1 = FlatType::Tuple(only_in_1, ext);
|
||||
let flat_type2 = FlatType::Tuple(only_in_2, ext);
|
||||
|
||||
let sub1 = fresh(env, pool, ctx, Structure(flat_type1));
|
||||
let sub2 = fresh(env, pool, ctx, Structure(flat_type2));
|
||||
|
||||
let rec1_outcome = unify_pool(env, pool, ext1, sub2, ctx.mode);
|
||||
if !rec1_outcome.mismatches.is_empty() {
|
||||
return rec1_outcome;
|
||||
}
|
||||
|
||||
let rec2_outcome = unify_pool(env, pool, sub1, ext2, ctx.mode);
|
||||
if !rec2_outcome.mismatches.is_empty() {
|
||||
return rec2_outcome;
|
||||
}
|
||||
|
||||
let mut field_outcome =
|
||||
unify_shared_tuple_elems(env, pool, ctx, shared_elems, other_fields, ext);
|
||||
|
||||
field_outcome
|
||||
.mismatches
|
||||
.reserve(rec1_outcome.mismatches.len() + rec2_outcome.mismatches.len());
|
||||
field_outcome.union(rec1_outcome);
|
||||
field_outcome.union(rec2_outcome);
|
||||
|
||||
field_outcome
|
||||
}
|
||||
}
|
||||
|
||||
enum OtherFields {
|
||||
None,
|
||||
Other(RecordFields, RecordFields),
|
||||
|
@ -2172,6 +2284,89 @@ fn unify_shared_fields<M: MetaCollector>(
|
|||
}
|
||||
}
|
||||
|
||||
enum OtherTupleElems {
|
||||
None,
|
||||
Other(TupleElems, TupleElems),
|
||||
}
|
||||
|
||||
type SharedTupleElems = Vec<(usize, (Variable, Variable))>;
|
||||
|
||||
#[must_use]
|
||||
fn unify_shared_tuple_elems<M: MetaCollector>(
|
||||
env: &mut Env,
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
shared_elems: SharedTupleElems,
|
||||
other_elems: OtherTupleElems,
|
||||
ext: Variable,
|
||||
) -> Outcome<M> {
|
||||
let mut matching_elems = Vec::with_capacity(shared_elems.len());
|
||||
let num_shared_elems = shared_elems.len();
|
||||
|
||||
let mut whole_outcome = Outcome::default();
|
||||
|
||||
for (name, (actual, expected)) in shared_elems {
|
||||
let local_outcome = unify_pool(env, pool, actual, expected, ctx.mode);
|
||||
|
||||
if local_outcome.mismatches.is_empty() {
|
||||
let actual = choose_merged_var(env.subs, actual, expected);
|
||||
|
||||
matching_elems.push((name, actual));
|
||||
whole_outcome.union(local_outcome);
|
||||
}
|
||||
}
|
||||
|
||||
if num_shared_elems == matching_elems.len() {
|
||||
// pull elems in from the ext_var
|
||||
|
||||
let (ext_elems, new_ext_var) = TupleElems::empty().sorted_iterator_and_ext(env.subs, ext);
|
||||
let ext_elems: Vec<_> = ext_elems.into_iter().collect();
|
||||
|
||||
let elems: TupleElems = match other_elems {
|
||||
OtherTupleElems::None => {
|
||||
if ext_elems.is_empty() {
|
||||
TupleElems::insert_into_subs(env.subs, matching_elems)
|
||||
} else {
|
||||
let all_elems = merge_sorted(matching_elems, ext_elems);
|
||||
TupleElems::insert_into_subs(env.subs, all_elems)
|
||||
}
|
||||
}
|
||||
OtherTupleElems::Other(other1, other2) => {
|
||||
let mut all_elems = merge_sorted(matching_elems, ext_elems);
|
||||
all_elems = merge_sorted(
|
||||
all_elems,
|
||||
other1.iter_all().map(|(i1, i2)| {
|
||||
let elem_index: usize = env.subs[i1];
|
||||
let variable = env.subs[i2];
|
||||
|
||||
(elem_index, variable)
|
||||
}),
|
||||
);
|
||||
|
||||
all_elems = merge_sorted(
|
||||
all_elems,
|
||||
other2.iter_all().map(|(i1, i2)| {
|
||||
let elem_index: usize = env.subs[i1];
|
||||
let variable = env.subs[i2];
|
||||
|
||||
(elem_index, variable)
|
||||
}),
|
||||
);
|
||||
|
||||
TupleElems::insert_into_subs(env.subs, all_elems)
|
||||
}
|
||||
};
|
||||
|
||||
let flat_type = FlatType::Tuple(elems, new_ext_var);
|
||||
|
||||
let merge_outcome = merge(env, ctx, Structure(flat_type));
|
||||
whole_outcome.union(merge_outcome);
|
||||
whole_outcome
|
||||
} else {
|
||||
mismatch!("in unify_shared_tuple_elems")
|
||||
}
|
||||
}
|
||||
|
||||
fn separate_record_fields(
|
||||
subs: &Subs,
|
||||
fields1: RecordFields,
|
||||
|
@ -2192,6 +2387,22 @@ fn separate_record_fields(
|
|||
(separate(it1, it2), new_ext1, new_ext2)
|
||||
}
|
||||
|
||||
fn separate_tuple_elems(
|
||||
subs: &Subs,
|
||||
elems1: TupleElems,
|
||||
ext1: Variable,
|
||||
elems2: TupleElems,
|
||||
ext2: Variable,
|
||||
) -> (Separate<usize, Variable>, Variable, Variable) {
|
||||
let (it1, new_ext1) = elems1.sorted_iterator_and_ext(subs, ext1);
|
||||
let (it2, new_ext2) = elems2.sorted_iterator_and_ext(subs, ext2);
|
||||
|
||||
let it1 = it1.collect::<Vec<_>>();
|
||||
let it2 = it2.collect::<Vec<_>>();
|
||||
|
||||
(separate(it1, it2), new_ext1, new_ext2)
|
||||
}
|
||||
|
||||
// TODO: consider combining with `merge_sorted_help` with a `by_key` predicate.
|
||||
// But that might not get inlined!
|
||||
fn merge_sorted_keys<K, I1, I2>(input1: I1, input2: I2) -> Vec<K>
|
||||
|
@ -3020,6 +3231,10 @@ fn unify_flat_type<M: MetaCollector>(
|
|||
unify_record(env, pool, ctx, *fields1, *ext1, *fields2, *ext2)
|
||||
}
|
||||
|
||||
(Tuple(elems1, ext1), Tuple(elems2, ext2)) => {
|
||||
unify_tuple(env, pool, ctx, *elems1, *ext1, *elems2, *ext2)
|
||||
}
|
||||
|
||||
(EmptyTagUnion, EmptyTagUnion) => merge(env, ctx, Structure(*left)),
|
||||
|
||||
(TagUnion(tags, ext), EmptyTagUnion) if tags.is_empty() => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue