mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 04:08:19 +00:00
Merge branch 'main' into specialize-exprs
This commit is contained in:
commit
2e96aca0fd
797 changed files with 17394 additions and 12632 deletions
|
@ -20,3 +20,5 @@ ven_pretty = { path = "../../vendor/pretty" }
|
|||
|
||||
bumpalo.workspace = true
|
||||
static_assertions.workspace = true
|
||||
|
||||
soa.workspace = true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use crate::subs::{
|
||||
self, AliasVariables, Content, FlatType, GetSubsSlice, Label, Subs, SubsIndex, UnionLabels,
|
||||
self, AliasVariables, Content, FlatType, GetSubsSlice, Label, Subs, UnionLabels,
|
||||
UnsortedUnionLabels, Variable,
|
||||
};
|
||||
use crate::types::{
|
||||
|
@ -15,9 +15,7 @@ use roc_module::symbol::{Interns, ModuleId, Symbol};
|
|||
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 = "()";
|
||||
static EFFECTFUL_FUNC: &str = "! : ... => ?";
|
||||
|
||||
/// Requirements for parentheses.
|
||||
///
|
||||
|
@ -152,7 +150,7 @@ fn find_names_needed(
|
|||
|
||||
// User-defined names are already taken.
|
||||
// We must not accidentally generate names that collide with them!
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
let name = subs.field_names[name_index.index()].clone();
|
||||
match names_taken.get(&name) {
|
||||
Some(var) if *var == root => {}
|
||||
Some(_) => {
|
||||
|
@ -180,7 +178,7 @@ fn find_names_needed(
|
|||
);
|
||||
}
|
||||
}
|
||||
Structure(Func(arg_vars, closure_var, ret_var)) => {
|
||||
Structure(Func(arg_vars, closure_var, ret_var, fx_var)) => {
|
||||
for index in arg_vars.into_iter() {
|
||||
let var = subs[index];
|
||||
find_names_needed(
|
||||
|
@ -210,7 +208,17 @@ fn find_names_needed(
|
|||
names_taken,
|
||||
find_under_alias,
|
||||
);
|
||||
|
||||
find_names_needed(
|
||||
*fx_var,
|
||||
subs,
|
||||
roots,
|
||||
root_appearances,
|
||||
names_taken,
|
||||
find_under_alias,
|
||||
);
|
||||
}
|
||||
Structure(EffectfulFunc) => {}
|
||||
Structure(Record(sorted_fields, ext_var)) => {
|
||||
for index in sorted_fields.iter_variables() {
|
||||
let var = subs[index];
|
||||
|
@ -404,8 +412,9 @@ fn find_names_needed(
|
|||
}
|
||||
Error
|
||||
| Structure(EmptyRecord)
|
||||
| Structure(EmptyTuple)
|
||||
| Structure(EmptyTagUnion)
|
||||
| Pure
|
||||
| Effectful
|
||||
| ErasedLambda => {
|
||||
// Errors and empty records don't need names.
|
||||
}
|
||||
|
@ -535,12 +544,12 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
|
||||
match old_content {
|
||||
FlexVar(_) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let name_index = subs.push_field_name(name);
|
||||
let content = FlexVar(Some(name_index));
|
||||
subs.set_content(root, content);
|
||||
}
|
||||
&FlexAbleVar(_, ability) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let name_index = subs.push_field_name(name);
|
||||
let content = FlexAbleVar(Some(name_index), ability);
|
||||
subs.set_content(root, content);
|
||||
}
|
||||
|
@ -549,7 +558,7 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
structure,
|
||||
} => {
|
||||
let structure = *structure;
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let name_index = subs.push_field_name(name);
|
||||
let content = RecursionVar {
|
||||
structure,
|
||||
opt_name: Some(name_index),
|
||||
|
@ -680,24 +689,24 @@ fn write_content<'a>(
|
|||
|
||||
match subs.get_content_without_compacting(var) {
|
||||
FlexVar(Some(name_index)) => {
|
||||
let name = &subs.field_names[name_index.index as usize];
|
||||
let name = &subs.field_names[name_index.index()];
|
||||
buf.push_str(name.as_str())
|
||||
}
|
||||
FlexVar(None) => buf.push_str(WILDCARD),
|
||||
RigidVar(name_index) => {
|
||||
let name = &subs.field_names[name_index.index as usize];
|
||||
let name = &subs.field_names[name_index.index()];
|
||||
buf.push_str(name.as_str())
|
||||
}
|
||||
FlexAbleVar(opt_name_index, abilities) => {
|
||||
let name = opt_name_index
|
||||
.map(|name_index| subs.field_names[name_index.index as usize].as_str())
|
||||
.map(|name_index| subs.field_names[name_index.index()].as_str())
|
||||
.unwrap_or(WILDCARD);
|
||||
let abilities = AbilitySet::from_iter(subs.get_subs_slice(*abilities).iter().copied());
|
||||
ctx.able_variables.push((name, abilities));
|
||||
buf.push_str(name);
|
||||
}
|
||||
RigidAbleVar(name_index, abilities) => {
|
||||
let name = subs.field_names[name_index.index as usize].as_str();
|
||||
let name = subs.field_names[name_index.index()].as_str();
|
||||
let abilities = AbilitySet::from_iter(subs.get_subs_slice(*abilities).iter().copied());
|
||||
ctx.able_variables.push((name, abilities));
|
||||
buf.push_str(name);
|
||||
|
@ -713,7 +722,7 @@ fn write_content<'a>(
|
|||
|
||||
ctx.recursion_structs_to_expand.insert(structure_root);
|
||||
} else {
|
||||
let name = &subs.field_names[name_index.index as usize];
|
||||
let name = &subs.field_names[name_index.index()];
|
||||
buf.push_str(name.as_str())
|
||||
}
|
||||
}
|
||||
|
@ -876,6 +885,12 @@ fn write_content<'a>(
|
|||
// Easy mode 🤠
|
||||
buf.push('?');
|
||||
}
|
||||
Pure => {
|
||||
buf.push_str("->");
|
||||
}
|
||||
Effectful => {
|
||||
buf.push_str("=>");
|
||||
}
|
||||
RangedNumber(range) => {
|
||||
buf.push_str("Range(");
|
||||
for (i, &var) in range.variable_slice().iter().enumerate() {
|
||||
|
@ -1113,19 +1128,20 @@ 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(
|
||||
Func(args, closure, ret, fx) => write_fn(
|
||||
env,
|
||||
ctx,
|
||||
subs.get_subs_slice(*args),
|
||||
*closure,
|
||||
*ret,
|
||||
*fx,
|
||||
subs,
|
||||
buf,
|
||||
parens,
|
||||
pol,
|
||||
),
|
||||
EffectfulFunc => buf.push_str(EFFECTFUL_FUNC),
|
||||
Record(fields, ext_var) => {
|
||||
use crate::types::{gather_fields, RecordStructure};
|
||||
|
||||
|
@ -1220,19 +1236,12 @@ fn write_flat_type<'a>(
|
|||
|
||||
buf.push_str(" )");
|
||||
|
||||
match subs.get_content_without_compacting(ext_var) {
|
||||
Content::Structure(EmptyTuple) => {
|
||||
// This is a closed tuple. We're done!
|
||||
}
|
||||
_ => {
|
||||
// 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, ext_var, subs, buf, parens, pol)
|
||||
}
|
||||
}
|
||||
// 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, ext_var, subs, buf, parens, pol)
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
buf.push('[');
|
||||
|
@ -1460,6 +1469,7 @@ fn write_fn<'a>(
|
|||
args: &[Variable],
|
||||
closure: Variable,
|
||||
ret: Variable,
|
||||
fx: Variable,
|
||||
subs: &'a Subs,
|
||||
buf: &mut String,
|
||||
parens: Parens,
|
||||
|
@ -1483,11 +1493,14 @@ fn write_fn<'a>(
|
|||
}
|
||||
|
||||
if !env.debug.print_lambda_sets {
|
||||
buf.push_str(" -> ");
|
||||
buf.push(' ');
|
||||
write_content(env, ctx, fx, subs, buf, Parens::Unnecessary, Polarity::Neg);
|
||||
buf.push(' ');
|
||||
} else {
|
||||
buf.push_str(" -");
|
||||
write_content(env, ctx, closure, subs, buf, parens, pol);
|
||||
buf.push_str("-> ");
|
||||
write_content(env, ctx, fx, subs, buf, Parens::Unnecessary, Polarity::Neg);
|
||||
buf.push(' ');
|
||||
}
|
||||
|
||||
write_content(env, ctx, ret, subs, buf, Parens::InFn, Polarity::Pos);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ use crate::subs::{
|
|||
VariableSubsSlice,
|
||||
};
|
||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_collections::soa::{Index, Slice};
|
||||
use roc_collections::soa::{index_push_new, slice_extend_new};
|
||||
use roc_collections::VecMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
|
@ -13,6 +13,7 @@ use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
|||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use soa::{Index, Slice};
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use std::path::PathBuf;
|
||||
|
@ -375,6 +376,8 @@ pub enum TypeTag {
|
|||
Index<TypeTag>,
|
||||
/// return type
|
||||
Index<TypeTag>,
|
||||
/// fx type
|
||||
Index<TypeTag>,
|
||||
),
|
||||
/// Closure arguments are implicit
|
||||
ClosureTag {
|
||||
|
@ -418,6 +421,9 @@ pub enum TypeTag {
|
|||
RecursiveTagUnion(Variable, UnionTags, ExtImplicitOpenness),
|
||||
Record(RecordFields),
|
||||
Tuple(TupleElems),
|
||||
// A function fx type
|
||||
Pure,
|
||||
Effectful,
|
||||
}
|
||||
|
||||
/// Look-aside slice of types used in [Types], when the slice does not correspond to the direct
|
||||
|
@ -602,7 +608,7 @@ impl Types {
|
|||
self.tags_slices
|
||||
.extend(repeat(Slice::default()).take(length));
|
||||
|
||||
Slice::extend_new(&mut self.tags, repeat(TypeTag::EmptyRecord).take(length))
|
||||
slice_extend_new(&mut self.tags, repeat(TypeTag::EmptyRecord).take(length))
|
||||
}
|
||||
|
||||
fn reserve_type_tag(&mut self) -> Index<TypeTag> {
|
||||
|
@ -610,7 +616,7 @@ impl Types {
|
|||
|
||||
self.tags_slices.push(Slice::default());
|
||||
|
||||
Index::push_new(&mut self.tags, TypeTag::EmptyRecord)
|
||||
index_push_new(&mut self.tags, TypeTag::EmptyRecord)
|
||||
}
|
||||
|
||||
fn set_type_tag(&mut self, index: Index<TypeTag>, tag: TypeTag, type_slice: Slice<TypeTag>) {
|
||||
|
@ -644,10 +650,10 @@ impl Types {
|
|||
extension: &TypeExtension,
|
||||
) -> (UnionTags, Slice<TypeTag>) {
|
||||
let tag_names_slice =
|
||||
Slice::extend_new(&mut self.tag_names, tags.iter().map(|(n, _)| n.clone()));
|
||||
slice_extend_new(&mut self.tag_names, tags.iter().map(|(n, _)| n.clone()));
|
||||
|
||||
// Store the payload slices in the aside buffer
|
||||
let type_slices = Slice::extend_new(
|
||||
let type_slices = slice_extend_new(
|
||||
&mut self.aside_types_slices,
|
||||
std::iter::repeat(Slice::default()).take(tags.len()),
|
||||
);
|
||||
|
@ -698,7 +704,7 @@ impl Types {
|
|||
Slice::new(slice.start() as _, slice.len() as _)
|
||||
};
|
||||
|
||||
let type_argument_abilities = Slice::extend_new(
|
||||
let type_argument_abilities = slice_extend_new(
|
||||
&mut self.type_arg_abilities,
|
||||
type_arguments
|
||||
.iter()
|
||||
|
@ -706,7 +712,7 @@ impl Types {
|
|||
);
|
||||
|
||||
// TODO: populate correctly
|
||||
let type_argument_regions = Slice::extend_new(
|
||||
let type_argument_regions = slice_extend_new(
|
||||
&mut self.regions,
|
||||
std::iter::repeat(Region::zero()).take(type_arguments.len()),
|
||||
);
|
||||
|
@ -732,10 +738,11 @@ impl Types {
|
|||
arguments: Slice<TypeTag>,
|
||||
lambda_set: Index<TypeTag>,
|
||||
ret: Index<TypeTag>,
|
||||
fx: Index<TypeTag>,
|
||||
) -> Index<TypeTag> {
|
||||
let index = self.reserve_type_tag();
|
||||
|
||||
let tag = TypeTag::Function(lambda_set, ret);
|
||||
let tag = TypeTag::Function(lambda_set, ret, fx);
|
||||
self.set_type_tag(index, tag, arguments);
|
||||
index
|
||||
}
|
||||
|
@ -747,19 +754,20 @@ impl Types {
|
|||
Type::EmptyTagUnion => {
|
||||
self.set_type_tag(index, TypeTag::EmptyTagUnion, Slice::default())
|
||||
}
|
||||
Type::Function(arguments, lambda_set, return_type) => {
|
||||
Type::Function(arguments, lambda_set, return_type, fx_type) => {
|
||||
let argument_slice = self.from_old_type_slice(arguments.iter());
|
||||
|
||||
let tag = TypeTag::Function(
|
||||
self.from_old_type(lambda_set),
|
||||
self.from_old_type(return_type),
|
||||
self.from_old_type(fx_type),
|
||||
);
|
||||
|
||||
self.set_type_tag(index, tag, argument_slice)
|
||||
}
|
||||
Type::Apply(symbol, arguments, region) => {
|
||||
let type_argument_regions =
|
||||
Slice::extend_new(&mut self.regions, arguments.iter().map(|t| t.region));
|
||||
slice_extend_new(&mut self.regions, arguments.iter().map(|t| t.region));
|
||||
|
||||
let type_slice = {
|
||||
let slice = self.reserve_type_tags(arguments.len());
|
||||
|
@ -835,17 +843,17 @@ impl Types {
|
|||
slice
|
||||
};
|
||||
|
||||
let field_types = Slice::extend_new(
|
||||
let field_types = slice_extend_new(
|
||||
&mut self.field_types,
|
||||
fields.values().map(|f| f.map(|_| ())),
|
||||
);
|
||||
|
||||
let field_names = Slice::extend_new(&mut self.field_names, fields.keys().cloned());
|
||||
let field_names = slice_extend_new(&mut self.field_names, fields.keys().cloned());
|
||||
|
||||
let record_fields = RecordFields {
|
||||
length: fields.len() as u16,
|
||||
field_names_start: field_names.start() as u32,
|
||||
variables_start: field_type_slice.start() as u32,
|
||||
variables_start: field_type_slice.start(),
|
||||
field_types_start: field_types.start() as u32,
|
||||
};
|
||||
|
||||
|
@ -870,11 +878,11 @@ impl Types {
|
|||
};
|
||||
|
||||
let elem_index_slice =
|
||||
Slice::extend_new(&mut self.tuple_elem_indices, elems.iter().map(|(i, _)| *i));
|
||||
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,
|
||||
variables_start: elem_type_slice.start(),
|
||||
elem_index_start: elem_index_slice.start() as u32,
|
||||
};
|
||||
|
||||
|
@ -902,7 +910,7 @@ impl Types {
|
|||
infer_ext_in_output_types,
|
||||
}) => {
|
||||
let type_argument_regions =
|
||||
Slice::extend_new(&mut self.regions, type_arguments.iter().map(|t| t.region));
|
||||
slice_extend_new(&mut self.regions, type_arguments.iter().map(|t| t.region));
|
||||
|
||||
let type_arguments_slice = {
|
||||
let slice = self.reserve_type_tags(type_arguments.len());
|
||||
|
@ -936,7 +944,7 @@ impl Types {
|
|||
Slice::new(slice.start() as _, slice.len() as _)
|
||||
};
|
||||
|
||||
let type_argument_abilities = Slice::extend_new(
|
||||
let type_argument_abilities = slice_extend_new(
|
||||
&mut self.type_arg_abilities,
|
||||
type_arguments
|
||||
.iter()
|
||||
|
@ -951,7 +959,7 @@ impl Types {
|
|||
infer_ext_in_output_variables: infer_ext_in_output_slice,
|
||||
};
|
||||
|
||||
let shared = Index::push_new(&mut self.aliases, alias_shared);
|
||||
let shared = index_push_new(&mut self.aliases, alias_shared);
|
||||
|
||||
let tag = TypeTag::DelayedAlias { shared };
|
||||
|
||||
|
@ -982,7 +990,7 @@ impl Types {
|
|||
infer_ext_in_output_types,
|
||||
);
|
||||
|
||||
let shared = Index::push_new(&mut self.aliases, alias_shared);
|
||||
let shared = index_push_new(&mut self.aliases, alias_shared);
|
||||
let actual = self.from_old_type(actual);
|
||||
|
||||
let tag = match kind {
|
||||
|
@ -1000,6 +1008,8 @@ impl Types {
|
|||
self.set_type_tag(index, TypeTag::RangedNumber(*range), Slice::default())
|
||||
}
|
||||
Type::Error => self.set_type_tag(index, TypeTag::Error, Slice::default()),
|
||||
Type::Pure => self.set_type_tag(index, TypeTag::Pure, Slice::default()),
|
||||
Type::Effectful => self.set_type_tag(index, TypeTag::Effectful, Slice::default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1056,7 +1066,7 @@ impl Types {
|
|||
lambda_set_variables: new_lambda_set_variables,
|
||||
infer_ext_in_output_variables: new_infer_ext_in_output_variables,
|
||||
};
|
||||
Index::push_new(&mut self.aliases, new_shared)
|
||||
index_push_new(&mut self.aliases, new_shared)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -1064,7 +1074,7 @@ impl Types {
|
|||
($union_tags:expr) => {{
|
||||
let (tags, payload_slices) = self.union_tag_slices($union_tags);
|
||||
|
||||
let new_payload_slices = Slice::extend_new(
|
||||
let new_payload_slices = slice_extend_new(
|
||||
&mut self.aside_types_slices,
|
||||
std::iter::repeat(Slice::default()).take(payload_slices.len()),
|
||||
);
|
||||
|
@ -1092,14 +1102,15 @@ impl Types {
|
|||
Variable(v) => (Variable(subst!(v)), Default::default()),
|
||||
EmptyRecord => (EmptyRecord, Default::default()),
|
||||
EmptyTagUnion => (EmptyTagUnion, Default::default()),
|
||||
Function(clos, ret) => {
|
||||
Function(clos, ret, fx) => {
|
||||
let args = self.get_type_arguments(typ);
|
||||
|
||||
let new_args = defer_slice!(args);
|
||||
let new_clos = defer!(clos);
|
||||
let new_ret = defer!(ret);
|
||||
let new_fx = defer!(fx);
|
||||
|
||||
(Function(new_clos, new_ret), new_args)
|
||||
(Function(new_clos, new_ret, new_fx), new_args)
|
||||
}
|
||||
ClosureTag {
|
||||
name,
|
||||
|
@ -1251,6 +1262,8 @@ impl Types {
|
|||
}
|
||||
RangedNumber(range) => (RangedNumber(range), Default::default()),
|
||||
Error => (Error, Default::default()),
|
||||
Pure => (Pure, Default::default()),
|
||||
Effectful => (Effectful, Default::default()),
|
||||
};
|
||||
|
||||
self.set_type_tag(dest_index, tag, args);
|
||||
|
@ -1280,8 +1293,8 @@ mod debug_types {
|
|||
};
|
||||
|
||||
use super::{TypeTag, Types};
|
||||
use roc_collections::soa::{Index, Slice};
|
||||
use roc_module::ident::TagName;
|
||||
use soa::{Index, Slice};
|
||||
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
|
||||
|
||||
pub struct DebugTag<'a>(pub &'a Types, pub Index<TypeTag>);
|
||||
|
@ -1327,7 +1340,7 @@ mod debug_types {
|
|||
let group = match types[tag] {
|
||||
TypeTag::EmptyRecord => f.text("{}"),
|
||||
TypeTag::EmptyTagUnion => f.text("[]"),
|
||||
TypeTag::Function(clos, ret) => {
|
||||
TypeTag::Function(clos, ret, fx) => {
|
||||
let args = types.get_type_arguments(tag);
|
||||
maybe_paren!(
|
||||
Free,
|
||||
|
@ -1338,6 +1351,8 @@ mod debug_types {
|
|||
)
|
||||
.append(f.text(" -"))
|
||||
.append(typ(types, f, Free, clos))
|
||||
.append(f.text(" -"))
|
||||
.append(typ(types, f, Free, fx))
|
||||
.append(f.text("->"))
|
||||
.append(f.line())
|
||||
.append(typ(types, f, Arg, ret))
|
||||
|
@ -1419,8 +1434,8 @@ mod debug_types {
|
|||
let (names, kind, tys) = types.record_fields_slices(fields);
|
||||
let fmt_fields = names
|
||||
.into_iter()
|
||||
.zip(kind.into_iter())
|
||||
.zip(tys.into_iter())
|
||||
.zip(kind)
|
||||
.zip(tys)
|
||||
.map(|((name, kind), ty)| {
|
||||
let (name, kind) = (&types[name], types[kind]);
|
||||
let fmt_kind = f.text(match kind {
|
||||
|
@ -1458,6 +1473,8 @@ mod debug_types {
|
|||
.align(),
|
||||
)
|
||||
}
|
||||
TypeTag::Pure => f.text("Pure"),
|
||||
TypeTag::Effectful => f.text("Effectful"),
|
||||
};
|
||||
group.group()
|
||||
}
|
||||
|
@ -1482,19 +1499,19 @@ mod debug_types {
|
|||
ext_slice: Slice<TypeTag>,
|
||||
) -> DocBuilder<'a, Arena<'a>> {
|
||||
let (tags, payload_slices) = types.union_tag_slices(tags);
|
||||
let fmt_tags =
|
||||
tags.into_iter()
|
||||
.zip(payload_slices.into_iter())
|
||||
.map(|(tag, payload_slice_index)| {
|
||||
let payload_slice = types[payload_slice_index];
|
||||
let fmt_payloads = payload_slice
|
||||
.into_iter()
|
||||
.map(|p| typ(types, f, TPrec::Arg, p));
|
||||
let iter = Some(f.text(types[tag].0.to_string()))
|
||||
.into_iter()
|
||||
.chain(fmt_payloads);
|
||||
f.intersperse(iter, f.text(" "))
|
||||
});
|
||||
let fmt_tags = tags
|
||||
.into_iter()
|
||||
.zip(payload_slices)
|
||||
.map(|(tag, payload_slice_index)| {
|
||||
let payload_slice = types[payload_slice_index];
|
||||
let fmt_payloads = payload_slice
|
||||
.into_iter()
|
||||
.map(|p| typ(types, f, TPrec::Arg, p));
|
||||
let iter = Some(f.text(types[tag].0.to_string()))
|
||||
.into_iter()
|
||||
.chain(fmt_payloads);
|
||||
f.intersperse(iter, f.text(" "))
|
||||
});
|
||||
|
||||
prefix.append(f.text("[")).append(
|
||||
f.intersperse(fmt_tags, f.reflow(", "))
|
||||
|
@ -1523,7 +1540,7 @@ mod debug_types {
|
|||
let args = types.get_type_arguments(tag);
|
||||
let fmt_args = args
|
||||
.into_iter()
|
||||
.zip(type_argument_abilities.into_iter())
|
||||
.zip(type_argument_abilities)
|
||||
.map(|(arg, abilities)| {
|
||||
let abilities = &types[abilities];
|
||||
let arg = typ(types, f, Arg, arg);
|
||||
|
@ -1652,8 +1669,8 @@ impl std::ops::Index<Slice<AsideTypeSlice>> for Types {
|
|||
pub enum Type {
|
||||
EmptyRec,
|
||||
EmptyTagUnion,
|
||||
/// 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>),
|
||||
/// A function. The types of its arguments, size of its closure, its return value, then the fx type.
|
||||
Function(Vec<Type>, Box<Type>, Box<Type>, Box<Type>),
|
||||
Record(SendMap<Lowercase, RecordField<Type>>, TypeExtension),
|
||||
Tuple(VecMap<usize, Type>, TypeExtension),
|
||||
TagUnion(Vec<(TagName, Vec<Type>)>, TypeExtension),
|
||||
|
@ -1685,6 +1702,9 @@ pub enum Type {
|
|||
Apply(Symbol, Vec<Loc<Type>>, Region),
|
||||
Variable(Variable),
|
||||
RangedNumber(NumericRange),
|
||||
/// A function's fx type
|
||||
Pure,
|
||||
Effectful,
|
||||
/// A type error, which will code gen to a runtime error
|
||||
Error,
|
||||
}
|
||||
|
@ -1729,8 +1749,8 @@ impl Clone for Type {
|
|||
match self {
|
||||
Self::EmptyRec => Self::EmptyRec,
|
||||
Self::EmptyTagUnion => Self::EmptyTagUnion,
|
||||
Self::Function(arg0, arg1, arg2) => {
|
||||
Self::Function(arg0.clone(), arg1.clone(), arg2.clone())
|
||||
Self::Function(arg0, arg1, arg2, arg3) => {
|
||||
Self::Function(arg0.clone(), arg1.clone(), arg2.clone(), arg3.clone())
|
||||
}
|
||||
Self::Record(arg0, arg1) => Self::Record(arg0.clone(), arg1.clone()),
|
||||
Self::Tuple(arg0, arg1) => Self::Tuple(arg0.clone(), arg1.clone()),
|
||||
|
@ -1773,6 +1793,8 @@ impl Clone for Type {
|
|||
Self::Variable(arg0) => Self::Variable(*arg0),
|
||||
Self::RangedNumber(arg1) => Self::RangedNumber(*arg1),
|
||||
Self::Error => Self::Error,
|
||||
Type::Pure => Self::Pure,
|
||||
Type::Effectful => Self::Effectful,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1885,7 +1907,7 @@ impl fmt::Debug for Type {
|
|||
match self {
|
||||
Type::EmptyRec => write!(f, "{{}}"),
|
||||
Type::EmptyTagUnion => write!(f, "[]"),
|
||||
Type::Function(args, closure, ret) => {
|
||||
Type::Function(args, closure, ret, fx) => {
|
||||
write!(f, "Fn(")?;
|
||||
|
||||
for (index, arg) in args.iter().enumerate() {
|
||||
|
@ -1897,7 +1919,8 @@ impl fmt::Debug for Type {
|
|||
}
|
||||
|
||||
write!(f, " |{closure:?}|")?;
|
||||
write!(f, " -> ")?;
|
||||
write!(f, " -{fx:?}")?;
|
||||
write!(f, "-> ")?;
|
||||
|
||||
ret.fmt(f)?;
|
||||
|
||||
|
@ -2136,13 +2159,15 @@ impl fmt::Debug for Type {
|
|||
Type::UnspecializedLambdaSet { unspecialized } => {
|
||||
write!(f, "{unspecialized:?}")
|
||||
}
|
||||
Type::Pure => write!(f, "->"),
|
||||
Type::Effectful => write!(f, "=>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn arity(&self) -> usize {
|
||||
if let Type::Function(args, _, _) = self {
|
||||
if let Type::Function(args, _, _, _) = self {
|
||||
args.len()
|
||||
} else {
|
||||
0
|
||||
|
@ -2186,10 +2211,11 @@ impl Type {
|
|||
*typ = replacement.clone();
|
||||
}
|
||||
}
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
stack.extend(args);
|
||||
stack.push(closure);
|
||||
stack.push(ret);
|
||||
stack.push(fx);
|
||||
}
|
||||
ClosureTag {
|
||||
name: _,
|
||||
|
@ -2298,7 +2324,7 @@ impl Type {
|
|||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Error => {}
|
||||
EmptyRec | EmptyTagUnion | Error | Pure | Effectful => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2315,10 +2341,11 @@ impl Type {
|
|||
*v = *replacement;
|
||||
}
|
||||
}
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
stack.extend(args);
|
||||
stack.push(closure);
|
||||
stack.push(ret);
|
||||
stack.push(fx);
|
||||
}
|
||||
ClosureTag {
|
||||
name: _,
|
||||
|
@ -2420,7 +2447,7 @@ impl Type {
|
|||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Error => {}
|
||||
EmptyRec | EmptyTagUnion | Error | Pure | Effectful => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2436,12 +2463,13 @@ impl Type {
|
|||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
for arg in args {
|
||||
arg.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||
}
|
||||
closure.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||
ret.substitute_alias(rep_symbol, rep_args, actual)
|
||||
ret.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||
fx.substitute_alias(rep_symbol, rep_args, actual)
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => match ext {
|
||||
TypeExtension::Open(ext, _) => ext.substitute_alias(rep_symbol, rep_args, actual),
|
||||
|
@ -2535,7 +2563,13 @@ impl Type {
|
|||
}
|
||||
RangedNumber(_) => Ok(()),
|
||||
UnspecializedLambdaSet { .. } => Ok(()),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => Ok(()),
|
||||
EmptyRec
|
||||
| EmptyTagUnion
|
||||
| ClosureTag { .. }
|
||||
| Error
|
||||
| Variable(_)
|
||||
| Pure
|
||||
| Effectful => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2550,7 +2584,7 @@ impl Type {
|
|||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, _fx) => {
|
||||
ret.contains_symbol(rep_symbol)
|
||||
|| closure.contains_symbol(rep_symbol)
|
||||
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
||||
|
@ -2598,7 +2632,13 @@ impl Type {
|
|||
UnspecializedLambdaSet {
|
||||
unspecialized: Uls(_, sym, _),
|
||||
} => *sym == rep_symbol,
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => false,
|
||||
EmptyRec
|
||||
| EmptyTagUnion
|
||||
| ClosureTag { .. }
|
||||
| Error
|
||||
| Variable(_)
|
||||
| Pure
|
||||
| Effectful => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2614,10 +2654,11 @@ impl Type {
|
|||
|
||||
match self {
|
||||
Variable(v) => *v == rep_variable,
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
ret.contains_variable(rep_variable)
|
||||
|| closure.contains_variable(rep_variable)
|
||||
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
||||
|| fx.contains_variable(rep_variable)
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => Self::contains_variable_ext(ext, rep_variable),
|
||||
ClosureTag {
|
||||
|
@ -2659,7 +2700,7 @@ impl Type {
|
|||
.iter()
|
||||
.any(|arg| arg.value.contains_variable(rep_variable)),
|
||||
RangedNumber(_) => false,
|
||||
EmptyRec | EmptyTagUnion | Error => false,
|
||||
EmptyRec | EmptyTagUnion | Error | Pure | Effectful => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2760,8 +2801,11 @@ impl Type {
|
|||
}
|
||||
TypeExtension::Closed => fields.values().all(|field| field.as_inner().is_narrow()),
|
||||
},
|
||||
Type::Function(args, clos, ret) => {
|
||||
args.iter().all(|a| a.is_narrow()) && clos.is_narrow() && ret.is_narrow()
|
||||
Type::Function(args, clos, ret, fx) => {
|
||||
args.iter().all(|a| a.is_narrow())
|
||||
&& clos.is_narrow()
|
||||
&& ret.is_narrow()
|
||||
&& fx.is_narrow()
|
||||
}
|
||||
// Lists and sets are morally two-tagged unions, as they can be empty
|
||||
Type::Apply(Symbol::LIST_LIST | Symbol::SET_SET, _, _) => false,
|
||||
|
@ -2800,12 +2844,13 @@ fn instantiate_aliases<'a, F>(
|
|||
use Type::*;
|
||||
|
||||
match typ {
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
for arg in args {
|
||||
instantiate_aliases(arg, region, aliases, ctx);
|
||||
}
|
||||
instantiate_aliases(closure, region, aliases, ctx);
|
||||
instantiate_aliases(ret, region, aliases, ctx);
|
||||
instantiate_aliases(fx, region, aliases, ctx);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
|
@ -2964,7 +3009,7 @@ fn instantiate_aliases<'a, F>(
|
|||
}
|
||||
RangedNumber(_) => {}
|
||||
UnspecializedLambdaSet { .. } => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Pure | Effectful => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2976,9 +3021,10 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
|||
|
||||
while let Some(tipe) = stack.pop() {
|
||||
match tipe {
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
stack.push(ret);
|
||||
stack.push(closure);
|
||||
stack.push(fx);
|
||||
stack.extend(args);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
|
@ -3025,7 +3071,13 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
|||
} => {
|
||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||
}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
EmptyRec
|
||||
| EmptyTagUnion
|
||||
| ClosureTag { .. }
|
||||
| Error
|
||||
| Variable(_)
|
||||
| Pure
|
||||
| Effectful => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3045,12 +3097,13 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
accum.insert(*v);
|
||||
}
|
||||
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
for arg in args {
|
||||
variables_help(arg, accum);
|
||||
}
|
||||
variables_help(closure, accum);
|
||||
variables_help(ret, accum);
|
||||
variables_help(fx, accum);
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, field) in fields {
|
||||
|
@ -3146,6 +3199,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
variables_help(&x.value, accum);
|
||||
}
|
||||
}
|
||||
Pure | Effectful => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3174,7 +3228,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
accum.type_variables.insert(*v);
|
||||
}
|
||||
|
||||
Function(args, closure, ret) => {
|
||||
Function(args, closure, ret, fx) => {
|
||||
for arg in args {
|
||||
variables_help_detailed(arg, accum);
|
||||
}
|
||||
|
@ -3185,6 +3239,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
}
|
||||
|
||||
variables_help_detailed(ret, accum);
|
||||
variables_help_detailed(fx, accum);
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, field) in fields {
|
||||
|
@ -3288,6 +3343,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
variables_help_detailed(&x.value, accum);
|
||||
}
|
||||
}
|
||||
Pure | Effectful => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3392,6 +3448,7 @@ pub enum Reason {
|
|||
foreign_symbol: ForeignSymbol,
|
||||
arg_index: HumanIndex,
|
||||
},
|
||||
Stmt(Option<Symbol>),
|
||||
FloatLiteral,
|
||||
IntLiteral,
|
||||
NumLiteral,
|
||||
|
@ -3425,6 +3482,7 @@ pub enum Reason {
|
|||
},
|
||||
CrashArg,
|
||||
ImportParams(ModuleId),
|
||||
FunctionOutput,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
|
@ -3474,6 +3532,7 @@ pub enum Category {
|
|||
|
||||
Expect,
|
||||
Dbg,
|
||||
Return,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -3608,6 +3667,7 @@ pub enum ErrorType {
|
|||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexVar(Lowercase),
|
||||
RigidVar(Lowercase),
|
||||
EffectfulFunc,
|
||||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexAbleVar(Lowercase, AbilitySet),
|
||||
RigidAbleVar(Lowercase, AbilitySet),
|
||||
|
@ -3620,12 +3680,23 @@ pub enum ErrorType {
|
|||
TypeExt,
|
||||
Polarity,
|
||||
),
|
||||
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
||||
Function(
|
||||
Vec<ErrorType>,
|
||||
Box<ErrorType>,
|
||||
ErrorFunctionFx,
|
||||
Box<ErrorType>,
|
||||
),
|
||||
Alias(Symbol, Vec<ErrorType>, Box<ErrorType>, AliasKind),
|
||||
Range(Vec<ErrorType>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ErrorFunctionFx {
|
||||
Pure,
|
||||
Effectful,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ErrorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO remove clone
|
||||
|
@ -3671,7 +3742,7 @@ impl ErrorType {
|
|||
.for_each(|(_, ts)| ts.iter().for_each(|t| t.add_names(taken)));
|
||||
ext.add_names(taken);
|
||||
}
|
||||
Function(args, capt, ret) => {
|
||||
Function(args, capt, _fx, ret) => {
|
||||
args.iter().for_each(|t| t.add_names(taken));
|
||||
capt.add_names(taken);
|
||||
ret.add_names(taken);
|
||||
|
@ -3687,6 +3758,7 @@ impl ErrorType {
|
|||
t.add_names(taken);
|
||||
});
|
||||
}
|
||||
EffectfulFunc => {}
|
||||
Error => {}
|
||||
}
|
||||
}
|
||||
|
@ -3757,7 +3829,7 @@ fn write_error_type_help(
|
|||
}
|
||||
}
|
||||
}
|
||||
Function(arguments, _closure, result) => {
|
||||
Function(arguments, _closure, fx, result) => {
|
||||
let write_parens = parens != Parens::Unnecessary;
|
||||
|
||||
if write_parens {
|
||||
|
@ -3773,7 +3845,10 @@ fn write_error_type_help(
|
|||
}
|
||||
}
|
||||
|
||||
buf.push_str(" -> ");
|
||||
match fx {
|
||||
ErrorFunctionFx::Pure => buf.push_str(" -> "),
|
||||
ErrorFunctionFx::Effectful => buf.push_str(" => "),
|
||||
}
|
||||
|
||||
write_error_type_help(interns, *result, buf, Parens::InFn);
|
||||
|
||||
|
@ -3836,6 +3911,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
EffectfulFunc => buf.push_str("EffectfulFunc"),
|
||||
Type(symbol, arguments) => {
|
||||
let write_parens = parens == Parens::InTypeParam && !arguments.is_empty();
|
||||
|
||||
|
@ -3907,7 +3983,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Function(arguments, _closure, result) => {
|
||||
Function(arguments, _closure, fx, result) => {
|
||||
let write_parens = parens != Parens::Unnecessary;
|
||||
|
||||
if write_parens {
|
||||
|
@ -3923,7 +3999,10 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
}
|
||||
}
|
||||
|
||||
buf.push_str(" -> ");
|
||||
match fx {
|
||||
ErrorFunctionFx::Pure => buf.push_str(" -> "),
|
||||
ErrorFunctionFx::Effectful => buf.push_str(" => "),
|
||||
}
|
||||
|
||||
write_debug_error_type_help(*result, buf, Parens::InFn);
|
||||
|
||||
|
@ -4411,7 +4490,7 @@ fn instantiate_lambda_sets_as_unspecialized(
|
|||
match typ {
|
||||
Type::EmptyRec => {}
|
||||
Type::EmptyTagUnion => {}
|
||||
Type::Function(args, lambda_set, ret) => {
|
||||
Type::Function(args, lambda_set, ret, fx) => {
|
||||
debug_assert!(
|
||||
matches!(**lambda_set, Type::Variable(..)),
|
||||
"lambda set already bound"
|
||||
|
@ -4419,6 +4498,7 @@ fn instantiate_lambda_sets_as_unspecialized(
|
|||
|
||||
**lambda_set = new_uls();
|
||||
stack.push(ret);
|
||||
stack.push(fx);
|
||||
stack.extend(args.iter_mut().rev());
|
||||
}
|
||||
Type::Record(fields, ext) => {
|
||||
|
@ -4487,6 +4567,7 @@ fn instantiate_lambda_sets_as_unspecialized(
|
|||
Type::Variable(_) => {}
|
||||
Type::RangedNumber(_) => {}
|
||||
Type::Error => {}
|
||||
Type::Pure | Type::Effectful => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4501,16 +4582,20 @@ mod test {
|
|||
let l1 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l2 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l3 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let fx1 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let fx2 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let fx3 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let mut typ = Type::Function(
|
||||
vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec))],
|
||||
vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec), fx1)],
|
||||
l1,
|
||||
Box::new(Type::TagUnion(
|
||||
vec![(
|
||||
TagName("A".into()),
|
||||
vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec))],
|
||||
vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec), fx2)],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)),
|
||||
fx3,
|
||||
);
|
||||
|
||||
let able_var = var_store.fresh();
|
||||
|
@ -4531,11 +4616,11 @@ mod test {
|
|||
}
|
||||
|
||||
match typ {
|
||||
Type::Function(args, l1, ret) => {
|
||||
Type::Function(args, l1, ret, _fx) => {
|
||||
check_uls!(*l1, 1);
|
||||
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l2, ret)] => {
|
||||
[Type::Function(args, l2, ret, _fx)] => {
|
||||
check_uls!(**l2, 2);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
|
@ -4548,7 +4633,7 @@ mod test {
|
|||
[(name, args)] => {
|
||||
assert_eq!(name.0.as_str(), "A");
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l3, ret)] => {
|
||||
[Type::Function(args, l3, ret, _fx)] => {
|
||||
check_uls!(**l3, 3);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue