mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
merge remote/main and update mono
This commit is contained in:
commit
ab4ac1c494
96 changed files with 4746 additions and 3422 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -5084,8 +5084,12 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_reporting",
|
||||
|
@ -5195,6 +5199,7 @@ dependencies = [
|
|||
"cli_utils",
|
||||
"indoc",
|
||||
"roc_build",
|
||||
"roc_command_utils",
|
||||
"roc_linker",
|
||||
"roc_load",
|
||||
"roc_mono",
|
||||
|
|
|
@ -474,7 +474,7 @@ mod cli_run {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial(basic_cli_url)]
|
||||
#[serial(zig_platform_parser_package_basic_cli_url)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn hello_world() {
|
||||
test_roc_app_slim(
|
||||
|
@ -533,8 +533,9 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
// zig_platform_parser_package_basic_cli_url use to be split up but then things could get stuck
|
||||
#[test]
|
||||
#[serial(zig_platform)]
|
||||
#[serial(zig_platform_parser_package_basic_cli_url)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn platform_switching_zig() {
|
||||
test_roc_app_slim(
|
||||
|
@ -915,8 +916,7 @@ mod cli_run {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial(parser_package)]
|
||||
#[serial(zig_platform)]
|
||||
#[serial(zig_platform_parser_package_basic_cli_url)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn parse_movies_csv() {
|
||||
test_roc_app_slim(
|
||||
|
@ -929,8 +929,7 @@ mod cli_run {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial(parser_package)]
|
||||
#[serial(basic_cli_url)]
|
||||
#[serial(zig_platform_parser_package_basic_cli_url)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn parse_letter_counts() {
|
||||
test_roc_app_slim(
|
||||
|
|
|
@ -1450,6 +1450,10 @@ fn expr_spec<'a>(
|
|||
Reset {
|
||||
symbol,
|
||||
update_mode,
|
||||
}
|
||||
| ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => {
|
||||
let tag_value_id = env.symbols[symbol];
|
||||
|
||||
|
|
|
@ -1151,10 +1151,7 @@ fn link_macos(
|
|||
|
||||
output_path.set_extension("dylib");
|
||||
|
||||
(
|
||||
vec!["-dylib", "-undefined", "dynamic_lookup", "-no_fixup_chains"],
|
||||
output_path,
|
||||
)
|
||||
(vec!["-dylib"], output_path)
|
||||
}
|
||||
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
||||
};
|
||||
|
|
|
@ -52,15 +52,34 @@ interface Decode
|
|||
Bool.{ Bool },
|
||||
]
|
||||
|
||||
## Error types when decoding a `List U8` of utf-8 bytes using a [Decoder]
|
||||
DecodeError : [TooShort]
|
||||
|
||||
## Return type of a [Decoder].
|
||||
##
|
||||
## This is can be useful when creating a [custom](#custom) decoder or when
|
||||
## using [fromBytesPartial](#fromBytesPartial). For example writing unit tests,
|
||||
## such as;
|
||||
## ```
|
||||
## expect
|
||||
## input = "\"hello\", " |> Str.toUtf8
|
||||
## actual = Decode.fromBytesPartial input Json.fromUtf8
|
||||
## expected = Ok "hello"
|
||||
##
|
||||
## actual.result == expected
|
||||
## ```
|
||||
DecodeResult val : { result : Result val DecodeError, rest : List U8 }
|
||||
|
||||
## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded
|
||||
## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting]
|
||||
## ability
|
||||
Decoder val fmt := List U8, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
|
||||
## Definition of the [Decoding] ability
|
||||
Decoding has
|
||||
decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting
|
||||
|
||||
## Definition of the [DecoderFormatting] ability
|
||||
DecoderFormatting has
|
||||
u8 : Decoder U8 fmt | fmt has DecoderFormatting
|
||||
u16 : Decoder U16 fmt | fmt has DecoderFormatting
|
||||
|
@ -96,15 +115,46 @@ DecoderFormatting has
|
|||
## `finalizer` should produce the tuple value from the decoded `state`.
|
||||
tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
|
||||
## Build a custom [Decoder] function. For example the implementation of
|
||||
## `decodeBool` could be defined as follows;
|
||||
##
|
||||
## ```
|
||||
## decodeBool = Decode.custom \bytes, @Json {} ->
|
||||
## when bytes is
|
||||
## ['f', 'a', 'l', 's', 'e', ..] -> { result: Ok Bool.false, rest: List.drop bytes 5 }
|
||||
## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.drop bytes 4 }
|
||||
## _ -> { result: Err TooShort, rest: bytes }
|
||||
## ```
|
||||
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
custom = \decode -> @Decoder decode
|
||||
|
||||
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
|
||||
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
|
||||
|
||||
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
|
||||
## ```
|
||||
## expect
|
||||
## input = "\"hello\", " |> Str.toUtf8
|
||||
## actual = Decode.fromBytesPartial input Json.fromUtf8
|
||||
## expected = Ok "hello"
|
||||
##
|
||||
## actual.result == expected
|
||||
## ```
|
||||
fromBytesPartial : List U8, fmt -> DecodeResult val | val has Decoding, fmt has DecoderFormatting
|
||||
fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
|
||||
|
||||
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
|
||||
## expected. If successful returns `Ok val`, however, if there are bytes
|
||||
## remaining returns `Err Leftover (List U8)`.
|
||||
## ```
|
||||
## expect
|
||||
## input = "\"hello\", " |> Str.toUtf8
|
||||
## actual = Decode.fromBytes input Json.fromUtf8
|
||||
## expected = Ok "hello"
|
||||
##
|
||||
## actual == expected
|
||||
## ```
|
||||
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError | val has Decoding, fmt has DecoderFormatting
|
||||
fromBytes = \bytes, fmt ->
|
||||
when fromBytesPartial bytes fmt is
|
||||
|
@ -116,5 +166,6 @@ fromBytes = \bytes, fmt ->
|
|||
else
|
||||
Err (Leftover rest)
|
||||
|
||||
## Transform the `val` of a [DecodeResult]
|
||||
mapResult : DecodeResult a, (a -> b) -> DecodeResult b
|
||||
mapResult = \{ result, rest }, mapper -> { result: Result.map result mapper, rest }
|
||||
|
|
|
@ -1258,51 +1258,6 @@ fn shallow_dealias_with_scope<'a>(scope: &'a mut Scope, typ: &'a Type) -> &'a Ty
|
|||
result
|
||||
}
|
||||
|
||||
pub fn instantiate_and_freshen_alias_type(
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
type_variables: &[Loc<AliasVar>],
|
||||
type_arguments: Vec<Type>,
|
||||
lambda_set_variables: &[LambdaSet],
|
||||
mut actual_type: Type,
|
||||
) -> (Vec<(Lowercase, Type)>, Vec<LambdaSet>, Type) {
|
||||
let mut substitutions = ImMap::default();
|
||||
let mut type_var_to_arg = Vec::new();
|
||||
|
||||
for (loc_var, arg_ann) in type_variables.iter().zip(type_arguments.into_iter()) {
|
||||
let name = loc_var.value.name.clone();
|
||||
let var = loc_var.value.var;
|
||||
|
||||
substitutions.insert(var, arg_ann.clone());
|
||||
type_var_to_arg.push((name.clone(), arg_ann));
|
||||
}
|
||||
|
||||
// make sure the recursion variable is freshly instantiated
|
||||
if let Type::RecursiveTagUnion(rvar, _, _) = &mut actual_type {
|
||||
let new = var_store.fresh();
|
||||
substitutions.insert(*rvar, Type::Variable(new));
|
||||
*rvar = new;
|
||||
}
|
||||
|
||||
// make sure hidden variables are freshly instantiated
|
||||
let mut new_lambda_set_variables = Vec::with_capacity(lambda_set_variables.len());
|
||||
for typ in lambda_set_variables.iter() {
|
||||
if let Type::Variable(var) = typ.0 {
|
||||
let fresh = var_store.fresh();
|
||||
substitutions.insert(var, Type::Variable(fresh));
|
||||
introduced_variables.insert_lambda_set(fresh);
|
||||
new_lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||
} else {
|
||||
unreachable!("at this point there should be only vars in there");
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate variables
|
||||
actual_type.substitute(&substitutions);
|
||||
|
||||
(type_var_to_arg, new_lambda_set_variables, actual_type)
|
||||
}
|
||||
|
||||
pub fn freshen_opaque_def(
|
||||
var_store: &mut VarStore,
|
||||
opaque: &Alias,
|
||||
|
@ -1318,25 +1273,46 @@ pub fn freshen_opaque_def(
|
|||
})
|
||||
.collect();
|
||||
|
||||
let fresh_type_arguments = fresh_variables
|
||||
.iter()
|
||||
.map(|av| Type::Variable(av.var))
|
||||
.collect();
|
||||
|
||||
// NB: We don't introduce the fresh variables here, we introduce them during constraint gen.
|
||||
// NB: If there are bugs, check whether this is a problem!
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
|
||||
let (_fresh_type_arguments, fresh_lambda_set, fresh_type) = instantiate_and_freshen_alias_type(
|
||||
var_store,
|
||||
&mut introduced_variables,
|
||||
&opaque.type_variables,
|
||||
fresh_type_arguments,
|
||||
&opaque.lambda_set_variables,
|
||||
opaque.typ.clone(),
|
||||
);
|
||||
let mut substitutions = ImMap::default();
|
||||
|
||||
(fresh_variables, fresh_lambda_set, fresh_type)
|
||||
// Freshen all type variables used in the opaque.
|
||||
for (loc_var, fresh_var) in opaque.type_variables.iter().zip(fresh_variables.iter()) {
|
||||
let old_var = loc_var.value.var;
|
||||
substitutions.insert(old_var, Type::Variable(fresh_var.var));
|
||||
// NB: fresh var not introduced in this pass; see above.
|
||||
}
|
||||
|
||||
// Freshen all nested recursion variables.
|
||||
for &rec_var in opaque.recursion_variables.iter() {
|
||||
let new = var_store.fresh();
|
||||
substitutions.insert(rec_var, Type::Variable(new));
|
||||
}
|
||||
|
||||
// Freshen all nested lambda sets.
|
||||
let mut new_lambda_set_variables = Vec::with_capacity(opaque.lambda_set_variables.len());
|
||||
for typ in opaque.lambda_set_variables.iter() {
|
||||
if let Type::Variable(var) = typ.0 {
|
||||
let fresh = var_store.fresh();
|
||||
substitutions.insert(var, Type::Variable(fresh));
|
||||
introduced_variables.insert_lambda_set(fresh);
|
||||
new_lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||
} else {
|
||||
unreachable!("at this point there should be only vars in there");
|
||||
}
|
||||
}
|
||||
|
||||
// Fresh the real type with our new variables.
|
||||
let actual_type = {
|
||||
let mut typ = opaque.typ.clone();
|
||||
typ.substitute(&substitutions);
|
||||
typ
|
||||
};
|
||||
|
||||
(fresh_variables, new_lambda_set_variables, actual_type)
|
||||
}
|
||||
|
||||
fn insertion_sort_by<T, F>(arr: &mut [T], mut compare: F)
|
||||
|
|
|
@ -2925,12 +2925,14 @@ fn correct_mutual_recursive_type_alias<'a>(
|
|||
};
|
||||
|
||||
let mut new_lambda_sets = ImSet::default();
|
||||
let mut new_recursion_variables = ImSet::default();
|
||||
let mut new_infer_ext_vars = ImSet::default();
|
||||
alias_type.instantiate_aliases(
|
||||
alias_region,
|
||||
&can_instantiate_symbol,
|
||||
var_store,
|
||||
&mut new_lambda_sets,
|
||||
&mut new_recursion_variables,
|
||||
&mut new_infer_ext_vars,
|
||||
);
|
||||
|
||||
|
@ -2953,6 +2955,9 @@ fn correct_mutual_recursive_type_alias<'a>(
|
|||
.map(|var| LambdaSet(Type::Variable(*var))),
|
||||
);
|
||||
|
||||
// add any new recursion variables
|
||||
alias.recursion_variables.extend(new_recursion_variables);
|
||||
|
||||
// add any new infer-in-output extension variables that the instantiation created to the current alias
|
||||
alias
|
||||
.infer_ext_in_output_variables
|
||||
|
|
|
@ -140,7 +140,7 @@ impl Pattern {
|
|||
RecordDestructure { destructs, .. } => {
|
||||
// If all destructs are surely exhaustive, then this is surely exhaustive.
|
||||
destructs.iter().all(|d| match &d.value.typ {
|
||||
DestructType::Required | DestructType::Optional(_, _) => false,
|
||||
DestructType::Required | DestructType::Optional(_, _) => true,
|
||||
DestructType::Guard(_, pat) => pat.value.surely_exhaustive(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -463,6 +463,7 @@ pub fn create_alias(
|
|||
let mut hidden = type_variables;
|
||||
|
||||
for var in (vars.iter().map(|lv| lv.value.var))
|
||||
.chain(recursion_variables.iter().copied())
|
||||
.chain(infer_ext_in_output_variables.iter().copied())
|
||||
{
|
||||
hidden.remove(&var);
|
||||
|
|
|
@ -137,6 +137,10 @@ impl<T> Slice<T> {
|
|||
pub fn into_iter(&self) -> impl Iterator<Item = Index<T>> {
|
||||
self.indices().map(|i| Index::new(i as _))
|
||||
}
|
||||
|
||||
pub const fn at(&self, i: usize) -> Index<T> {
|
||||
Index::new(self.start + i as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
|
|
|
@ -2097,6 +2097,7 @@ fn constrain_destructure_def(
|
|||
loc_pattern,
|
||||
&mut ftv,
|
||||
&mut def_pattern_state.headers,
|
||||
IsRecursiveDef::No,
|
||||
);
|
||||
|
||||
let env = &mut Env {
|
||||
|
@ -2674,6 +2675,7 @@ fn constrain_typed_def(
|
|||
&def.loc_pattern,
|
||||
&mut ftv,
|
||||
&mut def_pattern_state.headers,
|
||||
IsRecursiveDef::No,
|
||||
);
|
||||
|
||||
let env = &mut Env {
|
||||
|
@ -3323,6 +3325,12 @@ pub struct InstantiateRigids {
|
|||
pub new_infer_variables: Vec<Variable>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum IsRecursiveDef {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
fn instantiate_rigids(
|
||||
types: &mut Types,
|
||||
constraints: &mut Constraints,
|
||||
|
@ -3331,65 +3339,82 @@ fn instantiate_rigids(
|
|||
loc_pattern: &Loc<Pattern>,
|
||||
ftv: &mut MutMap<Lowercase, Variable>, // rigids defined before the current annotation
|
||||
headers: &mut VecMap<Symbol, Loc<TypeOrVar>>,
|
||||
is_recursive_def: IsRecursiveDef,
|
||||
) -> InstantiateRigids {
|
||||
let mut annotation = annotation.clone();
|
||||
let mut new_rigid_variables: Vec<Variable> = Vec::new();
|
||||
let mut new_rigid_variables = vec![];
|
||||
let mut new_infer_variables = vec![];
|
||||
|
||||
let mut rigid_substitution: MutMap<Variable, Variable> = MutMap::default();
|
||||
for named in introduced_vars.iter_named() {
|
||||
use std::collections::hash_map::Entry::*;
|
||||
let mut generate_fresh_ann = |types: &mut Types| {
|
||||
let mut annotation = annotation.clone();
|
||||
|
||||
match ftv.entry(named.name().clone()) {
|
||||
Occupied(occupied) => {
|
||||
let existing_rigid = occupied.get();
|
||||
rigid_substitution.insert(named.variable(), *existing_rigid);
|
||||
}
|
||||
Vacant(vacant) => {
|
||||
// It's possible to use this rigid in nested defs
|
||||
vacant.insert(named.variable());
|
||||
new_rigid_variables.push(named.variable());
|
||||
let mut rigid_substitution: MutMap<Variable, Variable> = MutMap::default();
|
||||
for named in introduced_vars.iter_named() {
|
||||
use std::collections::hash_map::Entry::*;
|
||||
|
||||
match ftv.entry(named.name().clone()) {
|
||||
Occupied(occupied) => {
|
||||
let existing_rigid = occupied.get();
|
||||
rigid_substitution.insert(named.variable(), *existing_rigid);
|
||||
}
|
||||
Vacant(vacant) => {
|
||||
// It's possible to use this rigid in nested defs
|
||||
vacant.insert(named.variable());
|
||||
new_rigid_variables.push(named.variable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wildcards are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.wildcards.iter().map(|v| v.value));
|
||||
|
||||
// lambda set vars are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.lambda_sets.iter().copied());
|
||||
|
||||
// ext-infer vars are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.infer_ext_in_output.iter().copied());
|
||||
|
||||
new_infer_variables.extend(introduced_vars.inferred.iter().map(|v| v.value));
|
||||
|
||||
// Instantiate rigid variables
|
||||
if !rigid_substitution.is_empty() {
|
||||
annotation.substitute_variables(&rigid_substitution);
|
||||
}
|
||||
|
||||
types.from_old_type(&annotation)
|
||||
};
|
||||
|
||||
let signature = generate_fresh_ann(types);
|
||||
{
|
||||
// If this is a recursive def, we must also generate a fresh annotation to be used as the
|
||||
// type annotation that will be used in the first def headers introduced during the solving
|
||||
// of the recursive definition.
|
||||
//
|
||||
// That is, this annotation serves as step (1) of XREF(rec-def-strategy). We don't want to
|
||||
// link to the final annotation, since it may be incomplete (or incorrect, see step (1)).
|
||||
// So, we generate a fresh annotation here, and return a separate fresh annotation below;
|
||||
// the latter annotation is the one used to construct the finalized type.
|
||||
let annotation_index = if is_recursive_def == IsRecursiveDef::Yes {
|
||||
generate_fresh_ann(types)
|
||||
} else {
|
||||
signature
|
||||
};
|
||||
|
||||
let loc_annotation_ref = Loc::at(loc_pattern.region, annotation_index);
|
||||
if let Pattern::Identifier(symbol) = loc_pattern.value {
|
||||
let annotation_index = constraints.push_type(types, annotation_index);
|
||||
headers.insert(symbol, Loc::at(loc_pattern.region, annotation_index));
|
||||
} else if let Some(new_headers) = crate::pattern::headers_from_annotation(
|
||||
types,
|
||||
constraints,
|
||||
&loc_pattern.value,
|
||||
&loc_annotation_ref,
|
||||
) {
|
||||
headers.extend(new_headers)
|
||||
}
|
||||
}
|
||||
|
||||
// wildcards are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.wildcards.iter().map(|v| v.value));
|
||||
|
||||
// lambda set vars are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.lambda_sets.iter().copied());
|
||||
|
||||
// ext-infer vars are always freshly introduced in this annotation
|
||||
new_rigid_variables.extend(introduced_vars.infer_ext_in_output.iter().copied());
|
||||
|
||||
let new_infer_variables: Vec<Variable> =
|
||||
introduced_vars.inferred.iter().map(|v| v.value).collect();
|
||||
|
||||
// Instantiate rigid variables
|
||||
if !rigid_substitution.is_empty() {
|
||||
annotation.substitute_variables(&rigid_substitution);
|
||||
}
|
||||
let annotation_index = types.from_old_type(&annotation);
|
||||
|
||||
// TODO investigate when we can skip this. It seems to only be required for correctness
|
||||
// for recursive functions. For non-recursive functions the final type is correct, but
|
||||
// alias information is sometimes lost
|
||||
//
|
||||
// Skipping all of this cloning here would be neat!
|
||||
let loc_annotation_ref = Loc::at(loc_pattern.region, &annotation);
|
||||
if let Pattern::Identifier(symbol) = loc_pattern.value {
|
||||
let annotation_index = constraints.push_type(types, annotation_index);
|
||||
headers.insert(symbol, Loc::at(loc_pattern.region, annotation_index));
|
||||
} else if let Some(new_headers) = crate::pattern::headers_from_annotation(
|
||||
types,
|
||||
constraints,
|
||||
&loc_pattern.value,
|
||||
&loc_annotation_ref,
|
||||
) {
|
||||
headers.extend(new_headers)
|
||||
}
|
||||
|
||||
InstantiateRigids {
|
||||
signature: annotation_index,
|
||||
signature,
|
||||
new_rigid_variables,
|
||||
new_infer_variables,
|
||||
}
|
||||
|
@ -3867,7 +3892,7 @@ pub fn rec_defs_help_simple(
|
|||
}
|
||||
}
|
||||
|
||||
// Strategy for recursive defs:
|
||||
// NB(rec-def-strategy) Strategy for recursive defs:
|
||||
//
|
||||
// 1. Let-generalize all rigid annotations. These are the source of truth we'll solve
|
||||
// everything else with. If there are circular type errors here, they will be caught
|
||||
|
@ -4068,11 +4093,12 @@ fn rec_defs_help(
|
|||
&def.loc_pattern,
|
||||
&mut ftv,
|
||||
&mut def_pattern_state.headers,
|
||||
IsRecursiveDef::Yes,
|
||||
);
|
||||
|
||||
let is_hybrid = !new_infer_variables.is_empty();
|
||||
|
||||
hybrid_and_flex_info.vars.extend(new_infer_variables);
|
||||
hybrid_and_flex_info.vars.extend(&new_infer_variables);
|
||||
|
||||
let signature_index = constraints.push_type(types, signature);
|
||||
|
||||
|
@ -4113,13 +4139,14 @@ fn rec_defs_help(
|
|||
let region = def.loc_expr.region;
|
||||
|
||||
let loc_body_expr = &**loc_body;
|
||||
let mut state = PatternState {
|
||||
let mut argument_pattern_state = PatternState {
|
||||
headers: VecMap::default(),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
delayed_is_open_constraints: vec![],
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut vars =
|
||||
Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
let ret_type_index = constraints.push_type(types, ret_type);
|
||||
|
@ -4133,7 +4160,7 @@ fn rec_defs_help(
|
|||
env,
|
||||
def,
|
||||
&mut def_pattern_state,
|
||||
&mut state,
|
||||
&mut argument_pattern_state,
|
||||
arguments,
|
||||
arg_types,
|
||||
);
|
||||
|
@ -4157,27 +4184,31 @@ fn rec_defs_help(
|
|||
let typ = types.function(pattern_types, lambda_set, ret_type);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let body_type =
|
||||
constraints.push_expected_type(NoExpectation(ret_type_index));
|
||||
let expr_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
);
|
||||
let expr_con = {
|
||||
let body_type =
|
||||
constraints.push_expected_type(NoExpectation(ret_type_index));
|
||||
|
||||
constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
)
|
||||
};
|
||||
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
|
||||
|
||||
vars.push(*fn_var);
|
||||
|
||||
let state_constraints = constraints.and_constraint(state.constraints);
|
||||
let state_constraints =
|
||||
constraints.and_constraint(argument_pattern_state.constraints);
|
||||
let expected_index = constraints.push_expected_type(expected);
|
||||
let cons = [
|
||||
constraints.let_constraint(
|
||||
[],
|
||||
state.vars,
|
||||
state.headers,
|
||||
argument_pattern_state.vars,
|
||||
argument_pattern_state.headers,
|
||||
state_constraints,
|
||||
expr_con,
|
||||
generalizable,
|
||||
|
@ -4216,9 +4247,15 @@ fn rec_defs_help(
|
|||
} else {
|
||||
rigid_info.vars.extend(&new_rigid_variables);
|
||||
|
||||
let rigids = new_rigid_variables;
|
||||
let flex = def_pattern_state
|
||||
.vars
|
||||
.into_iter()
|
||||
.chain(new_infer_variables);
|
||||
|
||||
rigid_info.constraints.push(constraints.let_constraint(
|
||||
new_rigid_variables,
|
||||
def_pattern_state.vars,
|
||||
rigids,
|
||||
flex,
|
||||
[], // no headers introduced (at this level)
|
||||
def_con,
|
||||
Constraint::True,
|
||||
|
@ -4278,7 +4315,7 @@ fn rec_defs_help(
|
|||
}
|
||||
}
|
||||
|
||||
// Strategy for recursive defs:
|
||||
// NB(rec-def-strategy) Strategy for recursive defs:
|
||||
//
|
||||
// 1. Let-generalize all rigid annotations. These are the source of truth we'll solve
|
||||
// everything else with. If there are circular type errors here, they will be caught
|
||||
|
|
|
@ -5,14 +5,15 @@ use roc_can::expected::{Expected, PExpected};
|
|||
use roc_can::pattern::Pattern::{self, *};
|
||||
use roc_can::pattern::{DestructType, ListPatterns, RecordDestruct, TupleDestruct};
|
||||
use roc_collections::all::{HumanIndex, SendMap};
|
||||
use roc_collections::soa::Index;
|
||||
use roc_collections::VecMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{
|
||||
AliasKind, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField, Type,
|
||||
TypeExtension, TypeTag, Types,
|
||||
AliasKind, AliasShared, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField,
|
||||
Type, TypeExtension, TypeTag, Types,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -31,10 +32,10 @@ pub struct PatternState {
|
|||
/// Would add `x => <42>` to the headers (i.e., symbol points to a type variable). If the
|
||||
/// definition has an annotation, we instead now add `x => Int`.
|
||||
pub fn headers_from_annotation(
|
||||
types: &mut Types,
|
||||
types: &Types,
|
||||
constraints: &mut Constraints,
|
||||
pattern: &Pattern,
|
||||
annotation: &Loc<&Type>,
|
||||
annotation: &Loc<Index<TypeTag>>,
|
||||
) -> Option<VecMap<Symbol, Loc<TypeOrVar>>> {
|
||||
let mut headers = VecMap::default();
|
||||
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
|
||||
|
@ -51,12 +52,13 @@ pub fn headers_from_annotation(
|
|||
}
|
||||
|
||||
fn headers_from_annotation_help(
|
||||
types: &mut Types,
|
||||
types: &Types,
|
||||
constraints: &mut Constraints,
|
||||
pattern: &Pattern,
|
||||
annotation: &Loc<&Type>,
|
||||
annotation: &Loc<Index<TypeTag>>,
|
||||
headers: &mut VecMap<Symbol, Loc<TypeOrVar>>,
|
||||
) -> bool {
|
||||
let typ = annotation.value;
|
||||
match pattern {
|
||||
Identifier(symbol)
|
||||
| Shadowed(_, _, symbol)
|
||||
|
@ -64,20 +66,14 @@ fn headers_from_annotation_help(
|
|||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let annotation_index = constraints.push_type(types, typ);
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*symbol, typ);
|
||||
true
|
||||
}
|
||||
|
||||
As(subpattern, symbol) => {
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let annotation_index = constraints.push_type(types, typ);
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*symbol, typ);
|
||||
|
||||
|
@ -94,36 +90,45 @@ fn headers_from_annotation_help(
|
|||
| SingleQuote(..)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
Type::Record(fields, _) => {
|
||||
for loc_destruct in destructs {
|
||||
let destruct = &loc_destruct.value;
|
||||
RecordDestructure { destructs, .. } => {
|
||||
let dealiased = types.shallow_dealias(annotation.value);
|
||||
match types[dealiased] {
|
||||
TypeTag::Record(fields) => {
|
||||
let (field_names, _, field_types) = types.record_fields_slices(fields);
|
||||
let field_names = &types[field_names];
|
||||
|
||||
// NOTE: We ignore both Guard and optionality when
|
||||
// determining the type of the assigned def (which is what
|
||||
// gets added to the header here).
|
||||
//
|
||||
// For example, no matter whether it's `{ x } = rec` or
|
||||
// `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases
|
||||
// the type of `x` within the binding itself is the same.
|
||||
if let Some(field_type) = fields.get(&destruct.label) {
|
||||
let field_type_index = {
|
||||
let typ = types.from_old_type(&field_type.as_inner().clone());
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
headers.insert(
|
||||
destruct.symbol,
|
||||
Loc::at(annotation.region, field_type_index),
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
for loc_destruct in destructs {
|
||||
let destruct = &loc_destruct.value;
|
||||
|
||||
// NOTE: We ignore both Guard and optionality when
|
||||
// determining the type of the assigned def (which is what
|
||||
// gets added to the header here).
|
||||
//
|
||||
// For example, no matter whether it's `{ x } = rec` or
|
||||
// `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases
|
||||
// the type of `x` within the binding itself is the same.
|
||||
if let Some(i) = field_names
|
||||
.iter()
|
||||
.position(|field| field == &destruct.label)
|
||||
{
|
||||
let field_type_index = {
|
||||
let typ = field_types.at(i);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
headers.insert(
|
||||
destruct.symbol,
|
||||
Loc::at(annotation.region, field_type_index),
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
true
|
||||
TypeTag::EmptyRecord => destructs.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
Type::EmptyRec => destructs.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
|
||||
TupleDestructure { destructs: _, .. } => {
|
||||
todo!();
|
||||
|
@ -132,7 +137,7 @@ fn headers_from_annotation_help(
|
|||
List { patterns, .. } => {
|
||||
if let Some((_, Some(rest))) = patterns.opt_rest {
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
let typ = annotation.value;
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
|
@ -152,31 +157,38 @@ fn headers_from_annotation_help(
|
|||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => match annotation.value.shallow_dealias() {
|
||||
Type::TagUnion(tags, _) => {
|
||||
if let Some((_, arg_types)) = tags.iter().find(|(name, _)| name == tag_name) {
|
||||
if !arguments.len() == arg_types.len() {
|
||||
return false;
|
||||
}
|
||||
} => {
|
||||
let dealiased = types.shallow_dealias(annotation.value);
|
||||
match types[dealiased] {
|
||||
TypeTag::TagUnion(tags, _) => {
|
||||
let (tags, payloads) = types.union_tag_slices(tags);
|
||||
let tags = &types[tags];
|
||||
|
||||
arguments
|
||||
.iter()
|
||||
.zip(arg_types.iter())
|
||||
.all(|(arg_pattern, arg_type)| {
|
||||
headers_from_annotation_help(
|
||||
types,
|
||||
constraints,
|
||||
&arg_pattern.1.value,
|
||||
&Loc::at(annotation.region, arg_type),
|
||||
headers,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
if let Some(i) = tags.iter().position(|name| name == tag_name) {
|
||||
let arg_types_slice = types[payloads.at(i)];
|
||||
|
||||
if !arguments.len() == arg_types_slice.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
arguments.iter().zip(arg_types_slice.into_iter()).all(
|
||||
|(arg_pattern, arg_type)| {
|
||||
headers_from_annotation_help(
|
||||
types,
|
||||
constraints,
|
||||
&arg_pattern.1.value,
|
||||
&Loc::at(annotation.region, arg_type),
|
||||
headers,
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
|
||||
UnwrappedOpaque {
|
||||
whole_var: _,
|
||||
|
@ -185,36 +197,41 @@ fn headers_from_annotation_help(
|
|||
specialized_def_type: _,
|
||||
type_arguments: pat_type_arguments,
|
||||
lambda_set_variables: pat_lambda_set_variables,
|
||||
} => match &annotation.value {
|
||||
Type::Alias {
|
||||
symbol,
|
||||
kind: AliasKind::Opaque,
|
||||
actual,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_types: _,
|
||||
} if symbol == opaque
|
||||
&& type_arguments.len() == pat_type_arguments.len()
|
||||
&& lambda_set_variables.len() == pat_lambda_set_variables.len() =>
|
||||
{
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*opaque, typ);
|
||||
} => {
|
||||
let typ = annotation.value;
|
||||
|
||||
let (_, argument_pat) = &**argument;
|
||||
headers_from_annotation_help(
|
||||
types,
|
||||
constraints,
|
||||
&argument_pat.value,
|
||||
&Loc::at(annotation.region, actual),
|
||||
headers,
|
||||
)
|
||||
match types[typ] {
|
||||
TypeTag::OpaqueAlias { shared, actual } => {
|
||||
let AliasShared {
|
||||
symbol,
|
||||
lambda_set_variables,
|
||||
..
|
||||
} = types[shared];
|
||||
let type_arguments = types.get_type_arguments(typ);
|
||||
|
||||
if symbol == *opaque
|
||||
&& type_arguments.len() == pat_type_arguments.len()
|
||||
&& lambda_set_variables.len() == pat_lambda_set_variables.len()
|
||||
{
|
||||
let annotation_index = constraints.push_type(types, typ);
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*opaque, typ);
|
||||
|
||||
let (_, argument_pat) = &**argument;
|
||||
headers_from_annotation_help(
|
||||
types,
|
||||
constraints,
|
||||
&argument_pat.value,
|
||||
&Loc::at(annotation.region, actual),
|
||||
headers,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1602,7 +1602,7 @@ trait Backend<'a> {
|
|||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
Expr::Reset { symbol, .. } => {
|
||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::EmptyArray => {}
|
||||
|
|
|
@ -539,6 +539,7 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
|
|||
pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
layout: InLayout<'a>,
|
||||
|
@ -599,12 +600,11 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
|||
env.builder
|
||||
.build_pointer_cast(value_ptr2, value_ptr_type, "load_opaque");
|
||||
|
||||
let value1 = env
|
||||
.builder
|
||||
.new_build_load(value_type, value_cast1, "load_opaque");
|
||||
let value2 = env
|
||||
.builder
|
||||
.new_build_load(value_type, value_cast2, "load_opaque");
|
||||
let value1 = load_roc_value(env, layout_interner, layout, value_cast1, "load_opaque");
|
||||
let value2 = load_roc_value(env, layout_interner, layout, value_cast2, "load_opaque");
|
||||
|
||||
increment_refcount_layout(env, layout_interner, layout_ids, 1, value1, layout);
|
||||
increment_refcount_layout(env, layout_interner, layout_ids, 1, value2, layout);
|
||||
|
||||
let default = [value1.into(), value2.into()];
|
||||
|
||||
|
|
|
@ -1235,6 +1235,53 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
phi.as_basic_value()
|
||||
}
|
||||
}
|
||||
ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => {
|
||||
let bytes = update_mode.to_bytes();
|
||||
let update_var = UpdateModeVar(&bytes);
|
||||
let update_mode = func_spec_solutions
|
||||
.update_mode(update_var)
|
||||
.unwrap_or(UpdateMode::Immutable);
|
||||
|
||||
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
|
||||
let tag_ptr = tag_ptr.into_pointer_value();
|
||||
|
||||
let ctx = env.context;
|
||||
let not_unique_block = ctx.append_basic_block(parent, "else_decref");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
|
||||
|
||||
let is_unique = match update_mode {
|
||||
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
|
||||
UpdateMode::Immutable => refcount_ptr.is_1(env),
|
||||
};
|
||||
|
||||
let parent_block = env.builder.get_insert_block().unwrap();
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_unique, cont_block, not_unique_block);
|
||||
|
||||
{
|
||||
// If reset is used on a shared, non-reusable reference, it behaves
|
||||
// like dec and returns NULL, which instructs reuse to behave like ctor
|
||||
env.builder.position_at_end(not_unique_block);
|
||||
refcount_ptr.decrement(env, layout_interner, layout);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
{
|
||||
env.builder.position_at_end(cont_block);
|
||||
let phi = env.builder.build_phi(tag_ptr.get_type(), "branch");
|
||||
|
||||
let null_ptr = tag_ptr.get_type().const_null();
|
||||
phi.add_incoming(&[(&tag_ptr, parent_block), (&null_ptr, not_unique_block)]);
|
||||
|
||||
phi.as_basic_value()
|
||||
}
|
||||
}
|
||||
|
||||
StructAtIndex {
|
||||
index, structure, ..
|
||||
|
|
|
@ -2606,6 +2606,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
let compare_wrapper = build_compare_wrapper(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
function,
|
||||
closure_layout,
|
||||
element_layout,
|
||||
|
|
|
@ -1095,6 +1095,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
|
||||
Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
|
||||
|
||||
Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*arg, sym, storage),
|
||||
|
||||
Expr::RuntimeErrorFunction(_) => {
|
||||
todo!("Expression `{}`", expr.to_pretty(100, false))
|
||||
}
|
||||
|
@ -2017,6 +2019,33 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
);
|
||||
}
|
||||
|
||||
fn expr_resetref(&mut self, argument: Symbol, ret_symbol: Symbol, ret_storage: &StoredValue) {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
// Get an IR expression for the call to the specialized procedure
|
||||
let layout = self.storage.symbol_layouts[&argument];
|
||||
let (specialized_call_expr, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.call_resetref_refcount(ident_ids, self.layout_interner, layout, argument);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
// Generate Wasm code for the IR call expression
|
||||
self.expr(
|
||||
ret_symbol,
|
||||
self.env.arena.alloc(specialized_call_expr),
|
||||
Layout::BOOL,
|
||||
ret_storage,
|
||||
);
|
||||
}
|
||||
|
||||
/// Generate a refcount helper procedure and return a pointer (table index) to it
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_refcount_fn_index(&mut self, layout: InLayout<'a>, op: HelperOp) -> u32 {
|
||||
|
|
|
@ -30,6 +30,7 @@ use roc_module::symbol::{
|
|||
IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds,
|
||||
PackageQualified, Symbol,
|
||||
};
|
||||
use roc_mono::inc_dec;
|
||||
use roc_mono::ir::{
|
||||
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
|
||||
ProcLayout, Procs, ProcsBase, UpdateModeIds,
|
||||
|
@ -38,6 +39,7 @@ use roc_mono::layout::LayoutInterner;
|
|||
use roc_mono::layout::{
|
||||
GlobalLayoutInterner, LambdaName, Layout, LayoutCache, LayoutProblem, Niche, STLayoutInterner,
|
||||
};
|
||||
use roc_mono::reset_reuse;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_parse::ast::{
|
||||
self, CommentOrNewline, Defs, Expr, ExtractSpaces, Pattern, Spaced, StrLiteral, TypeAnnotation,
|
||||
|
@ -3087,6 +3089,8 @@ fn update<'a>(
|
|||
std::mem::swap(&mut state.layout_interner, &mut taken);
|
||||
taken
|
||||
};
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut layout_interner = layout_interner
|
||||
.unwrap()
|
||||
.expect("outstanding references to global layout interener, but we just drained all layout caches");
|
||||
|
@ -3098,9 +3102,17 @@ fn update<'a>(
|
|||
|
||||
let ident_ids = state.constrained_ident_ids.get_mut(&module_id).unwrap();
|
||||
|
||||
Proc::insert_reset_reuse_operations(
|
||||
inc_dec::insert_inc_dec_operations(
|
||||
arena,
|
||||
&mut layout_interner,
|
||||
&layout_interner,
|
||||
&mut state.procedures,
|
||||
);
|
||||
|
||||
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_REFCOUNT);
|
||||
|
||||
reset_reuse::insert_reset_reuse_operations(
|
||||
arena,
|
||||
&layout_interner,
|
||||
module_id,
|
||||
ident_ids,
|
||||
&mut update_mode_ids,
|
||||
|
@ -3109,23 +3121,6 @@ fn update<'a>(
|
|||
|
||||
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
|
||||
|
||||
let host_exposed_procs = bumpalo::collections::Vec::from_iter_in(
|
||||
state.exposed_to_host.top_level_values.keys().copied(),
|
||||
arena,
|
||||
);
|
||||
|
||||
Proc::insert_refcount_operations(
|
||||
arena,
|
||||
&layout_interner,
|
||||
module_id,
|
||||
ident_ids,
|
||||
&mut update_mode_ids,
|
||||
&mut state.procedures,
|
||||
&host_exposed_procs,
|
||||
);
|
||||
|
||||
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_REFCOUNT);
|
||||
|
||||
// This is not safe with the new non-recursive RC updates that we do for tag unions
|
||||
//
|
||||
// Proc::optimize_refcount_operations(
|
||||
|
|
|
@ -12,9 +12,6 @@ use roc_collections::ReferenceMatrix;
|
|||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
pub(crate) const OWNED: bool = false;
|
||||
pub(crate) const BORROWED: bool = true;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Ownership {
|
||||
Owned,
|
||||
|
@ -22,6 +19,14 @@ pub enum Ownership {
|
|||
}
|
||||
|
||||
impl Ownership {
|
||||
pub fn is_owned(&self) -> bool {
|
||||
matches!(self, Ownership::Owned)
|
||||
}
|
||||
|
||||
pub fn is_borrowed(&self) -> bool {
|
||||
matches!(self, Ownership::Borrowed)
|
||||
}
|
||||
|
||||
/// For reference-counted types (lists, (big) strings, recursive tags), owning a value
|
||||
/// means incrementing its reference count. Hence, we prefer borrowing for these types
|
||||
fn from_layout(layout: &Layout) -> Self {
|
||||
|
@ -285,21 +290,6 @@ impl<'a> ParamMap<'a> {
|
|||
.into_bump_slice()
|
||||
}
|
||||
|
||||
fn init_borrow_args_always_owned(
|
||||
arena: &'a Bump,
|
||||
ps: &'a [(InLayout<'a>, Symbol)],
|
||||
) -> &'a [Param<'a>] {
|
||||
Vec::from_iter_in(
|
||||
ps.iter().map(|(layout, symbol)| Param {
|
||||
ownership: Ownership::Owned,
|
||||
layout: *layout,
|
||||
symbol: *symbol,
|
||||
}),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice()
|
||||
}
|
||||
|
||||
fn visit_proc(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
|
@ -307,11 +297,6 @@ impl<'a> ParamMap<'a> {
|
|||
proc: &Proc<'a>,
|
||||
key: (Symbol, ProcLayout<'a>),
|
||||
) {
|
||||
if proc.must_own_arguments {
|
||||
self.visit_proc_always_owned(arena, interner, proc, key);
|
||||
return;
|
||||
}
|
||||
|
||||
let index: usize = self.get_param_offset(interner, key.0, key.1).into();
|
||||
|
||||
for (i, param) in Self::init_borrow_args(arena, interner, proc.args)
|
||||
|
@ -325,26 +310,6 @@ impl<'a> ParamMap<'a> {
|
|||
self.visit_stmt(arena, interner, proc.name.name(), &proc.body);
|
||||
}
|
||||
|
||||
fn visit_proc_always_owned(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
proc: &Proc<'a>,
|
||||
key: (Symbol, ProcLayout<'a>),
|
||||
) {
|
||||
let index: usize = self.get_param_offset(interner, key.0, key.1).into();
|
||||
|
||||
for (i, param) in Self::init_borrow_args_always_owned(arena, proc.args)
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
{
|
||||
self.declarations[index + i] = param;
|
||||
}
|
||||
|
||||
self.visit_stmt(arena, interner, proc.name.name(), &proc.body);
|
||||
}
|
||||
|
||||
fn visit_stmt(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
|
@ -447,7 +412,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
fn update_param_map_help(&mut self, ps: &[Param<'a>]) -> &'a [Param<'a>] {
|
||||
let mut new_ps = Vec::with_capacity_in(ps.len(), self.arena);
|
||||
new_ps.extend(ps.iter().map(|p| {
|
||||
if p.ownership == Ownership::Owned {
|
||||
if p.ownership.is_owned() {
|
||||
*p
|
||||
} else if self.is_owned(p.symbol) {
|
||||
self.modified = true;
|
||||
|
@ -473,7 +438,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
let ps = &mut param_map.declarations[index..][..length];
|
||||
|
||||
for p in ps.iter_mut() {
|
||||
if p.ownership == Ownership::Owned {
|
||||
if p.ownership.is_owned() {
|
||||
// do nothing
|
||||
} else if self.is_owned(p.symbol) {
|
||||
self.modified = true;
|
||||
|
@ -497,7 +462,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
debug_assert_eq!(xs.len(), ps.len());
|
||||
|
||||
for (x, p) in xs.iter().zip(ps.iter()) {
|
||||
if p.ownership == Ownership::Owned {
|
||||
if p.ownership.is_owned() {
|
||||
self.own_var(*x);
|
||||
}
|
||||
}
|
||||
|
@ -506,13 +471,10 @@ impl<'a> BorrowInfState<'a> {
|
|||
/// This looks at an application `f x1 x2 x3`
|
||||
/// If the parameter (based on the definition of `f`) is owned,
|
||||
/// then the argument must also be owned
|
||||
fn own_args_using_bools(&mut self, xs: &[Symbol], ps: &[bool]) {
|
||||
fn own_args_using_bools(&mut self, xs: &[Symbol], ps: &[Ownership]) {
|
||||
debug_assert_eq!(xs.len(), ps.len());
|
||||
|
||||
for (x, borrow) in xs.iter().zip(ps.iter()) {
|
||||
if !borrow {
|
||||
self.own_var(*x);
|
||||
}
|
||||
for (x, _) in xs.iter().zip(ps.iter()).filter(|(_, o)| o.is_owned()) {
|
||||
self.own_var(*x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,44 +595,44 @@ impl<'a> BorrowInfState<'a> {
|
|||
match op {
|
||||
ListMap { xs } => {
|
||||
// own the list if the function wants to own the element
|
||||
if function_ps[0].ownership == Ownership::Owned {
|
||||
if function_ps[0].ownership.is_owned() {
|
||||
self.own_var(*xs);
|
||||
}
|
||||
}
|
||||
ListMap2 { xs, ys } => {
|
||||
// own the lists if the function wants to own the element
|
||||
if function_ps[0].ownership == Ownership::Owned {
|
||||
if function_ps[0].ownership.is_owned() {
|
||||
self.own_var(*xs);
|
||||
}
|
||||
|
||||
if function_ps[1].ownership == Ownership::Owned {
|
||||
if function_ps[1].ownership.is_owned() {
|
||||
self.own_var(*ys);
|
||||
}
|
||||
}
|
||||
ListMap3 { xs, ys, zs } => {
|
||||
// own the lists if the function wants to own the element
|
||||
if function_ps[0].ownership == Ownership::Owned {
|
||||
if function_ps[0].ownership.is_owned() {
|
||||
self.own_var(*xs);
|
||||
}
|
||||
if function_ps[1].ownership == Ownership::Owned {
|
||||
if function_ps[1].ownership.is_owned() {
|
||||
self.own_var(*ys);
|
||||
}
|
||||
if function_ps[2].ownership == Ownership::Owned {
|
||||
if function_ps[2].ownership.is_owned() {
|
||||
self.own_var(*zs);
|
||||
}
|
||||
}
|
||||
ListMap4 { xs, ys, zs, ws } => {
|
||||
// own the lists if the function wants to own the element
|
||||
if function_ps[0].ownership == Ownership::Owned {
|
||||
if function_ps[0].ownership.is_owned() {
|
||||
self.own_var(*xs);
|
||||
}
|
||||
if function_ps[1].ownership == Ownership::Owned {
|
||||
if function_ps[1].ownership.is_owned() {
|
||||
self.own_var(*ys);
|
||||
}
|
||||
if function_ps[2].ownership == Ownership::Owned {
|
||||
if function_ps[2].ownership.is_owned() {
|
||||
self.own_var(*zs);
|
||||
}
|
||||
if function_ps[3].ownership == Ownership::Owned {
|
||||
if function_ps[3].ownership.is_owned() {
|
||||
self.own_var(*ws);
|
||||
}
|
||||
}
|
||||
|
@ -742,7 +704,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.if_is_owned_then_own(z, *x);
|
||||
}
|
||||
|
||||
Reset { symbol: x, .. } => {
|
||||
Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
|
||||
self.own_var(z);
|
||||
self.own_var(*x);
|
||||
}
|
||||
|
@ -961,22 +923,22 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[bool] {
|
||||
pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[Ownership] {
|
||||
// NOTE this means that Roc is responsible for cleaning up resources;
|
||||
// the host cannot (currently) take ownership
|
||||
let all = bumpalo::vec![in arena; BORROWED; arity];
|
||||
let all = bumpalo::vec![in arena; Ownership::Borrowed; arity];
|
||||
all.into_bump_slice()
|
||||
}
|
||||
|
||||
pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
|
||||
use LowLevel::*;
|
||||
|
||||
// TODO is true or false more efficient for non-refcounted layouts?
|
||||
let irrelevant = OWNED;
|
||||
let irrelevant = Ownership::Owned;
|
||||
let function = irrelevant;
|
||||
let closure_data = irrelevant;
|
||||
let owned = OWNED;
|
||||
let borrowed = BORROWED;
|
||||
let owned = Ownership::Owned;
|
||||
let borrowed = Ownership::Borrowed;
|
||||
|
||||
// Here we define the borrow signature of low-level operations
|
||||
//
|
||||
|
|
|
@ -213,7 +213,7 @@ fn eq_tag_union<'a>(
|
|||
layout_interner,
|
||||
union_layout,
|
||||
tags,
|
||||
None,
|
||||
NullableId::None,
|
||||
),
|
||||
|
||||
Recursive(tags) => eq_tag_union_help(
|
||||
|
@ -223,7 +223,7 @@ fn eq_tag_union<'a>(
|
|||
layout_interner,
|
||||
union_layout,
|
||||
tags,
|
||||
None,
|
||||
NullableId::None,
|
||||
),
|
||||
|
||||
NonNullableUnwrapped(field_layouts) => {
|
||||
|
@ -235,7 +235,7 @@ fn eq_tag_union<'a>(
|
|||
layout_interner,
|
||||
union_layout,
|
||||
tags,
|
||||
None,
|
||||
NullableId::None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -249,7 +249,7 @@ fn eq_tag_union<'a>(
|
|||
layout_interner,
|
||||
union_layout,
|
||||
other_tags,
|
||||
Some(nullable_id),
|
||||
NullableId::Wrapped(nullable_id),
|
||||
),
|
||||
|
||||
NullableUnwrapped {
|
||||
|
@ -262,7 +262,7 @@ fn eq_tag_union<'a>(
|
|||
layout_interner,
|
||||
union_layout,
|
||||
root.arena.alloc([other_fields]),
|
||||
Some(nullable_id as TagIdIntType),
|
||||
NullableId::Unwrapped(nullable_id),
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -271,6 +271,12 @@ fn eq_tag_union<'a>(
|
|||
body
|
||||
}
|
||||
|
||||
enum NullableId {
|
||||
None,
|
||||
Wrapped(TagIdIntType),
|
||||
Unwrapped(bool),
|
||||
}
|
||||
|
||||
fn eq_tag_union_help<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
|
@ -278,7 +284,7 @@ fn eq_tag_union_help<'a>(
|
|||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
tag_layouts: &'a [&'a [InLayout<'a>]],
|
||||
nullable_id: Option<TagIdIntType>,
|
||||
nullable_id: NullableId,
|
||||
) -> Stmt<'a> {
|
||||
let tailrec_loop = JoinPointId(root.create_symbol(ident_ids, "tailrec_loop"));
|
||||
let is_non_recursive = matches!(union_layout, UnionLayout::NonRecursive(_));
|
||||
|
@ -340,33 +346,48 @@ fn eq_tag_union_help<'a>(
|
|||
let mut tag_branches = Vec::with_capacity_in(tag_layouts.len(), root.arena);
|
||||
|
||||
// If there's a null tag, check it first. We might not need to load any data from memory.
|
||||
if let Some(id) = nullable_id {
|
||||
tag_branches.push((id as u64, BranchInfo::None, Stmt::Ret(Symbol::BOOL_TRUE)))
|
||||
match nullable_id {
|
||||
NullableId::Wrapped(id) => {
|
||||
tag_branches.push((id as u64, BranchInfo::None, Stmt::Ret(Symbol::BOOL_TRUE)))
|
||||
}
|
||||
NullableId::Unwrapped(id) => tag_branches.push((
|
||||
id as TagIdIntType as u64,
|
||||
BranchInfo::None,
|
||||
Stmt::Ret(Symbol::BOOL_TRUE),
|
||||
)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut tag_id: TagIdIntType = 0;
|
||||
for field_layouts in tag_layouts.iter().take(tag_layouts.len() - 1) {
|
||||
if let Some(null_id) = nullable_id {
|
||||
if tag_id == null_id as TagIdIntType {
|
||||
tag_id += 1;
|
||||
let default_tag = if let NullableId::Unwrapped(tag_id) = nullable_id {
|
||||
(!tag_id) as TagIdIntType
|
||||
} else {
|
||||
let mut tag_id: TagIdIntType = 0;
|
||||
|
||||
for field_layouts in tag_layouts.iter().take(tag_layouts.len() - 1) {
|
||||
if let NullableId::Wrapped(null_id) = nullable_id {
|
||||
if tag_id == null_id as TagIdIntType {
|
||||
tag_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let tag_stmt = eq_tag_fields(
|
||||
root,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
tailrec_loop,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
operands,
|
||||
tag_id,
|
||||
);
|
||||
tag_branches.push((tag_id as u64, BranchInfo::None, tag_stmt));
|
||||
|
||||
tag_id += 1;
|
||||
}
|
||||
|
||||
let tag_stmt = eq_tag_fields(
|
||||
root,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
tailrec_loop,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
operands,
|
||||
tag_id,
|
||||
);
|
||||
tag_branches.push((tag_id as u64, BranchInfo::None, tag_stmt));
|
||||
|
||||
tag_id += 1;
|
||||
}
|
||||
tag_id
|
||||
};
|
||||
|
||||
let tag_switch_stmt = Stmt::Switch {
|
||||
cond_symbol: tag_id_a,
|
||||
|
@ -383,7 +404,7 @@ fn eq_tag_union_help<'a>(
|
|||
union_layout,
|
||||
tag_layouts.last().unwrap(),
|
||||
operands,
|
||||
tag_id,
|
||||
default_tag,
|
||||
)),
|
||||
),
|
||||
ret_layout: LAYOUT_BOOL,
|
||||
|
|
|
@ -31,6 +31,7 @@ pub enum HelperOp {
|
|||
Dec,
|
||||
DecRef(JoinPointId),
|
||||
Reset,
|
||||
ResetRef,
|
||||
Eq,
|
||||
}
|
||||
|
||||
|
@ -158,11 +159,42 @@ impl<'a> CodeGenHelp<'a> {
|
|||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
argument: Symbol,
|
||||
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
self.call_refcount(ident_ids, layout_interner, layout, argument, false)
|
||||
}
|
||||
|
||||
/**
|
||||
Call a resetref operation. It is similar to reset except it does not recursively decrement it's children when unique.
|
||||
*/
|
||||
pub fn call_resetref_refcount(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
argument: Symbol,
|
||||
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
self.call_refcount(ident_ids, layout_interner, layout, argument, true)
|
||||
}
|
||||
|
||||
/**
|
||||
Call either a reset or a resetref refcount operation.
|
||||
*/
|
||||
fn call_refcount(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
argument: Symbol,
|
||||
resetref: bool,
|
||||
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::Reset,
|
||||
op: if resetref {
|
||||
HelperOp::ResetRef
|
||||
} else {
|
||||
HelperOp::Reset
|
||||
},
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout);
|
||||
|
@ -262,7 +294,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let arg = self.replace_rec_ptr(ctx, layout_interner, layout);
|
||||
match ctx.op {
|
||||
Dec | DecRef(_) => (LAYOUT_UNIT, self.arena.alloc([arg])),
|
||||
Reset => (layout, self.arena.alloc([layout])),
|
||||
Reset | ResetRef => (layout, self.arena.alloc([layout])),
|
||||
Inc => (LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])),
|
||||
Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])),
|
||||
}
|
||||
|
@ -347,6 +379,17 @@ impl<'a> CodeGenHelp<'a> {
|
|||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
ResetRef => (
|
||||
layout,
|
||||
refcount::refcount_resetref_proc_body(
|
||||
self,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
Eq => (
|
||||
LAYOUT_BOOL,
|
||||
equality::eq_generic(self, ident_ids, ctx, layout_interner, layout),
|
||||
|
@ -360,7 +403,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let inc_amount = (self.layout_isize, ARG_2);
|
||||
self.arena.alloc([roc_value, inc_amount])
|
||||
}
|
||||
Dec | DecRef(_) | Reset => self.arena.alloc([roc_value]),
|
||||
Dec | DecRef(_) | Reset | ResetRef => self.arena.alloc([roc_value]),
|
||||
Eq => self.arena.alloc([roc_value, (layout, ARG_2)]),
|
||||
}
|
||||
};
|
||||
|
@ -372,7 +415,6 @@ impl<'a> CodeGenHelp<'a> {
|
|||
closure_data_layout: None,
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
});
|
||||
|
||||
|
@ -411,6 +453,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
niche: Niche::NONE,
|
||||
},
|
||||
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
|
||||
HelperOp::ResetRef => unreachable!("No generated Proc for ResetRef"),
|
||||
HelperOp::Eq => ProcLayout {
|
||||
arguments: self.arena.alloc([layout, layout]),
|
||||
result: LAYOUT_BOOL,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::collections::CollectIn;
|
||||
use roc_module::low_level::{LowLevel, LowLevel::*};
|
||||
use roc_module::symbol::{IdentIds, Symbol};
|
||||
use roc_target::PtrWidth;
|
||||
|
@ -256,42 +257,6 @@ pub fn refcount_reset_proc_body<'a>(
|
|||
)
|
||||
};
|
||||
|
||||
let alloc_addr_stmt = {
|
||||
let alignment = root.create_symbol(ident_ids, "alignment");
|
||||
let alignment_expr = Expr::Literal(Literal::Int(
|
||||
(layout_interner
|
||||
.get(layout)
|
||||
.alignment_bytes(layout_interner, root.target_info) as i128)
|
||||
.to_ne_bytes(),
|
||||
));
|
||||
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
|
||||
let alloc_addr_expr = Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::NumSubWrap,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: root.arena.alloc([addr, alignment]),
|
||||
});
|
||||
|
||||
Stmt::Let(
|
||||
alignment,
|
||||
alignment_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
alloc_addr,
|
||||
alloc_addr_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(alloc_addr),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let rc_contents_stmt = refcount_union_contents(
|
||||
root,
|
||||
ident_ids,
|
||||
|
@ -303,7 +268,7 @@ pub fn refcount_reset_proc_body<'a>(
|
|||
structure,
|
||||
tag_id_sym,
|
||||
tag_id_layout,
|
||||
alloc_addr_stmt,
|
||||
Stmt::Ret(addr),
|
||||
);
|
||||
|
||||
tag_id_stmt(root.arena.alloc(
|
||||
|
@ -413,6 +378,166 @@ pub fn refcount_reset_proc_body<'a>(
|
|||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
pub fn refcount_resetref_proc_body<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
structure: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let rc = root.create_symbol(ident_ids, "rc");
|
||||
let refcount_1 = root.create_symbol(ident_ids, "refcount_1");
|
||||
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
||||
let addr = root.create_symbol(ident_ids, "addr");
|
||||
|
||||
let union_layout = match layout_interner.get(layout) {
|
||||
Layout::Union(u) => u,
|
||||
_ => unimplemented!("Reset is only implemented for UnionLayout"),
|
||||
};
|
||||
|
||||
// Whenever we recurse into a child layout we will want to Decrement
|
||||
ctx.op = HelperOp::Dec;
|
||||
ctx.recursive_union = Some(union_layout);
|
||||
let recursion_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
|
||||
|
||||
// Reset structure is unique. Return a pointer to the allocation.
|
||||
let then_stmt = {
|
||||
let alignment = root.create_symbol(ident_ids, "alignment");
|
||||
let alignment_int = layout_interner
|
||||
.get(layout)
|
||||
.allocation_alignment_bytes(layout_interner, root.target_info);
|
||||
let alignment_expr = Expr::Literal(Literal::Int((alignment_int as i128).to_ne_bytes()));
|
||||
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
|
||||
let alloc_addr_expr = Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::NumSubWrap,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: root.arena.alloc([addr, alignment]),
|
||||
});
|
||||
|
||||
Stmt::Let(
|
||||
alignment,
|
||||
alignment_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
alloc_addr,
|
||||
alloc_addr_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(alloc_addr),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
// Reset structure is not unique. Decrement it and return a NULL pointer.
|
||||
let else_stmt = {
|
||||
let decrement_unit = root.create_symbol(ident_ids, "decrement_unit");
|
||||
let decrement_expr = root
|
||||
.call_specialized_op(
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
root.arena.alloc([structure]),
|
||||
)
|
||||
.unwrap();
|
||||
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
|
||||
|
||||
// Zero
|
||||
let zero = root.create_symbol(ident_ids, "zero");
|
||||
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
|
||||
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
|
||||
|
||||
// Null pointer with union layout
|
||||
let null = root.create_symbol(ident_ids, "null");
|
||||
let null_stmt =
|
||||
|next| let_lowlevel(root.arena, root.layout_isize, null, PtrCast, &[zero], next);
|
||||
|
||||
decrement_stmt(root.arena.alloc(
|
||||
//
|
||||
zero_stmt(root.arena.alloc(
|
||||
//
|
||||
null_stmt(root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(null),
|
||||
)),
|
||||
)),
|
||||
))
|
||||
};
|
||||
|
||||
let if_stmt = Stmt::Switch {
|
||||
cond_symbol: is_unique,
|
||||
cond_layout: LAYOUT_BOOL,
|
||||
branches: root.arena.alloc([(1, BranchInfo::None, then_stmt)]),
|
||||
default_branch: (BranchInfo::None, root.arena.alloc(else_stmt)),
|
||||
ret_layout: layout,
|
||||
};
|
||||
|
||||
// Uniqueness test
|
||||
let is_unique_stmt = {
|
||||
let_lowlevel(
|
||||
root.arena,
|
||||
LAYOUT_BOOL,
|
||||
is_unique,
|
||||
Eq,
|
||||
&[rc, refcount_1],
|
||||
root.arena.alloc(if_stmt),
|
||||
)
|
||||
};
|
||||
|
||||
// Constant for unique refcount
|
||||
let refcount_1_encoded = match root.target_info.ptr_width() {
|
||||
PtrWidth::Bytes4 => i32::MIN as i128,
|
||||
PtrWidth::Bytes8 => i64::MIN as i128,
|
||||
}
|
||||
.to_ne_bytes();
|
||||
let refcount_1_expr = Expr::Literal(Literal::Int(refcount_1_encoded));
|
||||
let refcount_1_stmt = Stmt::Let(
|
||||
refcount_1,
|
||||
refcount_1_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(is_unique_stmt),
|
||||
);
|
||||
|
||||
// Refcount value
|
||||
let rc_expr = Expr::UnionAtIndex {
|
||||
structure: rc_ptr,
|
||||
tag_id: 0,
|
||||
union_layout: root.union_refcount,
|
||||
index: 0,
|
||||
};
|
||||
let rc_stmt = Stmt::Let(
|
||||
rc,
|
||||
rc_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(refcount_1_stmt),
|
||||
);
|
||||
|
||||
// Refcount pointer
|
||||
let rc_ptr_stmt = {
|
||||
rc_ptr_from_data_ptr_help(
|
||||
root,
|
||||
ident_ids,
|
||||
structure,
|
||||
rc_ptr,
|
||||
union_layout.stores_tag_id_in_pointer(root.target_info),
|
||||
root.arena.alloc(rc_stmt),
|
||||
addr,
|
||||
recursion_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
fn rc_return_stmt<'a>(
|
||||
root: &CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
|
@ -517,12 +642,12 @@ pub fn rc_ptr_from_data_ptr_help<'a>(
|
|||
let rc_addr_sym = root.create_symbol(ident_ids, "rc_addr");
|
||||
let sub_expr = Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::NumSub,
|
||||
op: LowLevel::NumSubSaturated,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: root.arena.alloc([addr_sym, ptr_size_sym]),
|
||||
});
|
||||
let sub_stmt = |next| Stmt::Let(rc_addr_sym, sub_expr, root.layout_isize, next);
|
||||
let sub_stmt = |next| Stmt::Let(rc_addr_sym, sub_expr, Layout::usize(root.target_info), next);
|
||||
|
||||
// Typecast the refcount address from integer to pointer
|
||||
let cast_expr = Expr::Call(Call {
|
||||
|
@ -594,6 +719,7 @@ fn modify_refcount<'a>(
|
|||
}
|
||||
|
||||
HelperOp::Dec | HelperOp::DecRef(_) => {
|
||||
debug_assert!(alignment >= root.target_info.ptr_width() as u32);
|
||||
let alignment_sym = root.create_symbol(ident_ids, "alignment");
|
||||
let alignment_expr = Expr::Literal(Literal::Int((alignment as i128).to_ne_bytes()));
|
||||
let alignment_stmt = |next| Stmt::Let(alignment_sym, alignment_expr, LAYOUT_U32, next);
|
||||
|
@ -771,7 +897,10 @@ fn refcount_list<'a>(
|
|||
//
|
||||
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let elem_alignment = layout_interner.alignment_bytes(elem_layout);
|
||||
let alignment = Ord::max(
|
||||
root.target_info.ptr_width() as u32,
|
||||
layout_interner.alignment_bytes(elem_layout),
|
||||
);
|
||||
|
||||
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
|
||||
let modify_list = modify_refcount(
|
||||
|
@ -779,7 +908,7 @@ fn refcount_list<'a>(
|
|||
ident_ids,
|
||||
ctx,
|
||||
rc_ptr,
|
||||
elem_alignment,
|
||||
alignment,
|
||||
arena.alloc(ret_stmt),
|
||||
);
|
||||
|
||||
|
@ -1277,6 +1406,13 @@ fn refcount_union_contents<'a>(
|
|||
// (Order is important, to avoid use-after-free for Dec)
|
||||
let following = Stmt::Jump(jp_contents_modified, &[]);
|
||||
|
||||
let field_layouts = field_layouts
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.collect_in::<Vec<_>>(root.arena)
|
||||
.into_bump_slice();
|
||||
|
||||
let fields_stmt = refcount_tag_fields(
|
||||
root,
|
||||
ident_ids,
|
||||
|
@ -1341,8 +1477,8 @@ fn refcount_union_rec<'a>(
|
|||
let rc_structure_stmt = {
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
|
||||
let alignment =
|
||||
Layout::Union(union_layout).alignment_bytes(layout_interner, root.target_info);
|
||||
let alignment = Layout::Union(union_layout)
|
||||
.allocation_alignment_bytes(layout_interner, root.target_info);
|
||||
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
|
||||
let modify_structure_stmt = modify_refcount(
|
||||
root,
|
||||
|
@ -1453,7 +1589,7 @@ fn refcount_union_tailrec<'a>(
|
|||
)
|
||||
};
|
||||
|
||||
let alignment = layout_interner.alignment_bytes(layout);
|
||||
let alignment = layout_interner.allocation_alignment_bytes(layout);
|
||||
let modify_structure_stmt = modify_refcount(
|
||||
root,
|
||||
ident_ids,
|
||||
|
@ -1501,7 +1637,7 @@ fn refcount_union_tailrec<'a>(
|
|||
let mut tail_stmt = None;
|
||||
for (i, field) in field_layouts.iter().enumerate() {
|
||||
if i != tailrec_index {
|
||||
filtered.push(*field);
|
||||
filtered.push((i, *field));
|
||||
} else {
|
||||
let field_val =
|
||||
root.create_symbol(ident_ids, &format!("field_{}_{}", tag_id, i));
|
||||
|
@ -1535,7 +1671,14 @@ fn refcount_union_tailrec<'a>(
|
|||
)),
|
||||
));
|
||||
|
||||
(*field_layouts, tail_stmt)
|
||||
let field_layouts = field_layouts
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.collect_in::<Vec<_>>(root.arena)
|
||||
.into_bump_slice();
|
||||
|
||||
(field_layouts, tail_stmt)
|
||||
};
|
||||
|
||||
let fields_stmt = refcount_tag_fields(
|
||||
|
@ -1606,20 +1749,20 @@ fn refcount_tag_fields<'a>(
|
|||
ctx: &mut Context<'a>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
field_layouts: &'a [InLayout<'a>],
|
||||
field_layouts: &'a [(usize, InLayout<'a>)],
|
||||
structure: Symbol,
|
||||
tag_id: TagIdIntType,
|
||||
following: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let mut stmt = following;
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate().rev() {
|
||||
for (i, field_layout) in field_layouts.iter().rev() {
|
||||
if layout_interner.contains_refcounted(*field_layout) {
|
||||
let field_val = root.create_symbol(ident_ids, &format!("field_{}_{}", tag_id, i));
|
||||
let field_val_expr = Expr::UnionAtIndex {
|
||||
union_layout,
|
||||
tag_id,
|
||||
index: i as u64,
|
||||
index: *i as u64,
|
||||
structure,
|
||||
};
|
||||
let field_val_stmt = |next| Stmt::Let(field_val, field_val_expr, *field_layout, next);
|
||||
|
@ -1662,7 +1805,7 @@ fn refcount_boxed<'a>(
|
|||
//
|
||||
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let alignment = layout_interner.alignment_bytes(layout);
|
||||
let alignment = layout_interner.allocation_alignment_bytes(layout);
|
||||
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
|
||||
let modify_outer = modify_refcount(
|
||||
root,
|
||||
|
|
|
@ -474,6 +474,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
|||
&Expr::Reset {
|
||||
symbol,
|
||||
update_mode: _,
|
||||
}
|
||||
| &Expr::ResetRef {
|
||||
symbol,
|
||||
update_mode: _,
|
||||
} => {
|
||||
self.check_sym_exists(symbol);
|
||||
None
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ use crate::ir::literal::{make_num_literal, IntOrFloatValue};
|
|||
use crate::layout::{
|
||||
self, Builtin, ClosureCallOptions, ClosureRepresentation, EnumDispatch, InLayout, LambdaName,
|
||||
LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, Niche, RawFunctionLayout,
|
||||
STLayoutInterner, TLLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
|
||||
TLLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
|
||||
};
|
||||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -308,7 +308,6 @@ pub struct Proc<'a> {
|
|||
pub closure_data_layout: Option<InLayout<'a>>,
|
||||
pub ret_layout: InLayout<'a>,
|
||||
pub is_self_recursive: SelfRecursive,
|
||||
pub must_own_arguments: bool,
|
||||
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||
}
|
||||
|
||||
|
@ -408,50 +407,6 @@ impl<'a> Proc<'a> {
|
|||
String::from_utf8(w).unwrap()
|
||||
}
|
||||
|
||||
pub fn insert_refcount_operations<'i>(
|
||||
arena: &'a Bump,
|
||||
layout_interner: &'i STLayoutInterner<'a>,
|
||||
home: ModuleId,
|
||||
ident_ids: &'i mut IdentIds,
|
||||
update_mode_ids: &'i mut UpdateModeIds,
|
||||
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
host_exposed_procs: &[Symbol],
|
||||
) {
|
||||
let borrow_params =
|
||||
crate::borrow::infer_borrow(arena, layout_interner, procs, host_exposed_procs);
|
||||
|
||||
crate::inc_dec::visit_procs(
|
||||
arena,
|
||||
layout_interner,
|
||||
home,
|
||||
ident_ids,
|
||||
update_mode_ids,
|
||||
arena.alloc(borrow_params),
|
||||
procs,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_reset_reuse_operations<'i>(
|
||||
arena: &'a Bump,
|
||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||
home: ModuleId,
|
||||
ident_ids: &'i mut IdentIds,
|
||||
update_mode_ids: &'i mut UpdateModeIds,
|
||||
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
) {
|
||||
for proc in procs.values_mut() {
|
||||
let new_proc = crate::reset_reuse::insert_reset_reuse(
|
||||
arena,
|
||||
layout_interner,
|
||||
home,
|
||||
ident_ids,
|
||||
update_mode_ids,
|
||||
proc.clone(),
|
||||
);
|
||||
*proc = new_proc;
|
||||
}
|
||||
}
|
||||
|
||||
fn make_tail_recursive(&mut self, env: &mut Env<'a, '_>) {
|
||||
let mut args = Vec::with_capacity_in(self.args.len(), env.arena);
|
||||
let mut proc_args = Vec::with_capacity_in(self.args.len(), env.arena);
|
||||
|
@ -1926,6 +1881,13 @@ pub enum Expr<'a> {
|
|||
update_mode: UpdateModeId,
|
||||
},
|
||||
|
||||
// Just like Reset, but does not recursively decrement the children.
|
||||
// Used in reuse analysis to replace a decref with a resetRef to avoid decrementing when the dec ref didn't.
|
||||
ResetRef {
|
||||
symbol: Symbol,
|
||||
update_mode: UpdateModeId,
|
||||
},
|
||||
|
||||
RuntimeErrorFunction(&'a str),
|
||||
}
|
||||
|
||||
|
@ -2045,11 +2007,21 @@ impl<'a> Expr<'a> {
|
|||
Reset {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => alloc.text(format!(
|
||||
"Reset {{ symbol: {:?}, id: {} }}",
|
||||
symbol, update_mode.id
|
||||
)),
|
||||
|
||||
} => alloc
|
||||
.text("Reset { symbol: ")
|
||||
.append(symbol_to_doc(alloc, *symbol, pretty))
|
||||
.append(", id: ")
|
||||
.append(format!("{:?}", update_mode))
|
||||
.append(" }"),
|
||||
ResetRef {
|
||||
symbol,
|
||||
update_mode,
|
||||
} => alloc
|
||||
.text("ResetRef { symbol: ")
|
||||
.append(symbol_to_doc(alloc, *symbol, pretty))
|
||||
.append(", id: ")
|
||||
.append(format!("{:?}", update_mode))
|
||||
.append(" }"),
|
||||
Struct(args) => {
|
||||
let it = args.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
|
||||
|
||||
|
@ -3202,7 +3174,6 @@ fn generate_runtime_error_function<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
}
|
||||
}
|
||||
|
@ -3295,7 +3266,6 @@ fn generate_host_exposed_function<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: result,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
|
@ -3360,7 +3330,6 @@ fn generate_host_exposed_lambda_set<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: return_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
|
@ -3457,7 +3426,6 @@ fn specialize_proc_help<'a>(
|
|||
closure_data_layout: Some(closure_data_layout),
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts,
|
||||
}
|
||||
}
|
||||
|
@ -3657,7 +3625,6 @@ fn specialize_proc_help<'a>(
|
|||
closure_data_layout,
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts,
|
||||
}
|
||||
}
|
||||
|
@ -5049,9 +5016,12 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
}
|
||||
CopyExisting(index) => {
|
||||
let record_needs_specialization =
|
||||
procs.ability_member_aliases.get(structure).is_some();
|
||||
let specialized_structure_sym = if record_needs_specialization {
|
||||
let structure_needs_specialization =
|
||||
procs.ability_member_aliases.get(structure).is_some()
|
||||
|| procs.is_module_thunk(structure)
|
||||
|| procs.is_imported_module_thunk(structure);
|
||||
|
||||
let specialized_structure_sym = if structure_needs_specialization {
|
||||
// We need to specialize the record now; create a new one for it.
|
||||
// TODO: reuse this symbol for all updates
|
||||
env.unique_symbol()
|
||||
|
@ -5068,10 +5038,7 @@ pub fn with_hole<'a>(
|
|||
stmt =
|
||||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
||||
|
||||
// If the records needs specialization or it's a thunk, we need to
|
||||
// create the specialized definition or force the thunk, respectively.
|
||||
// Both cases are handled below.
|
||||
if record_needs_specialization || procs.is_module_thunk(structure) {
|
||||
if structure_needs_specialization {
|
||||
stmt = specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
|
@ -5500,7 +5467,7 @@ pub fn with_hole<'a>(
|
|||
let passed_function = PassedFunction {
|
||||
name: lambda_name,
|
||||
captured_environment: closure_data_symbol,
|
||||
owns_captured_environment: false,
|
||||
owns_captured_environment: true,
|
||||
specialization_id,
|
||||
argument_layouts: arg_layouts,
|
||||
return_layout: ret_layout,
|
||||
|
@ -7501,7 +7468,9 @@ fn substitute_in_expr<'a>(
|
|||
|
||||
NullPointer => None,
|
||||
|
||||
Reuse { .. } | Reset { .. } => unreachable!("reset/reuse have not been introduced yet"),
|
||||
Reuse { .. } | Reset { .. } | ResetRef { .. } => {
|
||||
unreachable!("reset/resetref/reuse have not been introduced yet")
|
||||
}
|
||||
|
||||
Struct(args) => {
|
||||
let mut did_change = false;
|
||||
|
@ -9764,7 +9733,6 @@ where
|
|||
closure_data_layout: None,
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
|
@ -9860,7 +9828,6 @@ where
|
|||
closure_data_layout: None,
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
|
|
|
@ -2661,9 +2661,10 @@ impl<'a> Layout<'a> {
|
|||
Layout::RecursivePointer(_) => {
|
||||
unreachable!("should be looked up to get an actual layout")
|
||||
}
|
||||
Layout::Boxed(inner) => interner
|
||||
.get(*inner)
|
||||
.allocation_alignment_bytes(interner, target_info),
|
||||
Layout::Boxed(inner) => Ord::max(
|
||||
ptr_width,
|
||||
interner.get(*inner).alignment_bytes(interner, target_info),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3609,7 +3609,7 @@ fn check_for_infinite_type(
|
|||
}
|
||||
Content::LambdaSet(subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var: _,
|
||||
recursion_var: OptVariable::NONE,
|
||||
unspecialized,
|
||||
ambient_function: ambient_function_var,
|
||||
}) => {
|
||||
|
|
|
@ -31,7 +31,7 @@ mod solve_expr {
|
|||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, false)?;
|
||||
) = run_load_and_infer(src, [], false)?;
|
||||
|
||||
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
@ -103,7 +103,7 @@ mod solve_expr {
|
|||
interns,
|
||||
abilities_store,
|
||||
..
|
||||
} = run_load_and_infer(src, false).unwrap().0;
|
||||
} = run_load_and_infer(src, [], false).unwrap().0;
|
||||
|
||||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
|
|
@ -69,5 +69,9 @@ gen-wasm = []
|
|||
name = "list_map"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "quicksort"
|
||||
harness = false
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
development = ["roc_wasm_interp"]
|
||||
development = ["roc_wasm_interp"]
|
||||
|
|
|
@ -10,11 +10,16 @@ use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc
|
|||
use roc_mono::ir::OptLevel;
|
||||
use roc_std::RocList;
|
||||
|
||||
// results July 6th, 2022
|
||||
// results July 6, 2022
|
||||
//
|
||||
// roc sum map time: [612.73 ns 614.24 ns 615.98 ns]
|
||||
// roc sum map_with_index time: [5.3177 us 5.3218 us 5.3255 us]
|
||||
// rust (debug) time: [24.081 us 24.163 us 24.268 us]
|
||||
//
|
||||
// results April 9, 2023
|
||||
//
|
||||
// roc sum map time: [510.77 ns 517.47 ns 524.47 ns]
|
||||
// roc sum map_with_index time: [573.49 ns 578.17 ns 583.76 ns]
|
||||
|
||||
type Input = RocList<i64>;
|
||||
type Output = i64;
|
||||
|
@ -83,6 +88,9 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| unsafe {
|
||||
let mut main_result = RocCallResult::default();
|
||||
|
||||
// the roc code will dec this list, so inc it first so it is not free'd
|
||||
std::mem::forget(input.clone());
|
||||
|
||||
list_map_main(black_box(input), &mut main_result);
|
||||
})
|
||||
});
|
||||
|
@ -91,6 +99,9 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| unsafe {
|
||||
let mut main_result = RocCallResult::default();
|
||||
|
||||
// the roc code will dec this list, so inc it first so it is not free'd
|
||||
std::mem::forget(input.clone());
|
||||
|
||||
list_map_with_index_main(black_box(input), &mut main_result);
|
||||
})
|
||||
});
|
||||
|
|
158
crates/compiler/test_gen/benches/quicksort.rs
Normal file
158
crates/compiler/test_gen/benches/quicksort.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
#[path = "../src/helpers/mod.rs"]
|
||||
mod helpers;
|
||||
|
||||
// defines roc_alloc and friends
|
||||
pub use helpers::platform_functions::*;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_std::RocList;
|
||||
|
||||
// results April 9, 2023
|
||||
//
|
||||
// > pure roc quicksort time: [106.97 us 107.27 us 107.63 us]
|
||||
// > roc zig quicksort time: [34.765 us 35.301 us 35.865 us]
|
||||
// > rust std sort time: [20.413 us 20.623 us 20.838 us]
|
||||
|
||||
type Input = RocList<i64>;
|
||||
type Output = RocList<i64>;
|
||||
|
||||
type Main<I, O> = unsafe extern "C" fn(I, *mut RocCallResult<O>);
|
||||
|
||||
const ZIG_ROC_QUICKSORT: &str = indoc::indoc!(
|
||||
r#"
|
||||
app "bench" provides [main] to "./platform"
|
||||
|
||||
main : List I64 -> List I64
|
||||
main = \originalList -> List.sortAsc originalList
|
||||
"#
|
||||
);
|
||||
|
||||
const PURE_ROC_QUICKSORT: &str = indoc::indoc!(
|
||||
r#"
|
||||
app "bench" provides [main] to "./platform"
|
||||
|
||||
main : List I64 -> List I64
|
||||
main = \originalList ->
|
||||
n = List.len originalList
|
||||
|
||||
quicksortHelp originalList 0 (n - 1)
|
||||
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
partition : Nat, Nat, List (Num a) -> [Pair Nat (List (Num a))]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp low low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair newI (List.swap newList newI high)
|
||||
|
||||
Err _ ->
|
||||
Pair low initialList
|
||||
|
||||
partitionHelp : Nat, Nat, List (Num c), Nat, Num c -> [Pair Nat (List (Num c))]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (List.swap list i j) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
"#
|
||||
);
|
||||
|
||||
fn roc_function<'a>(
|
||||
arena: &'a Bump,
|
||||
source: &str,
|
||||
) -> libloading::Symbol<'a, Main<*mut Input, Output>> {
|
||||
let config = helpers::llvm::HelperConfig {
|
||||
mode: LlvmBackendMode::GenTest,
|
||||
ignore_problems: false,
|
||||
add_debug_info: true,
|
||||
opt_level: OptLevel::Optimize,
|
||||
};
|
||||
|
||||
let context = inkwell::context::Context::create();
|
||||
let (main_fn_name, errors, lib) =
|
||||
helpers::llvm::helper(arena, config, source, arena.alloc(context));
|
||||
|
||||
assert!(errors.is_empty(), "Encountered errors:\n{}", errors);
|
||||
|
||||
run_roc_dylib!(arena.alloc(lib), main_fn_name, *mut Input, Output)
|
||||
}
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let arena = Bump::new();
|
||||
|
||||
let pure_roc_quicksort_main = roc_function(&arena, PURE_ROC_QUICKSORT);
|
||||
let roc_zig_quicksort_main = roc_function(&arena, ZIG_ROC_QUICKSORT);
|
||||
|
||||
let input_numbers: Vec<_> = std::iter::repeat([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
.flatten()
|
||||
.take(1000)
|
||||
.collect();
|
||||
|
||||
let input = arena.alloc(RocList::from_slice(&input_numbers));
|
||||
|
||||
c.bench_function("pure roc quicksort", |b| {
|
||||
b.iter(|| unsafe {
|
||||
let mut main_result = RocCallResult::default();
|
||||
|
||||
assert!(input.is_unique());
|
||||
|
||||
// reset_input
|
||||
input.copy_from_slice(&input_numbers);
|
||||
|
||||
pure_roc_quicksort_main(black_box(input), &mut main_result);
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("roc zig quicksort", |b| {
|
||||
b.iter(|| unsafe {
|
||||
let mut main_result = RocCallResult::default();
|
||||
|
||||
assert!(input.is_unique());
|
||||
|
||||
// reset_input
|
||||
input.copy_from_slice(&input_numbers);
|
||||
|
||||
roc_zig_quicksort_main(black_box(input), &mut main_result);
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("rust std sort", |b| {
|
||||
b.iter(|| unsafe {
|
||||
assert!(input.is_unique());
|
||||
|
||||
// reset_input
|
||||
input.copy_from_slice(&input_numbers);
|
||||
|
||||
// an attempt to block optimizing based on the input list
|
||||
let ptr = black_box(input.as_mut_ptr());
|
||||
let input = std::slice::from_raw_parts_mut(ptr, 1000);
|
||||
|
||||
input.sort()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(quicksort_benches, criterion_benchmark);
|
||||
criterion_main!(quicksort_benches);
|
|
@ -2257,7 +2257,7 @@ fn gen_wrap_len() {
|
|||
"#
|
||||
),
|
||||
RocList::from_slice(&[3]),
|
||||
RocList<i64>
|
||||
RocList<usize>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -496,3 +496,78 @@ fn boxed_str_dec() {
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-wasm"))]
|
||||
fn non_nullable_unwrapped_alignment_8() {
|
||||
assert_refcounts!(
|
||||
indoc!(
|
||||
r#"
|
||||
Expr : [ZAdd Expr Expr, Val I64, Var I64]
|
||||
|
||||
eval : Expr -> I64
|
||||
eval = \e ->
|
||||
when e is
|
||||
Var _ -> 0
|
||||
Val v -> v
|
||||
ZAdd l r -> eval l + eval r
|
||||
|
||||
expr : Expr
|
||||
expr = (ZAdd (Val 4) (Val 5))
|
||||
|
||||
eval expr
|
||||
"#
|
||||
),
|
||||
i64,
|
||||
&[
|
||||
Deallocated, // Val 4
|
||||
Deallocated, // Val 5
|
||||
Deallocated, // ZAdd _ _
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-wasm"))]
|
||||
fn reset_reuse_alignment_8() {
|
||||
assert_refcounts!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Expr : [ZAdd Expr Expr, Val I64, Var I64]
|
||||
|
||||
eval : Expr -> I64
|
||||
eval = \e ->
|
||||
when e is
|
||||
Var _ -> 0
|
||||
Val v -> v
|
||||
ZAdd l r -> eval l + eval r
|
||||
|
||||
constFolding : Expr -> Expr
|
||||
constFolding = \e ->
|
||||
when e is
|
||||
ZAdd e1 e2 ->
|
||||
when Pair e1 e2 is
|
||||
Pair (Val a) (Val b) -> Val (a+b)
|
||||
Pair _ _ -> ZAdd e1 e2
|
||||
|
||||
|
||||
_ -> e
|
||||
|
||||
|
||||
expr : Expr
|
||||
expr = ZAdd (Val 4) (Val 5)
|
||||
|
||||
main : I64
|
||||
main = eval (constFolding expr)
|
||||
"#
|
||||
),
|
||||
i64,
|
||||
&[
|
||||
Deallocated, // Val 4
|
||||
Deallocated, // Val 5
|
||||
Deallocated, // ZAdd _ _
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -116,7 +116,6 @@ fn build_app_mono<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: int_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
|
|
|
@ -68,10 +68,12 @@ procedure List.80 (List.544, List.545, List.546, List.547, List.548):
|
|||
let List.523 : U64 = CallByName Num.19 List.436 List.524;
|
||||
jump List.518 List.433 List.438 List.435 List.523 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
let List.439 : U64 = UnionAtIndex (Id 0) (Index 0) List.521;
|
||||
let List.525 : [C U64, C U64] = TagId(0) List.439;
|
||||
ret List.525;
|
||||
else
|
||||
dec List.433;
|
||||
let List.519 : [C U64, C U64] = TagId(1) List.434;
|
||||
ret List.519;
|
||||
in
|
||||
|
@ -98,6 +100,7 @@ procedure Num.77 (#Attr.2, #Attr.3):
|
|||
procedure Test.1 (Test.2):
|
||||
let Test.13 : U64 = 0i64;
|
||||
let Test.14 : {} = Struct {};
|
||||
inc Test.2;
|
||||
let Test.3 : U64 = CallByName List.26 Test.2 Test.13 Test.14;
|
||||
let Test.12 : U64 = 0i64;
|
||||
let Test.10 : Int1 = CallByName Bool.11 Test.3 Test.12;
|
||||
|
|
|
@ -7,15 +7,18 @@ procedure List.2 (List.96, List.97):
|
|||
let List.504 : Int1 = CallByName Num.22 List.97 List.508;
|
||||
if List.504 then
|
||||
let List.506 : Str = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.505 : [C {}, C Str] = TagId(1) List.506;
|
||||
ret List.505;
|
||||
else
|
||||
dec List.96;
|
||||
let List.503 : {} = Struct {};
|
||||
let List.502 : [C {}, C Str] = TagId(0) List.503;
|
||||
ret List.502;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.510 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.510;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
|
@ -53,13 +56,13 @@ procedure Result.5 (Result.12, Result.13):
|
|||
let Result.40 : U8 = GetTagId Result.12;
|
||||
let Result.41 : Int1 = lowlevel Eq Result.39 Result.40;
|
||||
if Result.41 then
|
||||
dec Result.13;
|
||||
let Result.14 : Str = UnionAtIndex (Id 1) (Index 0) Result.12;
|
||||
inc Result.14;
|
||||
dec Result.12;
|
||||
ret Result.14;
|
||||
else
|
||||
dec Result.12;
|
||||
inc Result.13;
|
||||
ret Result.13;
|
||||
|
||||
procedure Test.10 (Test.11):
|
||||
|
@ -78,6 +81,8 @@ procedure Test.2 (Test.6):
|
|||
let Test.31 : Int1 = lowlevel Eq Test.29 Test.30;
|
||||
if Test.31 then
|
||||
let Test.7 : [<r>C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6;
|
||||
inc Test.7;
|
||||
dec Test.6;
|
||||
let Test.8 : Str = CallByName Test.2 Test.7;
|
||||
let Test.18 : Int1 = CallByName Bool.1;
|
||||
if Test.18 then
|
||||
|
@ -88,18 +93,17 @@ procedure Test.2 (Test.6):
|
|||
ret Test.17;
|
||||
else
|
||||
let Test.9 : List [<r>C List *self, C *self] = UnionAtIndex (Id 0) (Index 0) Test.6;
|
||||
inc Test.9;
|
||||
dec Test.6;
|
||||
let Test.24 : {} = Struct {};
|
||||
let Test.23 : List Str = CallByName List.5 Test.9 Test.24;
|
||||
let Test.21 : [C {}, C Str] = CallByName List.9 Test.23;
|
||||
dec Test.23;
|
||||
let Test.22 : Str = "foo";
|
||||
let Test.20 : Str = CallByName Result.5 Test.21 Test.22;
|
||||
dec Test.22;
|
||||
ret Test.20;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.32 : List [<r>C List *self, C *self] = Array [];
|
||||
let Test.15 : [<r>C List *self, C *self] = TagId(0) Test.32;
|
||||
let Test.14 : Str = CallByName Test.2 Test.15;
|
||||
dec Test.15;
|
||||
ret Test.14;
|
||||
|
|
|
@ -28,6 +28,7 @@ procedure List.80 (List.517, List.518, List.519, List.520, List.521):
|
|||
let List.505 : U64 = CallByName Num.19 List.436 List.506;
|
||||
jump List.500 List.433 List.503 List.435 List.505 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.500 List.517 List.518 List.519 List.520 List.521;
|
||||
|
@ -65,7 +66,6 @@ procedure Test.11 (Test.53, Test.54):
|
|||
case 0:
|
||||
dec Test.7;
|
||||
let Test.28 : Str = CallByName Test.2 Test.29;
|
||||
dec Test.29;
|
||||
ret Test.28;
|
||||
|
||||
case 1:
|
||||
|
@ -89,7 +89,6 @@ procedure Test.11 (Test.53, Test.54):
|
|||
jump Test.27 Test.53 Test.54;
|
||||
|
||||
procedure Test.2 (Test.13):
|
||||
inc Test.13;
|
||||
ret Test.13;
|
||||
|
||||
procedure Test.3 (Test.14):
|
||||
|
@ -136,7 +135,6 @@ procedure Test.9 (Test.10, #Attr.12):
|
|||
case 0:
|
||||
dec Test.7;
|
||||
let Test.39 : Str = CallByName Test.2 Test.10;
|
||||
dec Test.10;
|
||||
jump Test.38 Test.39;
|
||||
|
||||
case 1:
|
||||
|
@ -156,14 +154,12 @@ procedure Test.0 ():
|
|||
let Test.23 : Int1 = CallByName Bool.2;
|
||||
let Test.22 : Int1 = CallByName Test.1 Test.23;
|
||||
let Test.16 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName List.18 Test.20 Test.21 Test.22;
|
||||
dec Test.20;
|
||||
let Test.18 : Str = "hello";
|
||||
let Test.19 : U8 = GetTagId Test.16;
|
||||
switch Test.19:
|
||||
case 0:
|
||||
dec Test.16;
|
||||
let Test.17 : Str = CallByName Test.2 Test.18;
|
||||
dec Test.18;
|
||||
ret Test.17;
|
||||
|
||||
case 1:
|
||||
|
|
|
@ -7,9 +7,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
|
||||
if List.496 then
|
||||
let List.498 : {} = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.497 : [C {}, C {}] = TagId(1) List.498;
|
||||
ret List.497;
|
||||
else
|
||||
dec List.96;
|
||||
let List.495 : {} = Struct {};
|
||||
let List.494 : [C {}, C {}] = TagId(0) List.495;
|
||||
ret List.494;
|
||||
|
@ -27,6 +29,7 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
ret Num.275;
|
||||
|
||||
procedure Test.2 (Test.5):
|
||||
dec Test.5;
|
||||
let Test.17 : Str = "bar";
|
||||
ret Test.17;
|
||||
|
||||
|
@ -35,7 +38,6 @@ procedure Test.0 ():
|
|||
joinpoint Test.15 Test.3:
|
||||
let Test.13 : U64 = 0i64;
|
||||
let Test.6 : [C {}, C {}] = CallByName List.2 Test.3 Test.13;
|
||||
dec Test.3;
|
||||
let Test.10 : U8 = 1i64;
|
||||
let Test.11 : U8 = GetTagId Test.6;
|
||||
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
||||
|
@ -43,7 +45,6 @@ procedure Test.0 ():
|
|||
let Test.4 : {} = UnionAtIndex (Id 1) (Index 0) Test.6;
|
||||
let Test.8 : Str = "foo";
|
||||
let Test.7 : Str = CallByName Test.2 Test.8;
|
||||
dec Test.8;
|
||||
ret Test.7;
|
||||
else
|
||||
let Test.9 : Str = "bad!";
|
||||
|
|
|
@ -88,7 +88,6 @@ procedure Json.162 (Json.163, Json.904, Json.161):
|
|||
let Json.912 : {List U8, U64} = Struct {Json.165, Json.935};
|
||||
let Json.913 : {} = Struct {};
|
||||
let Json.911 : {List U8, U64} = CallByName List.18 Json.161 Json.912 Json.913;
|
||||
dec Json.161;
|
||||
let Json.167 : List U8 = StructAtIndex 0 Json.911;
|
||||
inc Json.167;
|
||||
dec Json.911;
|
||||
|
@ -105,7 +104,6 @@ procedure Json.162 (Json.163, Json.904, Json.161):
|
|||
let Json.952 : {List U8, U64} = Struct {Json.165, Json.975};
|
||||
let Json.953 : {} = Struct {};
|
||||
let Json.951 : {List U8, U64} = CallByName List.18 Json.161 Json.952 Json.953;
|
||||
dec Json.161;
|
||||
let Json.167 : List U8 = StructAtIndex 0 Json.951;
|
||||
inc Json.167;
|
||||
dec Json.951;
|
||||
|
@ -268,6 +266,7 @@ procedure List.80 (List.547, List.548, List.549, List.550, List.551):
|
|||
let List.527 : U64 = CallByName Num.19 List.436 List.528;
|
||||
jump List.522 List.433 List.525 List.435 List.527 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.522 List.547 List.548 List.549 List.550 List.551;
|
||||
|
@ -282,6 +281,7 @@ procedure List.80 (List.621, List.622, List.623, List.624, List.625):
|
|||
let List.600 : U64 = CallByName Num.19 List.436 List.601;
|
||||
jump List.595 List.433 List.598 List.435 List.600 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.595 List.621 List.622 List.623 List.624 List.625;
|
||||
|
|
|
@ -61,7 +61,6 @@ procedure Json.162 (Json.163, Json.904, Json.161):
|
|||
let Json.912 : {List U8, U64} = Struct {Json.165, Json.935};
|
||||
let Json.913 : {} = Struct {};
|
||||
let Json.911 : {List U8, U64} = CallByName List.18 Json.161 Json.912 Json.913;
|
||||
dec Json.161;
|
||||
let Json.167 : List U8 = StructAtIndex 0 Json.911;
|
||||
inc Json.167;
|
||||
dec Json.911;
|
||||
|
@ -165,6 +164,7 @@ procedure List.80 (List.554, List.555, List.556, List.557, List.558):
|
|||
let List.533 : U64 = CallByName Num.19 List.436 List.534;
|
||||
jump List.528 List.433 List.531 List.435 List.533 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.528 List.554 List.555 List.556 List.557 List.558;
|
||||
|
|
|
@ -69,7 +69,6 @@ procedure Json.162 (Json.163, Json.904, Json.161):
|
|||
let Json.912 : {List U8, U64} = Struct {Json.165, Json.935};
|
||||
let Json.913 : {} = Struct {};
|
||||
let Json.911 : {List U8, U64} = CallByName List.18 Json.161 Json.912 Json.913;
|
||||
dec Json.161;
|
||||
let Json.167 : List U8 = StructAtIndex 0 Json.911;
|
||||
inc Json.167;
|
||||
dec Json.911;
|
||||
|
@ -173,6 +172,7 @@ procedure List.80 (List.554, List.555, List.556, List.557, List.558):
|
|||
let List.533 : U64 = CallByName Num.19 List.436 List.534;
|
||||
jump List.528 List.433 List.531 List.435 List.533 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.528 List.554 List.555 List.556 List.557 List.558;
|
||||
|
|
|
@ -82,7 +82,6 @@ procedure Json.188 (Json.189, Json.904, #Attr.12):
|
|||
let Json.914 : {List U8, U64} = Struct {Json.191, Json.926};
|
||||
let Json.915 : {} = Struct {};
|
||||
let Json.913 : {List U8, U64} = CallByName List.18 Json.187 Json.914 Json.915;
|
||||
dec Json.187;
|
||||
let Json.193 : List U8 = StructAtIndex 0 Json.913;
|
||||
inc Json.193;
|
||||
dec Json.913;
|
||||
|
@ -174,6 +173,7 @@ procedure List.80 (List.560, List.561, List.562, List.563, List.564):
|
|||
let List.539 : U64 = CallByName Num.19 List.436 List.540;
|
||||
jump List.534 List.433 List.537 List.435 List.539 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.534 List.560 List.561 List.562 List.563 List.564;
|
||||
|
|
|
@ -88,7 +88,6 @@ procedure Json.188 (Json.189, Json.904, #Attr.12):
|
|||
let Json.914 : {List U8, U64} = Struct {Json.191, Json.926};
|
||||
let Json.915 : {} = Struct {};
|
||||
let Json.913 : {List U8, U64} = CallByName List.18 Json.187 Json.914 Json.915;
|
||||
dec Json.187;
|
||||
let Json.193 : List U8 = StructAtIndex 0 Json.913;
|
||||
inc Json.193;
|
||||
dec Json.913;
|
||||
|
@ -180,6 +179,7 @@ procedure List.80 (List.560, List.561, List.562, List.563, List.564):
|
|||
let List.539 : U64 = CallByName Num.19 List.436 List.540;
|
||||
jump List.534 List.433 List.537 List.435 List.539 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.534 List.560 List.561 List.562 List.563 List.564;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
procedure Test.1 (Test.2, Test.3):
|
||||
inc Test.2;
|
||||
dec Test.3;
|
||||
ret Test.2;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.5 : List I64 = Array [1i64, 2i64, 3i64];
|
||||
let Test.6 : List I64 = Array [3i64, 2i64, 1i64];
|
||||
let Test.4 : List I64 = CallByName Test.1 Test.5 Test.6;
|
||||
dec Test.6;
|
||||
dec Test.5;
|
||||
ret Test.4;
|
||||
|
|
|
@ -9,6 +9,7 @@ procedure Bool.2 ():
|
|||
procedure Test.2 (Test.4):
|
||||
let Test.11 : U8 = 1i64;
|
||||
let Test.12 : U8 = GetTagId Test.4;
|
||||
dec Test.4;
|
||||
let Test.13 : Int1 = lowlevel Eq Test.11 Test.12;
|
||||
if Test.13 then
|
||||
let Test.9 : Int1 = CallByName Bool.2;
|
||||
|
@ -22,5 +23,4 @@ procedure Test.0 ():
|
|||
let Test.15 : [<rnu><null>, C I64 *self] = TagId(1) ;
|
||||
let Test.8 : [<rnu><null>, C I64 *self] = TagId(0) Test.14 Test.15;
|
||||
let Test.7 : Int1 = CallByName Test.2 Test.8;
|
||||
dec Test.8;
|
||||
ret Test.7;
|
||||
|
|
|
@ -11,9 +11,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.504 : Int1 = CallByName Num.22 List.97 List.508;
|
||||
if List.504 then
|
||||
let List.506 : I64 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.505 : [C {}, C I64] = TagId(1) List.506;
|
||||
ret List.505;
|
||||
else
|
||||
dec List.96;
|
||||
let List.503 : {} = Struct {};
|
||||
let List.502 : [C {}, C I64] = TagId(0) List.503;
|
||||
ret List.502;
|
||||
|
@ -55,6 +57,7 @@ procedure Str.47 (#Attr.2):
|
|||
|
||||
procedure Str.72 (Str.244):
|
||||
let Str.245 : {I64, U8} = CallByName Str.47 Str.244;
|
||||
dec Str.244;
|
||||
let Str.304 : U8 = StructAtIndex 1 Str.245;
|
||||
let Str.305 : U8 = 0i64;
|
||||
let Str.301 : Int1 = CallByName Bool.11 Str.304 Str.305;
|
||||
|
@ -72,10 +75,8 @@ procedure Test.0 ():
|
|||
if Test.3 then
|
||||
let Test.5 : List I64 = Array [];
|
||||
let Test.4 : [C Int1, C I64] = CallByName List.9 Test.5;
|
||||
dec Test.5;
|
||||
ret Test.4;
|
||||
else
|
||||
let Test.2 : Str = "";
|
||||
let Test.1 : [C Int1, C I64] = CallByName Str.27 Test.2;
|
||||
dec Test.2;
|
||||
ret Test.1;
|
||||
|
|
|
@ -8,10 +8,13 @@ procedure Test.2 (Test.19):
|
|||
let Test.17 : U8 = GetTagId Test.7;
|
||||
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
|
||||
if Test.18 then
|
||||
dec Test.7;
|
||||
let Test.14 : {} = Struct {};
|
||||
ret Test.14;
|
||||
else
|
||||
let Test.5 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.7;
|
||||
inc Test.5;
|
||||
dec Test.7;
|
||||
jump Test.13 Test.5;
|
||||
in
|
||||
jump Test.13 Test.19;
|
||||
|
@ -19,7 +22,6 @@ procedure Test.2 (Test.19):
|
|||
procedure Test.0 ():
|
||||
let Test.12 : [<rnu><null>, C *self] = TagId(1) ;
|
||||
let Test.10 : {} = CallByName Test.2 Test.12;
|
||||
dec Test.12;
|
||||
let Test.11 : {} = Struct {};
|
||||
let Test.8 : Int1 = CallByName Bool.11 Test.10 Test.11;
|
||||
let Test.9 : Str = "";
|
||||
|
|
|
@ -16,8 +16,8 @@ procedure Test.1 (Test.2, Test.3):
|
|||
let Test.23 : {} = Struct {};
|
||||
joinpoint Test.24 Test.22:
|
||||
let Test.20 : Int1 = CallByName Bool.11 Test.21 Test.22;
|
||||
dec Test.22;
|
||||
dec Test.21;
|
||||
dec Test.22;
|
||||
let Test.18 : Int1 = CallByName Bool.4 Test.19 Test.20;
|
||||
ret Test.18;
|
||||
in
|
||||
|
|
|
@ -4,8 +4,6 @@ procedure Bool.1 ():
|
|||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
dec #Attr.3;
|
||||
dec #Attr.2;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
|
@ -47,6 +45,7 @@ procedure Decode.27 (Decode.107, Decode.108):
|
|||
let Decode.109 : [C {}, C Str] = StructAtIndex 1 Decode.122;
|
||||
inc Decode.109;
|
||||
dec Decode.122;
|
||||
inc Decode.110;
|
||||
let Decode.125 : Int1 = CallByName List.1 Decode.110;
|
||||
if Decode.125 then
|
||||
dec Decode.110;
|
||||
|
@ -82,10 +81,11 @@ procedure Json.448 (Json.449, Json.904):
|
|||
let Json.451 : List U8 = StructAtIndex 1 Json.1041;
|
||||
inc Json.451;
|
||||
dec Json.1041;
|
||||
inc Json.451;
|
||||
let Json.1037 : Int1 = CallByName List.1 Json.451;
|
||||
if Json.1037 then
|
||||
dec Json.452;
|
||||
dec Json.451;
|
||||
dec Json.452;
|
||||
let Json.1040 : {} = Struct {};
|
||||
let Json.1039 : [C {}, C Str] = TagId(0) Json.1040;
|
||||
let Json.1038 : {List U8, [C {}, C Str]} = Struct {Json.449, Json.1039};
|
||||
|
@ -112,8 +112,8 @@ procedure Json.448 (Json.449, Json.904):
|
|||
let Json.905 : {List U8, [C {}, C Str]} = Struct {Json.452, Json.906};
|
||||
ret Json.905;
|
||||
else
|
||||
dec Json.457;
|
||||
dec Json.452;
|
||||
dec Json.457;
|
||||
let Json.909 : {} = Struct {};
|
||||
let Json.908 : [C {}, C Str] = TagId(0) Json.909;
|
||||
let Json.907 : {List U8, [C {}, C Str]} = Struct {Json.449, Json.908};
|
||||
|
@ -127,6 +127,7 @@ procedure Json.55 ():
|
|||
procedure Json.56 (Json.462):
|
||||
let Json.1053 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = TagId(4) ;
|
||||
let Json.1054 : {} = Struct {};
|
||||
inc Json.462;
|
||||
let Json.1042 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = CallByName List.26 Json.462 Json.1053 Json.1054;
|
||||
let Json.1050 : U8 = 2i64;
|
||||
let Json.1051 : U8 = GetTagId Json.1042;
|
||||
|
@ -490,8 +491,10 @@ procedure Json.65 (Json.1160):
|
|||
inc Json.522;
|
||||
dec Json.899;
|
||||
let Json.1035 : U64 = 0i64;
|
||||
inc Json.521;
|
||||
let Json.523 : [C {}, C U8] = CallByName List.2 Json.521 Json.1035;
|
||||
let Json.1034 : U64 = 1i64;
|
||||
inc Json.521;
|
||||
let Json.524 : [C {}, C U8] = CallByName List.2 Json.521 Json.1034;
|
||||
let Json.1033 : U64 = 2i64;
|
||||
inc Json.521;
|
||||
|
@ -591,6 +594,7 @@ procedure Json.65 (Json.1160):
|
|||
|
||||
procedure List.1 (List.95):
|
||||
let List.552 : U64 = CallByName List.6 List.95;
|
||||
dec List.95;
|
||||
let List.553 : U64 = 0i64;
|
||||
let List.551 : Int1 = CallByName Bool.11 List.552 List.553;
|
||||
ret List.551;
|
||||
|
@ -600,9 +604,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.547 : Int1 = CallByName Num.22 List.97 List.550;
|
||||
if List.547 then
|
||||
let List.549 : U8 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.548 : [C {}, C U8] = TagId(1) List.549;
|
||||
ret List.548;
|
||||
else
|
||||
dec List.96;
|
||||
let List.546 : {} = Struct {};
|
||||
let List.545 : [C {}, C U8] = TagId(0) List.546;
|
||||
ret List.545;
|
||||
|
@ -707,10 +713,12 @@ procedure List.80 (List.612, List.613, List.614, List.615, List.616):
|
|||
let List.575 : U64 = CallByName Num.19 List.436 List.576;
|
||||
jump List.570 List.433 List.438 List.435 List.575 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
let List.439 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.573;
|
||||
let List.577 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(0) List.439;
|
||||
ret List.577;
|
||||
else
|
||||
dec List.433;
|
||||
let List.571 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(1) List.434;
|
||||
ret List.571;
|
||||
in
|
||||
|
@ -788,8 +796,8 @@ procedure Test.3 ():
|
|||
let Test.1 : [C [C List U8, C ], C Str] = CallByName Decode.27 Test.0 Test.8;
|
||||
let Test.7 : Str = "Roc";
|
||||
let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7;
|
||||
inc Test.1;
|
||||
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
|
||||
dec Test.6;
|
||||
expect Test.5;
|
||||
let Test.4 : {} = Struct {};
|
||||
ret Test.4;
|
||||
|
|
|
@ -4,8 +4,6 @@ procedure Bool.1 ():
|
|||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
dec #Attr.3;
|
||||
dec #Attr.2;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
|
@ -52,10 +50,11 @@ procedure Json.448 (Json.449, Json.904):
|
|||
let Json.451 : List U8 = StructAtIndex 1 Json.1041;
|
||||
inc Json.451;
|
||||
dec Json.1041;
|
||||
inc Json.451;
|
||||
let Json.1037 : Int1 = CallByName List.1 Json.451;
|
||||
if Json.1037 then
|
||||
dec Json.452;
|
||||
dec Json.451;
|
||||
dec Json.452;
|
||||
let Json.1040 : {} = Struct {};
|
||||
let Json.1039 : [C {}, C Str] = TagId(0) Json.1040;
|
||||
let Json.1038 : {List U8, [C {}, C Str]} = Struct {Json.449, Json.1039};
|
||||
|
@ -82,8 +81,8 @@ procedure Json.448 (Json.449, Json.904):
|
|||
let Json.905 : {List U8, [C {}, C Str]} = Struct {Json.452, Json.906};
|
||||
ret Json.905;
|
||||
else
|
||||
dec Json.457;
|
||||
dec Json.452;
|
||||
dec Json.457;
|
||||
let Json.909 : {} = Struct {};
|
||||
let Json.908 : [C {}, C Str] = TagId(0) Json.909;
|
||||
let Json.907 : {List U8, [C {}, C Str]} = Struct {Json.449, Json.908};
|
||||
|
@ -97,6 +96,7 @@ procedure Json.55 ():
|
|||
procedure Json.56 (Json.462):
|
||||
let Json.1053 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = TagId(4) ;
|
||||
let Json.1054 : {} = Struct {};
|
||||
inc Json.462;
|
||||
let Json.1042 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = CallByName List.26 Json.462 Json.1053 Json.1054;
|
||||
let Json.1050 : U8 = 2i64;
|
||||
let Json.1051 : U8 = GetTagId Json.1042;
|
||||
|
@ -460,8 +460,10 @@ procedure Json.65 (Json.1160):
|
|||
inc Json.522;
|
||||
dec Json.899;
|
||||
let Json.1035 : U64 = 0i64;
|
||||
inc Json.521;
|
||||
let Json.523 : [C {}, C U8] = CallByName List.2 Json.521 Json.1035;
|
||||
let Json.1034 : U64 = 1i64;
|
||||
inc Json.521;
|
||||
let Json.524 : [C {}, C U8] = CallByName List.2 Json.521 Json.1034;
|
||||
let Json.1033 : U64 = 2i64;
|
||||
inc Json.521;
|
||||
|
@ -561,6 +563,7 @@ procedure Json.65 (Json.1160):
|
|||
|
||||
procedure List.1 (List.95):
|
||||
let List.546 : U64 = CallByName List.6 List.95;
|
||||
dec List.95;
|
||||
let List.547 : U64 = 0i64;
|
||||
let List.545 : Int1 = CallByName Bool.11 List.546 List.547;
|
||||
ret List.545;
|
||||
|
@ -570,9 +573,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.541 : Int1 = CallByName Num.22 List.97 List.544;
|
||||
if List.541 then
|
||||
let List.543 : U8 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.542 : [C {}, C U8] = TagId(1) List.543;
|
||||
ret List.542;
|
||||
else
|
||||
dec List.96;
|
||||
let List.540 : {} = Struct {};
|
||||
let List.539 : [C {}, C U8] = TagId(0) List.540;
|
||||
ret List.539;
|
||||
|
@ -677,10 +682,12 @@ procedure List.80 (List.606, List.607, List.608, List.609, List.610):
|
|||
let List.569 : U64 = CallByName Num.19 List.436 List.570;
|
||||
jump List.564 List.433 List.438 List.435 List.569 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
let List.439 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.567;
|
||||
let List.571 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(0) List.439;
|
||||
ret List.571;
|
||||
else
|
||||
dec List.433;
|
||||
let List.565 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(1) List.434;
|
||||
ret List.565;
|
||||
in
|
||||
|
@ -746,6 +753,7 @@ procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
|||
|
||||
procedure Str.72 (Str.244):
|
||||
let Str.245 : {I64, U8} = CallByName Str.47 Str.244;
|
||||
dec Str.244;
|
||||
let Str.304 : U8 = StructAtIndex 1 Str.245;
|
||||
let Str.305 : U8 = 0i64;
|
||||
let Str.301 : Int1 = CallByName Bool.11 Str.304 Str.305;
|
||||
|
@ -795,7 +803,6 @@ procedure Test.0 ():
|
|||
inc Test.3;
|
||||
dec Test.1;
|
||||
let Test.19 : [C {}, C I64] = CallByName Str.27 Test.3;
|
||||
dec Test.3;
|
||||
let Test.25 : U8 = 1i64;
|
||||
let Test.26 : U8 = GetTagId Test.19;
|
||||
let Test.27 : Int1 = lowlevel Eq Test.25 Test.26;
|
||||
|
@ -822,8 +829,8 @@ procedure Test.12 ():
|
|||
let Test.18 : I64 = -1234i64;
|
||||
let Test.16 : {List U8, I64} = Struct {Test.17, Test.18};
|
||||
let Test.15 : [C Str, C {List U8, I64}] = TagId(1) Test.16;
|
||||
inc Test.10;
|
||||
let Test.14 : Int1 = CallByName Bool.11 Test.10 Test.15;
|
||||
dec Test.15;
|
||||
expect Test.14;
|
||||
let Test.13 : {} = Struct {};
|
||||
ret Test.13;
|
||||
|
|
|
@ -22,11 +22,11 @@ procedure Test.2 (Test.7, Test.8):
|
|||
ret Test.41;
|
||||
|
||||
procedure Test.3 (Test.17):
|
||||
dec Test.17;
|
||||
let Test.33 : {} = Struct {};
|
||||
ret Test.33;
|
||||
|
||||
procedure Test.4 (Test.18):
|
||||
inc Test.18;
|
||||
ret Test.18;
|
||||
|
||||
procedure Test.9 (Test.26, #Attr.12):
|
||||
|
@ -35,7 +35,6 @@ procedure Test.9 (Test.26, #Attr.12):
|
|||
let Test.46 : {} = Struct {};
|
||||
let Test.45 : Str = CallByName Test.16 Test.46;
|
||||
let Test.42 : Str = CallByName Test.4 Test.45;
|
||||
dec Test.45;
|
||||
let Test.44 : {} = Struct {};
|
||||
let Test.43 : Str = CallByName Test.13 Test.44 Test.42;
|
||||
ret Test.43;
|
||||
|
@ -46,7 +45,6 @@ procedure Test.9 (Test.26, #Attr.12):
|
|||
let Test.32 : {} = Struct {};
|
||||
let Test.31 : Str = CallByName Test.15 Test.32;
|
||||
let Test.28 : {} = CallByName Test.3 Test.31;
|
||||
dec Test.31;
|
||||
let Test.30 : {} = Struct {};
|
||||
let Test.29 : Str = CallByName Test.11 Test.30;
|
||||
ret Test.29;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
procedure Test.1 (Test.4):
|
||||
inc Test.4;
|
||||
ret Test.4;
|
||||
|
||||
procedure Test.21 (Test.23, #Attr.12):
|
||||
|
@ -21,10 +20,8 @@ procedure Test.0 ():
|
|||
if Test.20 then
|
||||
let Test.15 : Str = "";
|
||||
let Test.10 : Str = CallByName Test.1 Test.15;
|
||||
dec Test.15;
|
||||
jump Test.9 Test.10;
|
||||
else
|
||||
let Test.18 : Str = "";
|
||||
let Test.16 : Str = CallByName Test.1 Test.18;
|
||||
dec Test.18;
|
||||
jump Test.9 Test.16;
|
||||
|
|
|
@ -24,6 +24,7 @@ procedure List.80 (List.517, List.518, List.519, List.520, List.521):
|
|||
let List.505 : U64 = CallByName Num.19 List.436 List.506;
|
||||
jump List.500 List.433 List.503 List.435 List.505 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.500 List.517 List.518 List.519 List.520 List.521;
|
||||
|
@ -52,5 +53,4 @@ procedure Test.0 ():
|
|||
let Test.8 : List [<rnu>C *self, <null>] = Array [];
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.9 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.18 Test.8 Test.6 Test.15;
|
||||
dec Test.8;
|
||||
ret Test.9;
|
||||
|
|
|
@ -3,9 +3,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
|
||||
if List.496 then
|
||||
let List.498 : I64 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.497 : [C {}, C I64] = TagId(1) List.498;
|
||||
ret List.497;
|
||||
else
|
||||
dec List.96;
|
||||
let List.495 : {} = Struct {};
|
||||
let List.494 : [C {}, C I64] = TagId(0) List.495;
|
||||
ret List.494;
|
||||
|
@ -26,7 +28,6 @@ procedure Test.1 (Test.2):
|
|||
let Test.6 : List I64 = Array [1i64, 2i64, 3i64];
|
||||
let Test.7 : U64 = 0i64;
|
||||
let Test.5 : [C {}, C I64] = CallByName List.2 Test.6 Test.7;
|
||||
dec Test.6;
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
|
|
@ -3,15 +3,18 @@ procedure List.2 (List.96, List.97):
|
|||
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
|
||||
if List.496 then
|
||||
let List.498 : Str = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.497 : [C {}, C Str] = TagId(1) List.498;
|
||||
ret List.497;
|
||||
else
|
||||
dec List.96;
|
||||
let List.495 : {} = Struct {};
|
||||
let List.494 : [C {}, C Str] = TagId(0) List.495;
|
||||
ret List.494;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.502 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.502;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
|
@ -46,19 +49,18 @@ procedure Test.2 ():
|
|||
let Test.15 : List Str = CallByName Test.1;
|
||||
let Test.16 : {} = Struct {};
|
||||
let Test.14 : List Str = CallByName List.5 Test.15 Test.16;
|
||||
dec Test.15;
|
||||
ret Test.14;
|
||||
|
||||
procedure Test.3 (Test.4):
|
||||
let Test.18 : U64 = 2i64;
|
||||
let Test.17 : Str = CallByName Str.16 Test.4 Test.18;
|
||||
dec Test.4;
|
||||
ret Test.17;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : List Str = CallByName Test.2;
|
||||
let Test.13 : U64 = 0i64;
|
||||
let Test.6 : [C {}, C Str] = CallByName List.2 Test.12 Test.13;
|
||||
dec Test.12;
|
||||
let Test.9 : U8 = 1i64;
|
||||
let Test.10 : U8 = GetTagId Test.6;
|
||||
let Test.11 : Int1 = lowlevel Eq Test.9 Test.10;
|
||||
|
|
|
@ -3,9 +3,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.496 : Int1 = CallByName Num.22 List.97 List.500;
|
||||
if List.496 then
|
||||
let List.498 : Str = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.497 : [C {}, C Str] = TagId(1) List.498;
|
||||
ret List.497;
|
||||
else
|
||||
dec List.96;
|
||||
let List.495 : {} = Struct {};
|
||||
let List.494 : [C {}, C Str] = TagId(0) List.495;
|
||||
ret List.494;
|
||||
|
@ -55,7 +57,6 @@ procedure Test.0 ():
|
|||
let Test.12 : List Str = CallByName Test.2;
|
||||
let Test.13 : U64 = 0i64;
|
||||
let Test.6 : [C {}, C Str] = CallByName List.2 Test.12 Test.13;
|
||||
dec Test.12;
|
||||
let Test.9 : U8 = 1i64;
|
||||
let Test.10 : U8 = GetTagId Test.6;
|
||||
let Test.11 : Int1 = lowlevel Eq Test.9 Test.10;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.495 : U8 = GetTagId #Attr.3;
|
||||
joinpoint List.496 List.494:
|
||||
inc List.494;
|
||||
ret List.494;
|
||||
in
|
||||
switch List.495:
|
||||
|
@ -58,8 +57,8 @@ procedure Test.0 ():
|
|||
else
|
||||
let Test.20 : Str = "B";
|
||||
let Test.21 : Int1 = lowlevel Eq Test.20 Test.12;
|
||||
dec Test.12;
|
||||
dec Test.20;
|
||||
dec Test.12;
|
||||
if Test.21 then
|
||||
let Test.16 : [C U8, C U8, C ] = TagId(1) Test.2;
|
||||
jump Test.13 Test.16;
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
procedure List.28 (#Attr.2, #Attr.3):
|
||||
let List.496 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||
let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2;
|
||||
if #Derived_gen.0 then
|
||||
ret List.496;
|
||||
else
|
||||
decref #Attr.2;
|
||||
ret List.496;
|
||||
ret List.496;
|
||||
|
||||
procedure List.59 (List.282):
|
||||
let List.495 : {} = Struct {};
|
||||
|
|
|
@ -7,6 +7,8 @@ procedure Test.1 (Test.3):
|
|||
ret Test.13;
|
||||
|
||||
procedure Test.2 (Test.4, Test.5):
|
||||
dec Test.5;
|
||||
dec Test.4;
|
||||
let Test.9 : U64 = 18i64;
|
||||
ret Test.9;
|
||||
|
||||
|
@ -16,6 +18,4 @@ procedure Test.0 ():
|
|||
let Test.10 : {} = Struct {};
|
||||
let Test.8 : List U16 = CallByName Test.1 Test.10;
|
||||
let Test.6 : U64 = CallByName Test.2 Test.7 Test.8;
|
||||
dec Test.8;
|
||||
dec Test.7;
|
||||
ret Test.6;
|
||||
|
|
|
@ -4,6 +4,7 @@ procedure Str.3 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.2 (Test.4):
|
||||
let Test.16 : U8 = GetTagId Test.4;
|
||||
dec Test.4;
|
||||
switch Test.16:
|
||||
case 0:
|
||||
let Test.13 : Str = "A";
|
||||
|
@ -22,15 +23,12 @@ procedure Test.0 ():
|
|||
let Test.21 : [<rnw>C *self, <null>, C ] = TagId(1) ;
|
||||
let Test.20 : [<rnw>C *self, <null>, C ] = TagId(0) Test.21;
|
||||
let Test.17 : Str = CallByName Test.2 Test.20;
|
||||
dec Test.20;
|
||||
let Test.19 : [<rnw>C *self, <null>, C ] = TagId(1) ;
|
||||
let Test.18 : Str = CallByName Test.2 Test.19;
|
||||
dec Test.19;
|
||||
let Test.10 : Str = CallByName Str.3 Test.17 Test.18;
|
||||
dec Test.18;
|
||||
let Test.12 : [<rnw>C *self, <null>, C ] = TagId(2) ;
|
||||
let Test.11 : Str = CallByName Test.2 Test.12;
|
||||
dec Test.12;
|
||||
let Test.9 : Str = CallByName Str.3 Test.10 Test.11;
|
||||
dec Test.11;
|
||||
ret Test.9;
|
||||
|
|
|
@ -4,6 +4,7 @@ procedure Bool.11 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.2 (Test.5):
|
||||
let Test.14 : U8 = GetTagId Test.5;
|
||||
dec Test.5;
|
||||
switch Test.14:
|
||||
case 2:
|
||||
let Test.11 : Str = "a";
|
||||
|
@ -21,9 +22,8 @@ procedure Test.2 (Test.5):
|
|||
procedure Test.0 ():
|
||||
let Test.10 : [<rnw><null>, C Str, C *self] = TagId(0) ;
|
||||
let Test.8 : Str = CallByName Test.2 Test.10;
|
||||
dec Test.10;
|
||||
let Test.9 : Str = "c";
|
||||
let Test.7 : Int1 = CallByName Bool.11 Test.8 Test.9;
|
||||
dec Test.9;
|
||||
dec Test.8;
|
||||
dec Test.9;
|
||||
ret Test.7;
|
||||
|
|
|
@ -4,8 +4,6 @@ procedure Bool.1 ():
|
|||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
dec #Attr.3;
|
||||
dec #Attr.2;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Test.1 ():
|
||||
|
@ -30,6 +28,8 @@ procedure Test.0 ():
|
|||
dec Test.5;
|
||||
let Test.7 : {I64, Str} = CallByName Test.1;
|
||||
let Test.6 : Int1 = CallByName Bool.11 Test.7 Test.13;
|
||||
dec Test.13;
|
||||
dec Test.7;
|
||||
ret Test.6;
|
||||
else
|
||||
dec Test.5;
|
||||
|
|
|
@ -4,8 +4,6 @@ procedure Bool.1 ():
|
|||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
dec #Attr.3;
|
||||
dec #Attr.2;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Test.1 ():
|
||||
|
@ -22,6 +20,8 @@ procedure Test.0 ():
|
|||
if Test.13 then
|
||||
let Test.6 : {I64, Str} = CallByName Test.1;
|
||||
let Test.5 : Int1 = CallByName Bool.11 Test.6 Test.4;
|
||||
dec Test.6;
|
||||
dec Test.4;
|
||||
ret Test.5;
|
||||
else
|
||||
dec Test.4;
|
||||
|
|
|
@ -3,9 +3,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.513 : Int1 = CallByName Num.22 List.97 List.516;
|
||||
if List.513 then
|
||||
let List.515 : I64 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.514 : [C {}, C I64] = TagId(1) List.515;
|
||||
ret List.514;
|
||||
else
|
||||
dec List.96;
|
||||
let List.512 : {} = Struct {};
|
||||
let List.511 : [C {}, C I64] = TagId(0) List.512;
|
||||
ret List.511;
|
||||
|
@ -45,8 +47,10 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.28 : U64 = 0i64;
|
||||
inc Test.2;
|
||||
let Test.26 : [C {}, C I64] = CallByName List.2 Test.2 Test.28;
|
||||
let Test.27 : U64 = 0i64;
|
||||
inc Test.2;
|
||||
let Test.25 : [C {}, C I64] = CallByName List.2 Test.2 Test.27;
|
||||
let Test.8 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.25, Test.26};
|
||||
joinpoint Test.22:
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.494 : List [<rnnu>C List *self] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.494;
|
||||
|
||||
procedure Test.2 (Test.5):
|
||||
let Test.6 : List [<rnnu>C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5;
|
||||
inc Test.6;
|
||||
let #Derived_gen.1 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 1 } };
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15;
|
||||
let Test.14 : [<rnnu>C List *self] = TagId(0) Test.7;
|
||||
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 1 } TagId(0) Test.7;
|
||||
ret Test.14;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.16 : List [<rnnu>C List *self] = Array [];
|
||||
let Test.12 : [<rnnu>C List *self] = TagId(0) Test.16;
|
||||
let Test.10 : [<rnnu>C List *self] = CallByName Test.2 Test.12;
|
||||
dec Test.12;
|
||||
dec Test.10;
|
||||
let Test.11 : Str = "";
|
||||
ret Test.11;
|
||||
|
|
|
@ -81,5 +81,6 @@ procedure Test.9 (Test.44, Test.8):
|
|||
procedure Test.0 ():
|
||||
let Test.24 : I64 = 4i64;
|
||||
let Test.17 : [<r>C {}, C I64 {}] = CallByName Test.3 Test.24;
|
||||
dec Test.17;
|
||||
let Test.18 : Str = CallByName Test.2;
|
||||
ret Test.18;
|
||||
|
|
|
@ -3,9 +3,11 @@ procedure List.2 (List.96, List.97):
|
|||
let List.513 : Int1 = CallByName Num.22 List.97 List.516;
|
||||
if List.513 then
|
||||
let List.515 : I64 = CallByName List.66 List.96 List.97;
|
||||
dec List.96;
|
||||
let List.514 : [C {}, C I64] = TagId(1) List.515;
|
||||
ret List.514;
|
||||
else
|
||||
dec List.96;
|
||||
let List.512 : {} = Struct {};
|
||||
let List.511 : [C {}, C I64] = TagId(0) List.512;
|
||||
ret List.511;
|
||||
|
@ -44,7 +46,9 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
ret Num.277;
|
||||
|
||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||
inc Test.4;
|
||||
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
||||
inc Test.4;
|
||||
let Test.28 : [C {}, C I64] = CallByName List.2 Test.4 Test.2;
|
||||
let Test.13 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.28, Test.29};
|
||||
joinpoint Test.25:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
procedure Test.1 (Test.2):
|
||||
dec Test.2;
|
||||
let Test.11 : Int1 = false;
|
||||
ret Test.11;
|
||||
|
||||
|
@ -13,7 +14,6 @@ procedure Test.4 (Test.13):
|
|||
procedure Test.0 ():
|
||||
let Test.16 : Str = "abc";
|
||||
let Test.6 : Int1 = CallByName Test.1 Test.16;
|
||||
dec Test.16;
|
||||
let Test.9 : {} = Struct {};
|
||||
switch Test.6:
|
||||
case 0:
|
||||
|
|
|
@ -71,7 +71,6 @@ procedure Json.188 (Json.189, Json.904, #Attr.12):
|
|||
let Json.914 : {List U8, U64} = Struct {Json.191, Json.926};
|
||||
let Json.915 : {} = Struct {};
|
||||
let Json.913 : {List U8, U64} = CallByName List.18 Json.187 Json.914 Json.915;
|
||||
dec Json.187;
|
||||
let Json.193 : List U8 = StructAtIndex 0 Json.913;
|
||||
inc Json.193;
|
||||
dec Json.913;
|
||||
|
@ -159,6 +158,7 @@ procedure List.80 (List.558, List.559, List.560, List.561, List.562):
|
|||
let List.537 : U64 = CallByName Num.19 List.436 List.538;
|
||||
jump List.532 List.433 List.535 List.435 List.537 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.532 List.558 List.559 List.560 List.561 List.562;
|
||||
|
|
|
@ -19,6 +19,7 @@ procedure Test.15 (Test.49):
|
|||
ret Test.70;
|
||||
|
||||
procedure Test.16 (Test.48):
|
||||
dec Test.48;
|
||||
let Test.79 : {} = Struct {};
|
||||
let Test.78 : Int1 = CallByName Test.13 Test.79;
|
||||
ret Test.78;
|
||||
|
@ -27,11 +28,9 @@ procedure Test.17 (Test.47):
|
|||
ret Test.47;
|
||||
|
||||
procedure Test.35 (Test.36, Test.73):
|
||||
inc Test.36;
|
||||
ret Test.36;
|
||||
|
||||
procedure Test.37 (Test.38, Test.81):
|
||||
inc Test.38;
|
||||
ret Test.38;
|
||||
|
||||
procedure Test.40 (Test.41, Test.65, Test.39):
|
||||
|
@ -50,7 +49,6 @@ procedure Test.43 (Test.44, Test.42):
|
|||
joinpoint Test.62 Test.60:
|
||||
let Test.59 : List U8 = Array [];
|
||||
let Test.58 : List U8 = CallByName Test.40 Test.59 Test.44 Test.60;
|
||||
dec Test.59;
|
||||
ret Test.58;
|
||||
in
|
||||
let Test.75 : Int1 = CallByName Bool.2;
|
||||
|
@ -59,7 +57,6 @@ procedure Test.43 (Test.44, Test.42):
|
|||
inc Test.77;
|
||||
dec Test.42;
|
||||
let Test.76 : Int1 = CallByName Test.16 Test.77;
|
||||
dec Test.77;
|
||||
let Test.61 : Int1 = CallByName Test.14 Test.76;
|
||||
jump Test.62 Test.61;
|
||||
else
|
||||
|
|
|
@ -46,6 +46,11 @@ procedure Encode.23 (Encode.98):
|
|||
procedure Encode.23 (Encode.98):
|
||||
ret Encode.98;
|
||||
|
||||
procedure Encode.24 (Encode.99, Encode.107, Encode.101):
|
||||
dec Encode.99;
|
||||
let Encode.138 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||
Crash Encode.138
|
||||
|
||||
procedure Encode.24 (Encode.99, Encode.107, Encode.101):
|
||||
let Encode.111 : List U8 = CallByName Test.5 Encode.99 Encode.101 Encode.107;
|
||||
ret Encode.111;
|
||||
|
@ -70,10 +75,6 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101):
|
|||
let Encode.134 : List U8 = CallByName Json.188 Encode.99 Encode.101 Encode.107;
|
||||
ret Encode.134;
|
||||
|
||||
procedure Encode.24 (Encode.99, Encode.107, Encode.101):
|
||||
let Encode.138 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||
Crash Encode.138
|
||||
|
||||
procedure Encode.26 (Encode.105, Encode.106):
|
||||
let Encode.109 : List U8 = Array [];
|
||||
let Encode.110 : {{}, {}} = CallByName Test.2 Encode.105;
|
||||
|
@ -111,7 +112,6 @@ procedure Json.188 (Json.189, Json.904, #Attr.12):
|
|||
let Json.914 : {List U8, U64} = Struct {Json.191, Json.926};
|
||||
let Json.915 : {} = Struct {};
|
||||
let Json.913 : {List U8, U64} = CallByName List.18 Json.187 Json.914 Json.915;
|
||||
dec Json.187;
|
||||
let Json.193 : List U8 = StructAtIndex 0 Json.913;
|
||||
inc Json.193;
|
||||
dec Json.913;
|
||||
|
@ -150,7 +150,6 @@ procedure Json.188 (Json.189, Json.904, #Attr.12):
|
|||
let Json.964 : {List U8, U64} = Struct {Json.191, Json.976};
|
||||
let Json.965 : {} = Struct {};
|
||||
let Json.963 : {List U8, U64} = CallByName List.18 Json.187 Json.964 Json.965;
|
||||
dec Json.187;
|
||||
let Json.193 : List U8 = StructAtIndex 0 Json.963;
|
||||
inc Json.193;
|
||||
dec Json.963;
|
||||
|
@ -192,7 +191,6 @@ procedure Json.190 (Json.906, Json.196):
|
|||
dec Json.906;
|
||||
let Json.975 : {} = Struct {};
|
||||
let Json.197 : List U8 = CallByName Encode.24 Json.194 Json.196 Json.975;
|
||||
dec Json.194;
|
||||
joinpoint Json.970 Json.198:
|
||||
let Json.968 : U64 = 1i64;
|
||||
let Json.967 : U64 = CallByName Num.20 Json.195 Json.968;
|
||||
|
@ -279,6 +277,7 @@ procedure List.80 (List.551, List.552, List.553, List.554, List.555):
|
|||
let List.531 : U64 = CallByName Num.19 List.436 List.532;
|
||||
jump List.526 List.433 List.529 List.435 List.531 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.526 List.551 List.552 List.553 List.554 List.555;
|
||||
|
@ -293,6 +292,7 @@ procedure List.80 (List.624, List.625, List.626, List.627, List.628):
|
|||
let List.604 : U64 = CallByName Num.19 List.436 List.605;
|
||||
jump List.599 List.433 List.602 List.435 List.604 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.599 List.624 List.625 List.626 List.627 List.628;
|
||||
|
|
|
@ -68,10 +68,12 @@ procedure List.80 (List.544, List.545, List.546, List.547, List.548):
|
|||
let List.523 : U64 = CallByName Num.19 List.436 List.524;
|
||||
jump List.518 List.433 List.438 List.435 List.523 List.437;
|
||||
else
|
||||
dec List.433;
|
||||
let List.439 : U64 = UnionAtIndex (Id 0) (Index 0) List.521;
|
||||
let List.525 : [C U64, C U64] = TagId(0) List.439;
|
||||
ret List.525;
|
||||
else
|
||||
dec List.433;
|
||||
let List.519 : [C U64, C U64] = TagId(1) List.434;
|
||||
ret List.519;
|
||||
in
|
||||
|
@ -102,6 +104,7 @@ procedure Test.3 (Test.4, Test.12):
|
|||
procedure Test.0 (Test.1):
|
||||
let Test.10 : U64 = 0i64;
|
||||
let Test.11 : {} = Struct {};
|
||||
inc Test.1;
|
||||
let Test.2 : U64 = CallByName List.26 Test.1 Test.10 Test.11;
|
||||
let Test.9 : U64 = 0i64;
|
||||
let Test.7 : Int1 = CallByName Bool.11 Test.2 Test.9;
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
|||
use roc_solve_problem::TypeError;
|
||||
use roc_types::{
|
||||
pretty_print::{name_and_print_var, DebugPrint},
|
||||
subs::{Subs, Variable},
|
||||
subs::{instantiate_rigids, Subs, Variable},
|
||||
};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
|
@ -44,8 +44,9 @@ fn promote_expr_to_module(src: &str) -> String {
|
|||
buffer
|
||||
}
|
||||
|
||||
pub fn run_load_and_infer(
|
||||
pub fn run_load_and_infer<'a>(
|
||||
src: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
no_promote: bool,
|
||||
) -> Result<(LoadedModule, String), std::io::Error> {
|
||||
use tempfile::tempdir;
|
||||
|
@ -65,6 +66,11 @@ pub fn run_load_and_infer(
|
|||
|
||||
let loaded = {
|
||||
let dir = tempdir()?;
|
||||
|
||||
for (file, source) in dependencies {
|
||||
std::fs::write(dir.path().join(format!("{file}.roc")), source)?;
|
||||
}
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let result = roc_load::load_and_typecheck_str(
|
||||
|
@ -248,9 +254,9 @@ fn parse_queries(src: &str, line_info: &LineInfo) -> Vec<TypeQuery> {
|
|||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct InferOptions {
|
||||
pub allow_errors: bool,
|
||||
pub print_can_decls: bool,
|
||||
pub print_only_under_alias: bool,
|
||||
pub allow_errors: bool,
|
||||
pub no_promote: bool,
|
||||
}
|
||||
|
||||
|
@ -338,7 +344,12 @@ impl InferredProgram {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
pub fn infer_queries<'a>(
|
||||
src: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: InferOptions,
|
||||
allow_can_errors: bool,
|
||||
) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
let (
|
||||
LoadedModule {
|
||||
module_id: home,
|
||||
|
@ -351,7 +362,7 @@ pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram
|
|||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, options.no_promote)?;
|
||||
) = run_load_and_infer(src, dependencies, options.no_promote)?;
|
||||
|
||||
let declarations = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
@ -359,23 +370,20 @@ pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram
|
|||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
||||
if !options.allow_errors {
|
||||
{
|
||||
let (can_problems, type_problems) =
|
||||
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||
|
||||
if !can_problems.is_empty() {
|
||||
if !can_problems.is_empty() && !allow_can_errors {
|
||||
return Err(format!("Canonicalization problems: {can_problems}",).into());
|
||||
}
|
||||
if !type_problems.is_empty() {
|
||||
if !type_problems.is_empty() && !options.allow_errors {
|
||||
return Err(format!("Type problems: {type_problems}",).into());
|
||||
}
|
||||
}
|
||||
|
||||
let line_info = LineInfo::new(&src);
|
||||
let queries = parse_queries(&src, &line_info);
|
||||
if queries.is_empty() {
|
||||
return Err("No queries provided!".into());
|
||||
}
|
||||
|
||||
let mut inferred_queries = Vec::with_capacity(queries.len());
|
||||
let exposed_by_module = ExposedByModule::default();
|
||||
|
@ -533,6 +541,7 @@ impl<'a> QueryCtx<'a> {
|
|||
let def_region = Region::new(start_pos, end_pos);
|
||||
let def_source = &self.source[start_pos.offset as usize..end_pos.offset as usize];
|
||||
|
||||
instantiate_rigids(self.subs, def.var());
|
||||
roc_late_solve::unify(
|
||||
self.home,
|
||||
self.arena,
|
||||
|
@ -560,40 +569,3 @@ impl<'a> QueryCtx<'a> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries_help(src: &str, expected: impl FnOnce(&str), options: InferOptions) {
|
||||
let InferredProgram {
|
||||
program,
|
||||
inferred_queries,
|
||||
} = infer_queries(src, options).unwrap();
|
||||
|
||||
let mut output_parts = Vec::with_capacity(inferred_queries.len() + 2);
|
||||
|
||||
if options.print_can_decls {
|
||||
use roc_can::debug::{pretty_print_declarations, PPCtx};
|
||||
let ctx = PPCtx {
|
||||
home: program.home,
|
||||
interns: &program.interns,
|
||||
print_lambda_names: true,
|
||||
};
|
||||
let pretty_decls = pretty_print_declarations(&ctx, &program.declarations);
|
||||
output_parts.push(pretty_decls);
|
||||
output_parts.push("\n".to_owned());
|
||||
}
|
||||
|
||||
for InferredQuery { elaboration, .. } in inferred_queries {
|
||||
let output_part = match elaboration {
|
||||
Elaboration::Specialization {
|
||||
specialized_name,
|
||||
typ,
|
||||
} => format!("{specialized_name} : {typ}"),
|
||||
Elaboration::Source { source, typ } => format!("{source} : {typ}"),
|
||||
Elaboration::Instantiation { .. } => panic!("Use uitest instead"),
|
||||
};
|
||||
output_parts.push(output_part);
|
||||
}
|
||||
|
||||
let pretty_output = output_parts.join("\n");
|
||||
|
||||
expected(&pretty_output);
|
||||
}
|
||||
|
|
|
@ -171,23 +171,15 @@ impl RecordField<Type> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_aliases<'a, F>(
|
||||
fn instantiate_aliases<'a, F>(
|
||||
&mut self,
|
||||
region: Region,
|
||||
aliases: &'a F,
|
||||
var_store: &mut VarStore,
|
||||
new_lambda_sets: &mut ImSet<Variable>,
|
||||
new_infer_ext_vars: &mut ImSet<Variable>,
|
||||
aliases: &F,
|
||||
ctx: &mut InstantiateAliasesCtx<'_>,
|
||||
) where
|
||||
F: Fn(Symbol) -> Option<&'a Alias>,
|
||||
{
|
||||
self.as_inner_mut().instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_sets,
|
||||
new_infer_ext_vars,
|
||||
)
|
||||
instantiate_aliases(self.as_inner_mut(), region, aliases, ctx)
|
||||
}
|
||||
|
||||
pub fn contains_symbol(&self, rep_symbol: Symbol) -> bool {
|
||||
|
@ -229,20 +221,12 @@ impl LambdaSet {
|
|||
fn instantiate_aliases<'a, F>(
|
||||
&mut self,
|
||||
region: Region,
|
||||
aliases: &'a F,
|
||||
var_store: &mut VarStore,
|
||||
new_lambda_sets: &mut ImSet<Variable>,
|
||||
new_infer_ext_vars: &mut ImSet<Variable>,
|
||||
aliases: &F,
|
||||
ctx: &mut InstantiateAliasesCtx<'_>,
|
||||
) where
|
||||
F: Fn(Symbol) -> Option<&'a Alias>,
|
||||
{
|
||||
self.0.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_sets,
|
||||
new_infer_ext_vars,
|
||||
)
|
||||
instantiate_aliases(&mut self.0, region, aliases, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1335,6 +1319,16 @@ impl Types {
|
|||
|
||||
cloned
|
||||
}
|
||||
|
||||
pub fn shallow_dealias(&self, value: Index<TypeTag>) -> Index<TypeTag> {
|
||||
let mut result = value;
|
||||
while let TypeTag::StructuralAlias { actual, .. } | TypeTag::OpaqueAlias { actual, .. } =
|
||||
self[result]
|
||||
{
|
||||
result = actual;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -1699,6 +1693,8 @@ impl_types_index! {
|
|||
|
||||
impl_types_index_slice! {
|
||||
tag_names, TagName
|
||||
field_names, Lowercase
|
||||
tags, TypeTag
|
||||
}
|
||||
|
||||
impl std::ops::Index<Index<AsideTypeSlice>> for Types {
|
||||
|
@ -2317,7 +2313,16 @@ impl Type {
|
|||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) => {
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
if let Some(replacement) = substitutions.get(rec) {
|
||||
let new_rec_var = match replacement {
|
||||
Type::Variable(v) => *v,
|
||||
_ => panic!("Recursion var substitution must be a variable"),
|
||||
};
|
||||
|
||||
*rec = new_rec_var;
|
||||
}
|
||||
|
||||
for (_, args) in tags {
|
||||
stack.extend(args.iter_mut());
|
||||
}
|
||||
|
@ -2806,7 +2811,7 @@ impl Type {
|
|||
}
|
||||
|
||||
/// a shallow dealias, continue until the first constructor is not an alias.
|
||||
pub fn shallow_dealias(&self) -> &Self {
|
||||
fn shallow_dealias(&self) -> &Self {
|
||||
let mut result = self;
|
||||
while let Type::Alias { actual, .. } = result {
|
||||
result = actual;
|
||||
|
@ -2830,370 +2835,21 @@ impl Type {
|
|||
pub fn instantiate_aliases<'a, F>(
|
||||
&mut self,
|
||||
region: Region,
|
||||
aliases: &'a F,
|
||||
aliases: &F,
|
||||
var_store: &mut VarStore,
|
||||
new_lambda_set_variables: &mut ImSet<Variable>,
|
||||
new_recursion_variables: &mut ImSet<Variable>,
|
||||
new_infer_ext_vars: &mut ImSet<Variable>,
|
||||
) where
|
||||
F: Fn(Symbol) -> Option<&'a Alias>,
|
||||
{
|
||||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
arg.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
closure.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
ret.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
ext.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, x) in fields.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,
|
||||
);
|
||||
}
|
||||
}
|
||||
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,
|
||||
infer_ext_in_output_types,
|
||||
symbol: _,
|
||||
}) => {
|
||||
debug_assert!(lambda_set_variables
|
||||
.iter()
|
||||
.all(|lambda_set| matches!(lambda_set.0, Type::Variable(..))));
|
||||
debug_assert!(infer_ext_in_output_types
|
||||
.iter()
|
||||
.all(|t| matches!(t, Type::Variable(..) | Type::EmptyTagUnion)));
|
||||
type_arguments.iter_mut().for_each(|t| {
|
||||
t.value.typ.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
)
|
||||
});
|
||||
}
|
||||
HostExposedAlias {
|
||||
type_arguments: type_args,
|
||||
lambda_set_variables,
|
||||
actual: actual_type,
|
||||
..
|
||||
} => {
|
||||
for arg in type_args {
|
||||
arg.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
|
||||
for arg in lambda_set_variables {
|
||||
arg.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
|
||||
actual_type.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
Alias {
|
||||
type_arguments: type_args,
|
||||
lambda_set_variables,
|
||||
actual: actual_type,
|
||||
..
|
||||
} => {
|
||||
for arg in type_args {
|
||||
arg.typ.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
|
||||
for arg in lambda_set_variables {
|
||||
arg.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
|
||||
actual_type.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
Apply(symbol, args, _) => {
|
||||
if let Some(alias) = aliases(*symbol) {
|
||||
// TODO switch to this, but we still need to check for recursion with the
|
||||
// `else` branch.
|
||||
// We would also need to determine polarity correct.
|
||||
if false {
|
||||
let mut type_var_to_arg = Vec::new();
|
||||
|
||||
for (alias_var, arg_ann) in alias.type_variables.iter().zip(args) {
|
||||
type_var_to_arg.push(Loc::at(
|
||||
arg_ann.region,
|
||||
OptAbleType {
|
||||
typ: arg_ann.value.clone(),
|
||||
opt_abilities: alias_var.value.opt_bound_abilities.clone(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let mut lambda_set_variables =
|
||||
Vec::with_capacity(alias.lambda_set_variables.len());
|
||||
|
||||
for _ in 0..alias.lambda_set_variables.len() {
|
||||
let lvar = var_store.fresh();
|
||||
|
||||
new_lambda_set_variables.insert(lvar);
|
||||
|
||||
lambda_set_variables.push(LambdaSet(Type::Variable(lvar)));
|
||||
}
|
||||
|
||||
let mut infer_ext_in_output_types =
|
||||
Vec::with_capacity(alias.infer_ext_in_output_variables.len());
|
||||
|
||||
for _ in 0..alias.infer_ext_in_output_variables.len() {
|
||||
let var = var_store.fresh();
|
||||
new_infer_ext_vars.insert(var);
|
||||
infer_ext_in_output_types.push(Type::Variable(var));
|
||||
}
|
||||
|
||||
let alias = Type::DelayedAlias(AliasCommon {
|
||||
symbol: *symbol,
|
||||
type_arguments: type_var_to_arg,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_types,
|
||||
});
|
||||
|
||||
*self = alias;
|
||||
} else {
|
||||
if args.len() != alias.type_variables.len() {
|
||||
// We will have already reported an error during canonicalization.
|
||||
*self = Type::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut actual = alias.typ.clone();
|
||||
|
||||
let mut named_args = Vec::with_capacity(args.len());
|
||||
let mut substitution = ImMap::default();
|
||||
|
||||
// TODO substitute further in args
|
||||
for (
|
||||
Loc {
|
||||
value:
|
||||
AliasVar {
|
||||
var: placeholder,
|
||||
opt_bound_abilities,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
filler,
|
||||
) in alias.type_variables.iter().zip(args.iter())
|
||||
{
|
||||
let mut filler = filler.clone();
|
||||
filler.value.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
named_args.push(OptAbleType {
|
||||
typ: filler.value.clone(),
|
||||
opt_abilities: opt_bound_abilities.clone(),
|
||||
});
|
||||
substitution.insert(*placeholder, filler.value);
|
||||
}
|
||||
|
||||
// make sure hidden variables are freshly instantiated
|
||||
let mut lambda_set_variables =
|
||||
Vec::with_capacity(alias.lambda_set_variables.len());
|
||||
for typ in alias.lambda_set_variables.iter() {
|
||||
if let Type::Variable(var) = typ.0 {
|
||||
let fresh = var_store.fresh();
|
||||
new_lambda_set_variables.insert(fresh);
|
||||
substitution.insert(var, Type::Variable(fresh));
|
||||
lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||
} else {
|
||||
unreachable!("at this point there should be only vars in there");
|
||||
}
|
||||
}
|
||||
let mut infer_ext_in_output_types =
|
||||
Vec::with_capacity(alias.infer_ext_in_output_variables.len());
|
||||
for var in alias.infer_ext_in_output_variables.iter() {
|
||||
let fresh = var_store.fresh();
|
||||
new_infer_ext_vars.insert(fresh);
|
||||
substitution.insert(*var, Type::Variable(fresh));
|
||||
infer_ext_in_output_types.push(Type::Variable(fresh));
|
||||
}
|
||||
|
||||
actual.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
|
||||
actual.substitute(&substitution);
|
||||
|
||||
// instantiate recursion variable!
|
||||
if let Type::RecursiveTagUnion(rec_var, mut tags, mut ext) = actual {
|
||||
let new_rec_var = var_store.fresh();
|
||||
substitution.clear();
|
||||
substitution.insert(rec_var, Type::Variable(new_rec_var));
|
||||
|
||||
for typ in tags.iter_mut().flat_map(|v| v.1.iter_mut()) {
|
||||
typ.substitute(&substitution);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = &mut ext {
|
||||
ext.substitute(&substitution);
|
||||
}
|
||||
|
||||
actual = Type::RecursiveTagUnion(new_rec_var, tags, ext);
|
||||
}
|
||||
let alias = Type::Alias {
|
||||
symbol: *symbol,
|
||||
type_arguments: named_args,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_types,
|
||||
actual: Box::new(actual),
|
||||
kind: alias.kind,
|
||||
};
|
||||
|
||||
*self = alias;
|
||||
}
|
||||
} else {
|
||||
// one of the special-cased Apply types.
|
||||
for x in args {
|
||||
x.value.instantiate_aliases(
|
||||
region,
|
||||
aliases,
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_infer_ext_vars,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
RangedNumber(_) => {}
|
||||
UnspecializedLambdaSet { .. } => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
}
|
||||
let mut ctx = InstantiateAliasesCtx {
|
||||
var_store,
|
||||
new_lambda_set_variables,
|
||||
new_recursion_variables,
|
||||
new_infer_ext_vars,
|
||||
};
|
||||
instantiate_aliases(self, region, aliases, &mut ctx)
|
||||
}
|
||||
|
||||
pub fn instantiate_lambda_sets_as_unspecialized(
|
||||
|
@ -3269,6 +2925,208 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
struct InstantiateAliasesCtx<'a> {
|
||||
var_store: &'a mut VarStore,
|
||||
new_lambda_set_variables: &'a mut ImSet<Variable>,
|
||||
new_recursion_variables: &'a mut ImSet<Variable>,
|
||||
new_infer_ext_vars: &'a mut ImSet<Variable>,
|
||||
}
|
||||
|
||||
fn instantiate_aliases<'a, F>(
|
||||
typ: &mut Type,
|
||||
region: Region,
|
||||
aliases: &F,
|
||||
ctx: &mut InstantiateAliasesCtx<'_>,
|
||||
) where
|
||||
F: Fn(Symbol) -> Option<&'a Alias>,
|
||||
{
|
||||
use Type::*;
|
||||
|
||||
match typ {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
instantiate_aliases(arg, region, aliases, ctx);
|
||||
}
|
||||
instantiate_aliases(closure, region, aliases, ctx);
|
||||
instantiate_aliases(ret, region, aliases, ctx);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
instantiate_aliases(ext, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
instantiate_aliases(x, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
instantiate_aliases(ext, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, x) in fields.iter_mut() {
|
||||
x.instantiate_aliases(region, aliases, ctx);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
instantiate_aliases(ext, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
Tuple(elems, ext) => {
|
||||
for (_, x) in elems.iter_mut() {
|
||||
instantiate_aliases(x, region, aliases, ctx);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext, _) = ext {
|
||||
instantiate_aliases(ext, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_types,
|
||||
symbol: _,
|
||||
}) => {
|
||||
debug_assert!(lambda_set_variables
|
||||
.iter()
|
||||
.all(|lambda_set| matches!(lambda_set.0, Type::Variable(..))));
|
||||
debug_assert!(infer_ext_in_output_types
|
||||
.iter()
|
||||
.all(|t| matches!(t, Type::Variable(..) | Type::EmptyTagUnion)));
|
||||
type_arguments
|
||||
.iter_mut()
|
||||
.for_each(|t| instantiate_aliases(&mut t.value.typ, region, aliases, ctx));
|
||||
}
|
||||
HostExposedAlias {
|
||||
type_arguments: type_args,
|
||||
lambda_set_variables,
|
||||
actual: actual_type,
|
||||
..
|
||||
} => {
|
||||
for arg in type_args {
|
||||
instantiate_aliases(arg, region, aliases, ctx);
|
||||
}
|
||||
|
||||
for arg in lambda_set_variables {
|
||||
arg.instantiate_aliases(region, aliases, ctx);
|
||||
}
|
||||
|
||||
instantiate_aliases(&mut *actual_type, region, aliases, ctx);
|
||||
}
|
||||
Alias {
|
||||
type_arguments: type_args,
|
||||
lambda_set_variables,
|
||||
actual: actual_type,
|
||||
..
|
||||
} => {
|
||||
for arg in type_args {
|
||||
instantiate_aliases(&mut arg.typ, region, aliases, ctx);
|
||||
}
|
||||
|
||||
for arg in lambda_set_variables {
|
||||
arg.instantiate_aliases(region, aliases, ctx);
|
||||
}
|
||||
|
||||
instantiate_aliases(actual_type, region, aliases, ctx);
|
||||
}
|
||||
Apply(symbol, args, _) => {
|
||||
if let Some(alias) = aliases(*symbol) {
|
||||
if args.len() != alias.type_variables.len() {
|
||||
// We will have already reported an error during canonicalization.
|
||||
*typ = Type::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut actual = alias.typ.clone();
|
||||
|
||||
let mut named_args = Vec::with_capacity(args.len());
|
||||
let mut substitution = ImMap::default();
|
||||
|
||||
// TODO substitute further in args
|
||||
for (
|
||||
Loc {
|
||||
value:
|
||||
AliasVar {
|
||||
var: placeholder,
|
||||
opt_bound_abilities,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
filler,
|
||||
) in alias.type_variables.iter().zip(args.iter())
|
||||
{
|
||||
let mut filler = filler.clone();
|
||||
instantiate_aliases(&mut filler.value, region, aliases, ctx);
|
||||
named_args.push(OptAbleType {
|
||||
typ: filler.value.clone(),
|
||||
opt_abilities: opt_bound_abilities.clone(),
|
||||
});
|
||||
substitution.insert(*placeholder, filler.value);
|
||||
}
|
||||
|
||||
// make sure nested lambda set variables are freshly instantiated
|
||||
let mut lambda_set_variables = Vec::with_capacity(alias.lambda_set_variables.len());
|
||||
for typ in alias.lambda_set_variables.iter() {
|
||||
if let Type::Variable(var) = typ.0 {
|
||||
let fresh = ctx.var_store.fresh();
|
||||
ctx.new_lambda_set_variables.insert(fresh);
|
||||
substitution.insert(var, Type::Variable(fresh));
|
||||
lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||
} else {
|
||||
unreachable!("at this point there should be only vars in there");
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all nested recursion variables are freshly instantiated
|
||||
let mut recursion_variables = Vec::with_capacity(alias.recursion_variables.len());
|
||||
for var in alias.recursion_variables.iter() {
|
||||
let fresh = ctx.var_store.fresh();
|
||||
ctx.new_recursion_variables.insert(fresh);
|
||||
substitution.insert(*var, Type::Variable(fresh));
|
||||
recursion_variables.push(Type::Variable(fresh));
|
||||
}
|
||||
|
||||
// make sure all nested infer-open-in-output position ext vars are freshly instantiated
|
||||
let mut infer_ext_in_output_types =
|
||||
Vec::with_capacity(alias.infer_ext_in_output_variables.len());
|
||||
for var in alias.infer_ext_in_output_variables.iter() {
|
||||
let fresh = ctx.var_store.fresh();
|
||||
ctx.new_infer_ext_vars.insert(fresh);
|
||||
substitution.insert(*var, Type::Variable(fresh));
|
||||
infer_ext_in_output_types.push(Type::Variable(fresh));
|
||||
}
|
||||
|
||||
instantiate_aliases(&mut actual, region, aliases, ctx);
|
||||
|
||||
actual.substitute(&substitution);
|
||||
|
||||
let alias = Type::Alias {
|
||||
symbol: *symbol,
|
||||
type_arguments: named_args,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_types,
|
||||
actual: Box::new(actual),
|
||||
kind: alias.kind,
|
||||
};
|
||||
|
||||
*typ = alias;
|
||||
} else {
|
||||
// one of the special-cased Apply types.
|
||||
for x in args {
|
||||
instantiate_aliases(&mut x.value, region, aliases, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
RangedNumber(_) => {}
|
||||
UnspecializedLambdaSet { .. } => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
||||
use Type::*;
|
||||
|
||||
|
|
|
@ -14,8 +14,12 @@ harness = false
|
|||
|
||||
[dev-dependencies]
|
||||
roc_builtins = { path = "../builtins" }
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
|
||||
roc_load = { path = "../load" }
|
||||
roc_packaging = { path = "../../packaging" }
|
||||
roc_module = { path = "../module", features = ["debug-symbols"] }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
|
|
156
crates/compiler/uitest/src/mono.rs
Normal file
156
crates/compiler/uitest/src/mono.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
use std::io;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::MutMap;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::{
|
||||
ir::{Proc, ProcLayout},
|
||||
layout::STLayoutInterner,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
use test_solve_helpers::format_problems;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MonoOptions {
|
||||
pub no_check: bool,
|
||||
}
|
||||
|
||||
pub fn write_compiled_ir<'a>(
|
||||
writer: &mut impl io::Write,
|
||||
test_module: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: MonoOptions,
|
||||
allow_can_errors: bool,
|
||||
) -> io::Result<()> {
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let exec_mode = ExecutionMode::Executable;
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
||||
let dir = tempdir()?;
|
||||
|
||||
for (file, source) in dependencies {
|
||||
std::fs::write(dir.path().join(format!("{file}.roc")), source)?;
|
||||
}
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info: roc_target::TargetInfo::default_x86_64(),
|
||||
threading: Threading::Single,
|
||||
render: roc_reporting::report::RenderTarget::Generic,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
exec_mode,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
file_path,
|
||||
test_module,
|
||||
dir.path().to_path_buf(),
|
||||
RocCacheDir::Disallowed,
|
||||
load_config,
|
||||
);
|
||||
|
||||
let loaded = match loaded {
|
||||
Ok(x) => x,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
|
||||
report,
|
||||
))) => {
|
||||
println!("{}", report);
|
||||
panic!();
|
||||
}
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
|
||||
use roc_load::MonomorphizedModule;
|
||||
let MonomorphizedModule {
|
||||
procedures,
|
||||
exposed_to_host,
|
||||
mut layout_interner,
|
||||
interns,
|
||||
can_problems,
|
||||
mut type_problems,
|
||||
sources,
|
||||
..
|
||||
} = loaded;
|
||||
|
||||
let main_fn_symbol = exposed_to_host.top_level_values.keys().copied().next();
|
||||
|
||||
for (module, can_problems) in can_problems.into_iter() {
|
||||
let type_problems = type_problems.remove(&module).unwrap_or_default();
|
||||
|
||||
let source = sources.get(&module).unwrap();
|
||||
|
||||
let (can_problems, type_problems) =
|
||||
format_problems(&source.1, module, &interns, can_problems, type_problems);
|
||||
|
||||
if !can_problems.is_empty() && !allow_can_errors {
|
||||
panic!("Canonicalization problems: {can_problems}");
|
||||
}
|
||||
if !type_problems.is_empty() {
|
||||
panic!("Type problems: {type_problems}");
|
||||
}
|
||||
}
|
||||
|
||||
if !options.no_check {
|
||||
check_procedures(arena, &interns, &mut layout_interner, &procedures);
|
||||
}
|
||||
|
||||
write_procedures(writer, layout_interner, procedures, main_fn_symbol)
|
||||
}
|
||||
|
||||
fn check_procedures<'a>(
|
||||
arena: &'a Bump,
|
||||
interns: &Interns,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
procedures: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
) {
|
||||
use roc_mono::debug::{check_procs, format_problems};
|
||||
let problems = check_procs(arena, interner, procedures);
|
||||
if problems.is_empty() {
|
||||
return;
|
||||
}
|
||||
let formatted = format_problems(interns, interner, problems);
|
||||
panic!("IR problems found:\n{formatted}");
|
||||
}
|
||||
|
||||
fn write_procedures<'a>(
|
||||
writer: &mut impl io::Write,
|
||||
interner: STLayoutInterner<'a>,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
opt_main_fn_symbol: Option<Symbol>,
|
||||
) -> io::Result<()> {
|
||||
let mut procs_strings = procedures
|
||||
.values()
|
||||
.map(|proc| proc.to_pretty(&interner, 200, false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let opt_main_fn = opt_main_fn_symbol.map(|main_fn_symbol| {
|
||||
let index = procedures
|
||||
.keys()
|
||||
.position(|(s, _)| *s == main_fn_symbol)
|
||||
.unwrap();
|
||||
procs_strings.swap_remove(index)
|
||||
});
|
||||
|
||||
procs_strings.sort();
|
||||
|
||||
if let Some(main_fn) = opt_main_fn {
|
||||
procs_strings.push(main_fn);
|
||||
}
|
||||
|
||||
let mut procs = procs_strings.iter().peekable();
|
||||
while let Some(proc) = procs.next() {
|
||||
if procs.peek().is_some() {
|
||||
writeln!(writer, "{}", proc)?;
|
||||
} else {
|
||||
write!(writer, "{}", proc)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -8,11 +8,15 @@ use std::{
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
use libtest_mimic::{run, Arguments, Failed, Trial};
|
||||
use mono::MonoOptions;
|
||||
use regex::Regex;
|
||||
use roc_collections::VecMap;
|
||||
use test_solve_helpers::{
|
||||
infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER,
|
||||
};
|
||||
|
||||
mod mono;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = Arguments::from_args();
|
||||
|
||||
|
@ -32,13 +36,25 @@ lazy_static! {
|
|||
.join("uitest")
|
||||
.join("tests");
|
||||
|
||||
/// # +opt can:<opt>
|
||||
static ref RE_OPT_CAN: Regex =
|
||||
Regex::new(r#"# \+opt can:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +opt infer:<opt>
|
||||
static ref RE_OPT_INFER: Regex =
|
||||
Regex::new(r#"# \+opt infer:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +opt print:<opt>
|
||||
static ref RE_OPT_PRINT: Regex =
|
||||
Regex::new(r#"# \+opt print:(?P<opt>.*)"#).unwrap();
|
||||
/// # +opt mono:<opt>
|
||||
static ref RE_OPT_MONO: Regex =
|
||||
Regex::new(r#"# \+opt mono:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +emit:<opt>
|
||||
static ref RE_EMIT: Regex =
|
||||
Regex::new(r#"# \+emit:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// ## module <name>
|
||||
static ref RE_MODULE: Regex =
|
||||
Regex::new(r#"## module (?P<name>.*)"#).unwrap();
|
||||
}
|
||||
|
||||
fn collect_uitest_files() -> io::Result<Vec<PathBuf>> {
|
||||
|
@ -78,12 +94,22 @@ fn into_test(path: PathBuf) -> io::Result<Trial> {
|
|||
fn run_test(path: PathBuf) -> Result<(), Failed> {
|
||||
let data = std::fs::read_to_string(&path)?;
|
||||
let TestCase {
|
||||
can_options,
|
||||
infer_options,
|
||||
print_options,
|
||||
source,
|
||||
} = TestCase::parse(data)?;
|
||||
emit_options,
|
||||
mono_options,
|
||||
program,
|
||||
} = TestCase::parse(&data)?;
|
||||
|
||||
let inferred_program = infer_queries(&source, infer_options)?;
|
||||
let inferred_program = infer_queries(
|
||||
program.test_module,
|
||||
program
|
||||
.other_modules
|
||||
.iter()
|
||||
.map(|(md, src)| (&**md, &**src)),
|
||||
infer_options,
|
||||
can_options.allow_errors,
|
||||
)?;
|
||||
|
||||
{
|
||||
let mut fd = fs::OpenOptions::new()
|
||||
|
@ -91,7 +117,14 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
|||
.truncate(true)
|
||||
.open(&path)?;
|
||||
|
||||
assemble_query_output(&mut fd, &source, inferred_program, print_options)?;
|
||||
assemble_query_output(
|
||||
&mut fd,
|
||||
program,
|
||||
inferred_program,
|
||||
can_options,
|
||||
mono_options,
|
||||
emit_options,
|
||||
)?;
|
||||
}
|
||||
|
||||
check_for_changes(&path)?;
|
||||
|
@ -101,32 +134,116 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
|||
|
||||
const EMIT_HEADER: &str = "# -emit:";
|
||||
|
||||
struct TestCase {
|
||||
struct Modules<'a> {
|
||||
before_any: &'a str,
|
||||
other_modules: VecMap<&'a str, &'a str>,
|
||||
test_module: &'a str,
|
||||
}
|
||||
|
||||
struct TestCase<'a> {
|
||||
can_options: CanOptions,
|
||||
infer_options: InferOptions,
|
||||
print_options: PrintOptions,
|
||||
source: String,
|
||||
mono_options: MonoOptions,
|
||||
emit_options: EmitOptions,
|
||||
program: Modules<'a>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrintOptions {
|
||||
can_decls: bool,
|
||||
struct CanOptions {
|
||||
allow_errors: bool,
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
fn parse(mut data: String) -> Result<Self, Failed> {
|
||||
#[derive(Default)]
|
||||
struct EmitOptions {
|
||||
can_decls: bool,
|
||||
mono: bool,
|
||||
}
|
||||
|
||||
impl<'a> TestCase<'a> {
|
||||
fn parse(mut data: &'a str) -> Result<Self, Failed> {
|
||||
// Drop anything following `# -emit:` header lines; that's the output.
|
||||
if let Some(drop_at) = data.find(EMIT_HEADER) {
|
||||
data.truncate(drop_at);
|
||||
data.truncate(data.trim_end().len());
|
||||
data = data[..drop_at].trim_end();
|
||||
}
|
||||
|
||||
let can_options = Self::parse_can_options(data)?;
|
||||
let infer_options = Self::parse_infer_options(data)?;
|
||||
let mono_options = Self::parse_mono_options(data)?;
|
||||
let emit_options = Self::parse_emit_options(data)?;
|
||||
|
||||
let program = Self::parse_modules(data);
|
||||
|
||||
Ok(TestCase {
|
||||
infer_options: Self::parse_infer_options(&data)?,
|
||||
print_options: Self::parse_print_options(&data)?,
|
||||
source: data,
|
||||
can_options,
|
||||
infer_options,
|
||||
mono_options,
|
||||
emit_options,
|
||||
program,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_modules(data: &'a str) -> Modules<'a> {
|
||||
let mut module_starts = RE_MODULE.captures_iter(data).peekable();
|
||||
|
||||
let first_module_start = match module_starts.peek() {
|
||||
None => {
|
||||
// This is just a single module with no name; it is the test module.
|
||||
return Modules {
|
||||
before_any: Default::default(),
|
||||
other_modules: Default::default(),
|
||||
test_module: data,
|
||||
};
|
||||
}
|
||||
Some(p) => p.get(0).unwrap().start(),
|
||||
};
|
||||
|
||||
let before_any = data[..first_module_start].trim();
|
||||
|
||||
let mut test_module = None;
|
||||
let mut other_modules = VecMap::new();
|
||||
|
||||
while let Some(module_start) = module_starts.next() {
|
||||
let module_name = module_start.name("name").unwrap().as_str();
|
||||
let module_start = module_start.get(0).unwrap().end();
|
||||
let module = match module_starts.peek() {
|
||||
None => &data[module_start..],
|
||||
Some(next_module_start) => {
|
||||
let module_end = next_module_start.get(0).unwrap().start();
|
||||
&data[module_start..module_end]
|
||||
}
|
||||
}
|
||||
.trim();
|
||||
|
||||
if module_name == "Test" {
|
||||
test_module = Some(module);
|
||||
} else {
|
||||
other_modules.insert(module_name, module);
|
||||
}
|
||||
}
|
||||
|
||||
let test_module = test_module.expect("no Test module found");
|
||||
Modules {
|
||||
before_any,
|
||||
other_modules,
|
||||
test_module,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_can_options(data: &str) -> Result<CanOptions, Failed> {
|
||||
let mut can_opts = CanOptions::default();
|
||||
|
||||
let found_can_opts = RE_OPT_CAN.captures_iter(data);
|
||||
for can_opt in found_can_opts {
|
||||
let opt = can_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"allow_errors" => can_opts.allow_errors = true,
|
||||
other => return Err(format!("unknown can option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(can_opts)
|
||||
}
|
||||
|
||||
fn parse_infer_options(data: &str) -> Result<InferOptions, Failed> {
|
||||
let mut infer_opts = InferOptions {
|
||||
no_promote: true,
|
||||
|
@ -146,19 +263,35 @@ impl TestCase {
|
|||
Ok(infer_opts)
|
||||
}
|
||||
|
||||
fn parse_print_options(data: &str) -> Result<PrintOptions, Failed> {
|
||||
let mut print_opts = PrintOptions::default();
|
||||
fn parse_mono_options(data: &str) -> Result<MonoOptions, Failed> {
|
||||
let mut mono_opts = MonoOptions::default();
|
||||
|
||||
let found_infer_opts = RE_OPT_PRINT.captures_iter(data);
|
||||
let found_infer_opts = RE_OPT_MONO.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"can_decls" => print_opts.can_decls = true,
|
||||
other => return Err(format!("unknown print option: {other:?}").into()),
|
||||
"no_check" => mono_opts.no_check = true,
|
||||
other => return Err(format!("unknown mono option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(print_opts)
|
||||
Ok(mono_opts)
|
||||
}
|
||||
|
||||
fn parse_emit_options(data: &str) -> Result<EmitOptions, Failed> {
|
||||
let mut emit_opts = EmitOptions::default();
|
||||
|
||||
let found_infer_opts = RE_EMIT.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"can_decls" => emit_opts.can_decls = true,
|
||||
"mono" => emit_opts.mono = true,
|
||||
other => return Err(format!("unknown emit option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(emit_opts)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,25 +317,60 @@ fn check_for_changes(path: &Path) -> Result<(), Failed> {
|
|||
/// Assemble the output for a test, with queries elaborated in-line.
|
||||
fn assemble_query_output(
|
||||
writer: &mut impl io::Write,
|
||||
source: &str,
|
||||
program: Modules<'_>,
|
||||
inferred_program: InferredProgram,
|
||||
print_options: PrintOptions,
|
||||
can_options: CanOptions,
|
||||
mono_options: MonoOptions,
|
||||
emit_options: EmitOptions,
|
||||
) -> io::Result<()> {
|
||||
let Modules {
|
||||
before_any,
|
||||
other_modules,
|
||||
test_module,
|
||||
} = program;
|
||||
|
||||
if !before_any.is_empty() {
|
||||
writeln!(writer, "{before_any}\n")?;
|
||||
}
|
||||
|
||||
for (module, source) in other_modules.iter() {
|
||||
writeln!(writer, "## module {module}")?;
|
||||
writeln!(writer, "{}\n", source)?;
|
||||
}
|
||||
|
||||
if !other_modules.is_empty() {
|
||||
writeln!(writer, "## module Test")?;
|
||||
}
|
||||
|
||||
// Reverse the queries so that we can pop them off the end as we pass through the lines.
|
||||
let (queries, program) = inferred_program.decompose();
|
||||
let mut sorted_queries = queries.into_sorted();
|
||||
sorted_queries.reverse();
|
||||
|
||||
let mut reflow = Reflow::new_unindented(writer);
|
||||
write_source_with_answers(&mut reflow, source, sorted_queries, 0)?;
|
||||
write_source_with_answers(&mut reflow, test_module, sorted_queries, 0)?;
|
||||
|
||||
// Finish up with any remaining print options we were asked to provide.
|
||||
let PrintOptions { can_decls } = print_options;
|
||||
// Finish up with any remaining emit options we were asked to provide.
|
||||
let EmitOptions { can_decls, mono } = emit_options;
|
||||
if can_decls {
|
||||
writeln!(writer, "\n{EMIT_HEADER}can_decls")?;
|
||||
program.write_can_decls(writer)?;
|
||||
}
|
||||
|
||||
if mono {
|
||||
writeln!(writer, "\n{EMIT_HEADER}mono")?;
|
||||
// Unfortunately, with the current setup we must now recompile into the IR.
|
||||
// TODO: extend the data returned by a monomorphized module to include
|
||||
// that of a solved module.
|
||||
mono::write_compiled_ir(
|
||||
writer,
|
||||
test_module,
|
||||
other_modules,
|
||||
mono_options,
|
||||
can_options.allow_errors,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# +opt print:can_decls
|
||||
# +opt infer:print_only_under_alias
|
||||
# +emit:can_decls
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Parser a : {} -> a
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
foo : a, Bool -> Str
|
||||
foo = \in, b -> if b then "done" else bar in
|
||||
# ^^^ a -[[bar(2)]]-> Str
|
||||
# ^^^ a, Bool -[[foo(1)]]-> Str
|
||||
|
||||
bar = \_ -> foo {} Bool.true
|
||||
# ^^^ {}, Bool -[[]]-> Str
|
||||
|
||||
foo "" Bool.false
|
||||
# ^^^{inst} Str, Bool -[[foo(1)]]-> Str
|
||||
# │ foo : a, Bool -> Str
|
||||
# │ foo = \in, b -> if b then "done" else bar in
|
||||
# │ ^^^ Str -[[bar(2)]]-> Str
|
||||
# │ ^^^ Str, Bool -[[foo(1)]]-> Str
|
|
@ -0,0 +1,17 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
f = \x -> x + 1
|
||||
|
||||
map : { f1: (I64 -> I64) } -> List I64
|
||||
map = \{ f1 } -> List.concat [f1 1] (map { f1 })
|
||||
# ^^^ { f1 : I64 -[[]]-> I64 } -[[map(2)]]-> List I64
|
||||
# ^^^ { f1 : I64 -[[]]-> I64 } -[[map(2)]]-> List I64
|
||||
|
||||
|
||||
map { f1: f }
|
||||
# ^^^{inst} { f1 : I64 -[[f(1)]]-> I64 } -[[map(2)]]-> List I64
|
||||
# │ map : { f1: (I64 -> I64) } -> List I64
|
||||
# │ map = \{ f1 } -> List.concat [f1 1] (map { f1 })
|
||||
# │ ^^^ { f1 : I64 -[[f(1)]]-> I64 } -[[map(2)]]-> List I64
|
||||
# │ ^^^ { f1 : I64 -[[f(1)]]-> I64 } -[[map(2)]]-> List I64
|
|
@ -0,0 +1,18 @@
|
|||
# +opt can:allow_errors
|
||||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [single] to "./platform"
|
||||
|
||||
LL a : [Nil, Cons a (LL a)]
|
||||
|
||||
LinkedList a : LL a
|
||||
|
||||
single : a -> LinkedList a
|
||||
single = \item -> (Cons item Nil)
|
||||
#^^^^^^{-1} a -[[single(0)]]-> [Cons a b, Nil]* as b
|
||||
|
||||
walk : LinkedList elem, state, (state, elem -> state) -> state
|
||||
walk = \list, state, fn ->
|
||||
#^^^^{-1} [Cons elem a, Nil] as a, state, (state, elem -[[]]-> state) -[[walk(3)]]-> state
|
||||
when list is
|
||||
Nil -> state
|
||||
Cons first rest -> walk (rest) (fn state first) fn
|
|
@ -0,0 +1,16 @@
|
|||
# +opt can:allow_errors
|
||||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [single] to "./platform"
|
||||
|
||||
LL a : [Nil, Cons a (LL a)]
|
||||
|
||||
LinkedList a := LL a
|
||||
|
||||
single = \item -> @LinkedList (Cons item Nil)
|
||||
#^^^^^^{-1} a -[[single(0)]]-> [Cons a b, Nil] as b
|
||||
|
||||
walk = \@LinkedList list, state, fn ->
|
||||
#^^^^{-1} [Cons a b, Nil] as b, c, (c, a -[[]]-> c) -[[walk(3)]]-> c
|
||||
when list is
|
||||
Nil -> state
|
||||
Cons first rest -> walk (@LinkedList rest) (fn state first) fn
|
|
@ -0,0 +1,25 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Effect : {} -> Str
|
||||
|
||||
after = \fx, toNext ->
|
||||
afterInner = \{} ->
|
||||
fxOut = fx {}
|
||||
next = toNext fxOut
|
||||
next {}
|
||||
|
||||
afterInner
|
||||
|
||||
await : Effect, (Str -> Effect) -> Effect
|
||||
await = \fx, cont -> after fx (\result -> cont result)
|
||||
|
||||
line : Str -> Effect
|
||||
line = \s -> \{} -> s
|
||||
|
||||
main =
|
||||
#^^^^{-1} {} -[[afterInner(8) ({} -[[afterInner(8) ({} -a-> Str) (Str -[[13 (Str -[[20 Str]]-> ({} -[[16 Str]]-> Str))]]-> ({} -[[16 Str]]-> Str)), 16 Str] as a]-> Str) (Str -[[13 (Str -[[21]]-> ({} -[[16 Str]]-> Str))]]-> ({} -[[16 Str]]-> Str))] as [[afterInner(8) ({} -[[afterInner(8) ({} -a-> Str) (Str -[[13 (Str -[[20 Str]]-> ({} -[[16 Str]]-> Str))]]-> ({} -[[16 Str]]-> Str)), 16 Str] as a]-> Str) (Str -[[13 (Str -[[21]]-> ({} -[[16 Str]]-> Str))]]-> ({} -[[16 Str]]-> Str))] as b]]-> Str
|
||||
await
|
||||
(List.walk ["a", "b"] (line "printing letters") (\state, elem -> await state (\_ -> line elem)))
|
||||
(\_ -> line "")
|
|
@ -0,0 +1,32 @@
|
|||
# +emit:mono
|
||||
# +opt mono:no_check
|
||||
|
||||
## module Dep
|
||||
interface Dep exposes [defaultRequest] imports []
|
||||
|
||||
defaultRequest = {
|
||||
url: "",
|
||||
body: "",
|
||||
}
|
||||
|
||||
## module Test
|
||||
app "test" imports [Dep.{ defaultRequest }] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
{ defaultRequest & url: "http://www.example.com" }
|
||||
|
||||
# -emit:mono
|
||||
procedure Dep.0 ():
|
||||
let Dep.2 : Str = "";
|
||||
let Dep.3 : Str = "";
|
||||
let Dep.1 : {Str, Str} = Struct {Dep.2, Dep.3};
|
||||
ret Dep.1;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : Str = "http://www.example.com";
|
||||
let Test.4 : {Str, Str} = CallByName Dep.0;
|
||||
let Test.2 : Str = StructAtIndex 0 Test.4;
|
||||
inc Test.2;
|
||||
dec Test.4;
|
||||
let Test.1 : {Str, Str} = Struct {Test.2, Test.3};
|
||||
ret Test.1;
|
|
@ -626,19 +626,42 @@ samp .comment,
|
|||
code .comment {
|
||||
color: var(--green);
|
||||
}
|
||||
/* Number, String, Tag, Type literals */
|
||||
|
||||
/* Number, String, Tag literals */
|
||||
samp .storage.type,
|
||||
code .storage.type,
|
||||
samp .string,
|
||||
code .string,
|
||||
samp .string.begin,
|
||||
code .string.begin,
|
||||
samp .string.end,
|
||||
code .string.end,
|
||||
samp .constant,
|
||||
code .constant,
|
||||
samp .literal,
|
||||
code .literal {
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
/* Keywords and punctuation */
|
||||
samp .kw,
|
||||
samp .keyword,
|
||||
code .keyword,
|
||||
samp .punctuation.section,
|
||||
code .punctuation.section,
|
||||
samp .punctuation.separator,
|
||||
code .punctuation.separator,
|
||||
samp .punctuation.terminator,
|
||||
code .punctuation.terminator,
|
||||
samp .kw,
|
||||
code .kw {
|
||||
color: var(--magenta);
|
||||
color: var(--magenta);
|
||||
}
|
||||
|
||||
/* Operators */
|
||||
samp .op,
|
||||
code .op {
|
||||
code .op,
|
||||
samp .keyword.operator,
|
||||
code .keyword.operator {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
|
@ -649,12 +672,22 @@ code .delimeter {
|
|||
}
|
||||
|
||||
/* Variables modules and field names */
|
||||
samp .function,
|
||||
code .function,
|
||||
samp .meta.group,
|
||||
code .meta.group,
|
||||
samp .meta.block,
|
||||
code .meta.block,
|
||||
samp .lowerident,
|
||||
code .lowerident {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
/* Types, Tags, and Modules */
|
||||
samp .type,
|
||||
code .type,
|
||||
samp .meta.path,
|
||||
code .meta.path,
|
||||
samp .upperident,
|
||||
code .upperident {
|
||||
color: var(--green);
|
||||
|
|
|
@ -360,27 +360,30 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
|||
})
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||
use Content::*;
|
||||
use IntWidth::*;
|
||||
|
||||
match (env.subs.get_content_without_compacting(raw_var), int_width) {
|
||||
(Alias(Symbol::NUM_UNSIGNED8 | Symbol::NUM_U8, ..), U8) => num_helper!(u8),
|
||||
(_, U8) => {
|
||||
// This is not a number, it's a tag union or something else
|
||||
app.call_function(main_fn_name, |_mem: &A::Memory, num: u8| {
|
||||
byte_to_ast(env, num, env.subs.get_content_without_compacting(raw_var))
|
||||
})
|
||||
match int_width {
|
||||
U8 => {
|
||||
let raw_content = env.subs.get_content_without_compacting(raw_var);
|
||||
if matches!(raw_content, Content::Alias(name, ..) if name.module_id() == ModuleId::NUM)
|
||||
{
|
||||
num_helper!(u8)
|
||||
} else {
|
||||
// This is not a number, it's a tag union or something else
|
||||
app.call_function(main_fn_name, |_mem: &A::Memory, num: u8| {
|
||||
byte_to_ast(env, num, env.subs.get_content_without_compacting(raw_var))
|
||||
})
|
||||
}
|
||||
}
|
||||
// The rest are numbers... for now
|
||||
(_, U16) => num_helper!(u16),
|
||||
(_, U32) => num_helper!(u32),
|
||||
(_, U64) => num_helper!(u64),
|
||||
(_, U128) => num_helper!(u128),
|
||||
(_, I8) => num_helper!(i8),
|
||||
(_, I16) => num_helper!(i16),
|
||||
(_, I32) => num_helper!(i32),
|
||||
(_, I64) => num_helper!(i64),
|
||||
(_, I128) => num_helper!(i128),
|
||||
U16 => num_helper!(u16),
|
||||
U32 => num_helper!(u32),
|
||||
U64 => num_helper!(u64),
|
||||
U128 => num_helper!(u128),
|
||||
I8 => num_helper!(i8),
|
||||
I16 => num_helper!(i16),
|
||||
I32 => num_helper!(i32),
|
||||
I64 => num_helper!(i64),
|
||||
I128 => num_helper!(i128),
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(float_width)) => {
|
||||
|
|
|
@ -1180,4 +1180,74 @@ mod test {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_access() {
|
||||
run_expect_test(
|
||||
indoc!(
|
||||
r#"
|
||||
interface Test exposes [] imports []
|
||||
|
||||
expect
|
||||
t = ("One", "Two")
|
||||
t.1 == "One"
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
This expectation failed:
|
||||
|
||||
3│> expect
|
||||
4│> t = ("One", "Two")
|
||||
5│> t.1 == "One"
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
t : (
|
||||
Str,
|
||||
Str,
|
||||
)a
|
||||
t = ("One", "Two")
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_on_opaque_number_type() {
|
||||
run_expect_test(
|
||||
indoc!(
|
||||
r#"
|
||||
interface Test exposes [] imports []
|
||||
|
||||
hexToByte : U8, U8 -> U8
|
||||
hexToByte = \upper, lower ->
|
||||
Num.bitwiseOr (Num.shiftRightBy upper 4) lower
|
||||
|
||||
expect
|
||||
actual = hexToByte 7 4
|
||||
expected = 't'
|
||||
actual == expected
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
This expectation failed:
|
||||
|
||||
7│> expect
|
||||
8│> actual = hexToByte 7 4
|
||||
9│> expected = 't'
|
||||
10│> actual == expected
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
actual : U8
|
||||
actual = 4
|
||||
|
||||
expected : Int Unsigned8
|
||||
expected = 116
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1138,8 +1138,8 @@ fn to_expr_report<'b>(
|
|||
),
|
||||
}
|
||||
}
|
||||
Reason::FnCall { name, arity } => match count_arguments(&found) {
|
||||
0 => {
|
||||
Reason::FnCall { name, arity } => match describe_wanted_function(&found) {
|
||||
DescribedFunction::NotAFunction(tag) => {
|
||||
let this_value = match name {
|
||||
None => alloc.text("This value"),
|
||||
Some(symbol) => alloc.concat([
|
||||
|
@ -1149,30 +1149,43 @@ fn to_expr_report<'b>(
|
|||
]),
|
||||
};
|
||||
|
||||
let lines = vec![
|
||||
alloc.concat([
|
||||
this_value,
|
||||
alloc.string(format!(
|
||||
" is not a function, but it was given {}:",
|
||||
if arity == 1 {
|
||||
"1 argument".into()
|
||||
} else {
|
||||
format!("{} arguments", arity)
|
||||
}
|
||||
)),
|
||||
use NotAFunctionTag::*;
|
||||
let doc = match tag {
|
||||
OpaqueNeedsUnwrap => alloc.stack([
|
||||
alloc.concat([
|
||||
this_value,
|
||||
alloc.reflow(
|
||||
" is an opaque type, so it cannot be called with an argument:",
|
||||
),
|
||||
]),
|
||||
alloc.region(lines.convert_region(expr_region)),
|
||||
alloc.reflow("I can't call an opaque type because I don't know what it is! Maybe you meant to unwrap it first?"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(expr_region)),
|
||||
alloc.reflow("Are there any missing commas? Or missing parentheses?"),
|
||||
];
|
||||
Other => alloc.stack([
|
||||
alloc.concat([
|
||||
this_value,
|
||||
alloc.string(format!(
|
||||
" is not a function, but it was given {}:",
|
||||
if arity == 1 {
|
||||
"1 argument".into()
|
||||
} else {
|
||||
format!("{} arguments", arity)
|
||||
}
|
||||
)),
|
||||
]),
|
||||
alloc.region(lines.convert_region(expr_region)),
|
||||
alloc.reflow("Are there any missing commas? Or missing parentheses?"),
|
||||
]),
|
||||
};
|
||||
|
||||
Report {
|
||||
filename,
|
||||
title: "TOO MANY ARGS".to_string(),
|
||||
doc: alloc.stack(lines),
|
||||
doc,
|
||||
severity,
|
||||
}
|
||||
}
|
||||
n => {
|
||||
DescribedFunction::Arguments(n) => {
|
||||
let this_function = match name {
|
||||
None => alloc.text("This function"),
|
||||
Some(symbol) => alloc.concat([
|
||||
|
@ -1543,13 +1556,35 @@ fn does_not_implement<'a>(
|
|||
])
|
||||
}
|
||||
|
||||
fn count_arguments(tipe: &ErrorType) -> usize {
|
||||
enum DescribedFunction {
|
||||
Arguments(usize),
|
||||
NotAFunction(NotAFunctionTag),
|
||||
}
|
||||
|
||||
enum NotAFunctionTag {
|
||||
OpaqueNeedsUnwrap,
|
||||
Other,
|
||||
}
|
||||
|
||||
fn describe_wanted_function(tipe: &ErrorType) -> DescribedFunction {
|
||||
use ErrorType::*;
|
||||
|
||||
match tipe {
|
||||
Function(args, _, _) => args.len(),
|
||||
Alias(_, _, actual, _) => count_arguments(actual),
|
||||
_ => 0,
|
||||
Function(args, _, _) => DescribedFunction::Arguments(args.len()),
|
||||
Alias(_, _, actual, AliasKind::Structural) => describe_wanted_function(actual),
|
||||
Alias(_, _, actual, AliasKind::Opaque) => {
|
||||
let tag = if matches!(
|
||||
describe_wanted_function(actual),
|
||||
DescribedFunction::Arguments(_)
|
||||
) {
|
||||
NotAFunctionTag::OpaqueNeedsUnwrap
|
||||
} else {
|
||||
NotAFunctionTag::Other
|
||||
};
|
||||
|
||||
DescribedFunction::NotAFunction(tag)
|
||||
}
|
||||
_ => DescribedFunction::NotAFunction(NotAFunctionTag::Other),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1237,20 +1237,20 @@ mod test_reporting {
|
|||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This 1st argument to `f` has an unexpected type:
|
||||
This expression is used in an unexpected way:
|
||||
|
||||
7│ g = \x -> f [x]
|
||||
^^^
|
||||
^^^^^
|
||||
|
||||
The argument is a list of type:
|
||||
This `f` call produces:
|
||||
|
||||
List List b
|
||||
|
||||
But you are trying to use it as:
|
||||
|
||||
List b
|
||||
|
||||
But `f` needs its 1st argument to be:
|
||||
|
||||
a
|
||||
|
||||
Tip: The type annotation uses the type variable `a` to say that this
|
||||
Tip: The type annotation uses the type variable `b` to say that this
|
||||
definition can produce any type of value. But in the body I see that
|
||||
it will only produce a `List` value of a single specific type. Maybe
|
||||
change the type annotation to be more specific? Maybe change the code
|
||||
|
@ -12689,42 +12689,6 @@ I recommend using camelCase. It's the standard style in Roc code!
|
|||
)
|
||||
);
|
||||
|
||||
test_report!(
|
||||
polymorphic_recursion_forces_ungeneralized_type,
|
||||
indoc!(
|
||||
r#"
|
||||
foo : a, Bool -> Str
|
||||
foo = \in, b -> if b then "done" else bar in
|
||||
|
||||
bar = \_ -> foo {} Bool.true
|
||||
|
||||
foo "" Bool.false
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This 1st argument to `foo` has an unexpected type:
|
||||
|
||||
9│ foo "" Bool.false
|
||||
^^
|
||||
|
||||
The argument is a string of type:
|
||||
|
||||
Str
|
||||
|
||||
But `foo` needs its 1st argument to be:
|
||||
|
||||
a
|
||||
|
||||
Tip: The type annotation uses the type variable `a` to say that this
|
||||
definition can produce any type of value. But in the body I see that
|
||||
it will only produce a `Str` value of a single specific type. Maybe
|
||||
change the type annotation to be more specific? Maybe change the code
|
||||
to be more general?
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
suggest_binding_rigid_var_to_ability,
|
||||
indoc!(
|
||||
|
@ -13417,4 +13381,33 @@ I recommend using camelCase. It's the standard style in Roc code!
|
|||
"#
|
||||
)
|
||||
);
|
||||
|
||||
test_report!(
|
||||
apply_opaque_as_function,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Parser a := Str -> a
|
||||
|
||||
parser : Parser Str
|
||||
parser = @Parser \s -> Str.concat s "asd"
|
||||
|
||||
main : Str
|
||||
main = parser "hi"
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
The `parser` value is an opaque type, so it cannot be called with an
|
||||
argument:
|
||||
|
||||
9│ main = parser "hi"
|
||||
^^^^^^
|
||||
|
||||
I can't call an opaque type because I don't know what it is! Maybe you
|
||||
meant to unwrap it first?
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ version.workspace = true
|
|||
|
||||
[dev-dependencies]
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
roc_command_utils = { path = "../utils/command" }
|
||||
roc_build = { path = "../compiler/build" }
|
||||
roc_linker = { path = "../linker" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
|
@ -20,5 +21,14 @@ indoc.workspace = true
|
|||
target-lexicon.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["target-aarch64", "target-x86_64", "target-wasm32"]
|
||||
|
||||
target-aarch64 = ["roc_build/target-aarch64"]
|
||||
target-arm = []
|
||||
target-wasm32 = []
|
||||
target-x86 = []
|
||||
target-x86_64 = ["roc_build/target-x86_64"]
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
development = ["roc_build", "roc_linker"]
|
||||
development = ["roc_build", "roc_linker"]
|
||||
|
|
|
@ -10,9 +10,8 @@ fn build_host() {
|
|||
use roc_build::program::build_and_preprocess_host;
|
||||
use roc_linker::preprocessed_host_filename;
|
||||
|
||||
let platform_main_roc = std::env::current_dir()
|
||||
.unwrap()
|
||||
.join("zig-platform/main.roc");
|
||||
let platform_main_roc =
|
||||
roc_command_utils::root_dir().join("crates/valgrind/zig-platform/main.roc");
|
||||
|
||||
// tests always run on the host
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
@ -58,15 +57,16 @@ fn valgrind_test_linux(source: &str) {
|
|||
// the host is identical for all tests so we only want to build it once
|
||||
BUILD_ONCE.call_once(build_host);
|
||||
|
||||
let pf = std::env::current_dir()
|
||||
.unwrap()
|
||||
.join("zig-platform/main.roc");
|
||||
let pf = roc_command_utils::root_dir().join("crates/valgrind/zig-platform/main.roc");
|
||||
|
||||
assert!(pf.exists(), "cannot find platform {:?}", &pf);
|
||||
|
||||
let mut app_module_source = format!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
let concat_header = !source.trim().starts_with("app ");
|
||||
|
||||
let mut app_module_source = if concat_header {
|
||||
format!(
|
||||
indoc::indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
packages {{ pf: "{}" }}
|
||||
imports []
|
||||
|
@ -74,16 +74,26 @@ fn valgrind_test_linux(source: &str) {
|
|||
|
||||
main =
|
||||
"#
|
||||
),
|
||||
pf.to_str().unwrap()
|
||||
);
|
||||
),
|
||||
pf.to_str().unwrap()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
for line in source.lines() {
|
||||
app_module_source.push_str(" ");
|
||||
if concat_header {
|
||||
app_module_source.push_str(" ");
|
||||
}
|
||||
app_module_source.push_str(line);
|
||||
app_module_source.push('\n');
|
||||
}
|
||||
|
||||
if !concat_header {
|
||||
app_module_source =
|
||||
app_module_source.replace("replace_me_platform_path", &pf.display().to_string());
|
||||
}
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let app_module_path = temp_dir.path().join("app.roc");
|
||||
|
||||
|
@ -319,3 +329,224 @@ fn str_concat_later_referencing_empty_list_with_capacity() {
|
|||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn joinpoint_with_closure() {
|
||||
valgrind_test(indoc!(
|
||||
r#"
|
||||
(
|
||||
Animal : [Cat, Dog, Goose]
|
||||
|
||||
makeSound : Animal -> Str
|
||||
makeSound = \animal ->
|
||||
dogSound = "Woof"
|
||||
when animal is
|
||||
Cat | Dog if isCat animal -> "Miauw"
|
||||
Goose -> "Honk"
|
||||
_ -> dogSound
|
||||
|
||||
isCat : Animal -> Bool
|
||||
isCat = \animal ->
|
||||
when animal is
|
||||
Cat -> Bool.true
|
||||
_ -> Bool.false
|
||||
|
||||
test =
|
||||
catSound = makeSound Cat
|
||||
dogSound = makeSound Dog
|
||||
gooseSound = makeSound Goose
|
||||
"Cat: \(catSound), Dog: \(dogSound), Goose: \(gooseSound)"
|
||||
|
||||
test
|
||||
)
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn joinpoint_with_reuse() {
|
||||
valgrind_test(indoc!(
|
||||
r#"
|
||||
(
|
||||
LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
|
||||
# mapLinkedList : LinkedList a, (a -> b) -> LinkedList b
|
||||
mapLinkedList = \linkedList, f -> when linkedList is
|
||||
Nil -> Nil
|
||||
Cons x xs ->
|
||||
x2 = if Bool.true then x else x
|
||||
Cons (f x2) (mapLinkedList xs f)
|
||||
|
||||
# printLinkedList : LinkedList a, (a -> Str) -> Str
|
||||
printLinkedList = \linkedList, f ->
|
||||
when linkedList is
|
||||
Nil -> "Nil"
|
||||
Cons x xs ->
|
||||
strX = f x
|
||||
strXs = printLinkedList xs f
|
||||
"Cons \(strX) (\(strXs))"
|
||||
|
||||
test =
|
||||
newList = mapLinkedList (Cons 1 (Cons 2 (Cons 3 Nil))) (\x -> x + 1)
|
||||
printLinkedList newList Num.toStr
|
||||
|
||||
test
|
||||
)
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_rebalance() {
|
||||
valgrind_test(indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
packages { pf: "replace_me_platform_path" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = show (insert 0 {} Empty)
|
||||
|
||||
insert : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
|
||||
insert = \key, value, dict ->
|
||||
when insertHelp key value dict is
|
||||
Node Red k v l r -> Node Black k v l r
|
||||
x -> x
|
||||
|
||||
insertHelp : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
|
||||
insertHelp = \key, value, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
# New nodes are always red. If it violates the rules, it will be fixed
|
||||
# when balancing.
|
||||
Node Red key value Empty Empty
|
||||
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
when Num.compare key nKey is
|
||||
LT -> balance nColor nKey nValue (insertHelp key value nLeft) nRight
|
||||
EQ -> Node nColor nKey value nLeft nRight
|
||||
GT -> balance nColor nKey nValue nLeft (insertHelp key value nRight)
|
||||
|
||||
balance : NodeColor, k, v, RedBlackTree k v, RedBlackTree k v -> RedBlackTree k v
|
||||
balance = \color, key, value, left, right ->
|
||||
when right is
|
||||
Node Red rK rV rLeft rRight ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
Red
|
||||
key
|
||||
value
|
||||
(Node Black lK lV lLeft lRight)
|
||||
(Node Black rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
Node color rK rV (Node Red key value left rLeft) rRight
|
||||
|
||||
_ ->
|
||||
when left is
|
||||
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black key value lRight right)
|
||||
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
|
||||
show : RedBlackTree I64 {} -> Str
|
||||
show = \tree -> showRBTree tree Num.toStr (\{} -> "{}")
|
||||
|
||||
showRBTree : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
|
||||
showRBTree = \tree, showKey, showValue ->
|
||||
when tree is
|
||||
Empty -> "Empty"
|
||||
Node color key value left right ->
|
||||
sColor = showColor color
|
||||
sKey = showKey key
|
||||
sValue = showValue value
|
||||
sL = nodeInParens left showKey showValue
|
||||
sR = nodeInParens right showKey showValue
|
||||
|
||||
"Node \(sColor) \(sKey) \(sValue) \(sL) \(sR)"
|
||||
|
||||
nodeInParens : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
|
||||
nodeInParens = \tree, showKey, showValue ->
|
||||
when tree is
|
||||
Empty ->
|
||||
showRBTree tree showKey showValue
|
||||
|
||||
Node _ _ _ _ _ ->
|
||||
inner = showRBTree tree showKey showValue
|
||||
|
||||
"(\(inner))"
|
||||
|
||||
showColor : NodeColor -> Str
|
||||
showColor = \color ->
|
||||
when color is
|
||||
Red -> "Red"
|
||||
Black -> "Black"
|
||||
|
||||
NodeColor : [Red, Black]
|
||||
|
||||
RedBlackTree k v : [Node NodeColor k v (RedBlackTree k v) (RedBlackTree k v), Empty]
|
||||
|
||||
Key k : Num k
|
||||
|
||||
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lowlevel_list_calls() {
|
||||
valgrind_test(indoc!(
|
||||
r#"
|
||||
(
|
||||
a = List.map [1,1,1,1,1] (\x -> x + 0)
|
||||
b = List.map2 a [1,1,1,1,1] (\x, y -> x + y)
|
||||
c = List.map3 a b [1,1,1,1,1] (\x, y, z -> x + y + z)
|
||||
d = List.map4 a b c [1,1,1,1,1] (\x, y, z, w -> x + y + z + w)
|
||||
e = List.sortWith d (\x, y -> Num.compare x y)
|
||||
|
||||
Num.toStr (List.len e)
|
||||
)
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn joinpoint_nullpointer() {
|
||||
valgrind_test(indoc!(
|
||||
r#"
|
||||
(
|
||||
LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
|
||||
printLinkedList : LinkedList Str -> Str
|
||||
printLinkedList = \linkedList->
|
||||
when linkedList is
|
||||
Nil -> "Nil"
|
||||
Cons x xs ->
|
||||
strXs = printLinkedList xs
|
||||
"Cons \(x) (\(strXs))"
|
||||
|
||||
linkedListHead : LinkedList Str -> LinkedList Str
|
||||
linkedListHead = \linkedList ->
|
||||
string = when linkedList is
|
||||
Cons s _ -> s
|
||||
Nil -> ""
|
||||
Cons string Nil
|
||||
|
||||
test =
|
||||
cons = printLinkedList (linkedListHead (Cons "foo" Nil))
|
||||
nil = printLinkedList (linkedListHead (Nil))
|
||||
"\(cons) - \(nil)"
|
||||
|
||||
test
|
||||
)
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
|
|
@ -54,7 +54,6 @@ impl<'a> WasiDispatcher<'a> {
|
|||
memory: &mut [u8],
|
||||
) -> Option<Value> {
|
||||
let success_code = Some(Value::I32(Errno::Success as i32));
|
||||
|
||||
match function_name {
|
||||
"args_get" => {
|
||||
// uint8_t ** argv,
|
||||
|
@ -97,7 +96,37 @@ impl<'a> WasiDispatcher<'a> {
|
|||
"fd_allocate" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_close" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_datasync" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_fdstat_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_fdstat_get" => {
|
||||
// (i32, i32) -> i32
|
||||
|
||||
// file descriptor
|
||||
let fd = arguments[0].expect_i32().unwrap() as usize;
|
||||
// ptr to a wasi_fdstat_t
|
||||
let stat_mut_ptr = arguments[1].expect_i32().unwrap() as usize;
|
||||
|
||||
match fd {
|
||||
1 => {
|
||||
// Tell WASI that stdout is a tty (no seek or tell)
|
||||
// https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/libc-bottom-half/sources/isatty.c
|
||||
// *Not* a tty if:
|
||||
// (statbuf.fs_filetype != __WASI_FILETYPE_CHARACTER_DEVICE ||
|
||||
// (statbuf.fs_rights_base & (__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL)) != 0)
|
||||
// So it's sufficient to set:
|
||||
// .fs_filetype = __WASI_FILETYPE_CHARACTER_DEVICE
|
||||
// .fs_rights_base = 0
|
||||
|
||||
const WASI_FILETYPE_CHARACTER_DEVICE: u8 = 2;
|
||||
memory[stat_mut_ptr] = WASI_FILETYPE_CHARACTER_DEVICE;
|
||||
|
||||
for b in memory[stat_mut_ptr + 1..stat_mut_ptr + 24].iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
}
|
||||
_ => todo!("WASI {}({:?})", function_name, arguments),
|
||||
}
|
||||
|
||||
success_code
|
||||
}
|
||||
"fd_fdstat_set_flags" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_fdstat_set_rights" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"fd_filestat_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
|
@ -211,14 +240,16 @@ impl<'a> WasiDispatcher<'a> {
|
|||
|
||||
let mut n_written: i32 = 0;
|
||||
let mut negative_length_count = 0;
|
||||
for _ in 0..iovs_len {
|
||||
let mut write_result = Ok(());
|
||||
for i in 0..iovs_len {
|
||||
// https://man7.org/linux/man-pages/man2/readv.2.html
|
||||
// struct iovec {
|
||||
// void *iov_base; /* Starting address */
|
||||
// size_t iov_len; /* Number of bytes to transfer */
|
||||
// };
|
||||
let iov_base = read_u32(memory, ptr_iovs) as usize;
|
||||
let iov_len = read_i32(memory, ptr_iovs + 4);
|
||||
let ptr_iov = ptr_iovs + (8 * i as usize); // index into the array of iovec's
|
||||
let iov_base = read_u32(memory, ptr_iov) as usize;
|
||||
let iov_len = read_i32(memory, ptr_iov + 4);
|
||||
if iov_len < 0 {
|
||||
// I found negative-length iov's when I implemented this in JS for the web REPL (see wasi.js)
|
||||
// I'm not sure why, but this solution worked, and it's the same WASI libc - there's only one.
|
||||
|
@ -228,18 +259,15 @@ impl<'a> WasiDispatcher<'a> {
|
|||
}
|
||||
let bytes = &memory[iov_base..][..iov_len as usize];
|
||||
|
||||
match &mut write_lock {
|
||||
WriteLock::StdOut(stdout) => {
|
||||
n_written += stdout.write(bytes).unwrap() as i32;
|
||||
}
|
||||
WriteLock::Stderr(stderr) => {
|
||||
n_written += stderr.write(bytes).unwrap() as i32;
|
||||
}
|
||||
WriteLock::RegularFile(content) => {
|
||||
content.extend_from_slice(bytes);
|
||||
n_written += bytes.len() as i32;
|
||||
}
|
||||
write_result = match &mut write_lock {
|
||||
WriteLock::StdOut(stdout) => stdout.write_all(bytes),
|
||||
WriteLock::Stderr(stderr) => stderr.write_all(bytes),
|
||||
WriteLock::RegularFile(content) => content.write_all(bytes),
|
||||
};
|
||||
if write_result.is_err() {
|
||||
break;
|
||||
}
|
||||
n_written += bytes.len() as i32;
|
||||
}
|
||||
|
||||
write_i32(memory, ptr_nwritten, n_written);
|
||||
|
@ -251,7 +279,10 @@ impl<'a> WasiDispatcher<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
success_code
|
||||
match write_result {
|
||||
Ok(()) => success_code,
|
||||
Err(_) => Some(Value::I32(Errno::Io as i32)),
|
||||
}
|
||||
}
|
||||
"path_create_directory" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"path_filestat_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
|
|
|
@ -18,6 +18,7 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
syntect = "5.0"
|
||||
roc_highlight = { path = "../../../crates/highlight" }
|
||||
roc_std = { path = "../../../crates/roc_std" }
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@ use std::fs;
|
|||
use std::os::raw::c_char;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::highlighting::{ThemeSet, Style};
|
||||
use syntect::util::{LinesWithEndings};
|
||||
use syntect::html::{ClassedHTMLGenerator, ClassStyle};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__transformFileContentForHost_1_exposed"]
|
||||
fn roc_transformFileContentForHost(relPath: &RocStr, content: &RocStr) -> RocStr;
|
||||
|
@ -210,21 +216,34 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
|
|||
// And track a little bit of state
|
||||
let mut in_code_block = false;
|
||||
let mut is_roc_code = false;
|
||||
|
||||
let syntax_set : syntect::parsing::SyntaxSet = SyntaxSet::load_defaults_newlines();
|
||||
let theme_set : syntect::highlighting::ThemeSet = ThemeSet::load_defaults();
|
||||
|
||||
for event in parser {
|
||||
match event {
|
||||
pulldown_cmark::Event::Code(cow_str) => {
|
||||
let highlighted_html =
|
||||
roc_highlight::highlight_roc_code_inline(cow_str.to_string().as_str());
|
||||
parser_with_highlighting.push(pulldown_cmark::Event::Html(
|
||||
pulldown_cmark::CowStr::from(highlighted_html),
|
||||
));
|
||||
pulldown_cmark::Event::Code(code_str) => {
|
||||
if code_str.starts_with("roc!") {
|
||||
let stripped = code_str
|
||||
.strip_prefix("roc!")
|
||||
.expect("expected leading 'roc!'");
|
||||
|
||||
let highlighted_html = roc_highlight::highlight_roc_code_inline(stripped.to_string().as_str());
|
||||
|
||||
parser_with_highlighting.push(pulldown_cmark::Event::Html(
|
||||
pulldown_cmark::CowStr::from(highlighted_html),
|
||||
));
|
||||
} else {
|
||||
let inline_code = pulldown_cmark::CowStr::from(format!("<code>{}</code>", code_str));
|
||||
parser_with_highlighting.push(
|
||||
pulldown_cmark::Event::Html(inline_code)
|
||||
);
|
||||
}
|
||||
}
|
||||
pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock(cbk)) => {
|
||||
in_code_block = true;
|
||||
is_roc_code = is_roc_code_block(&cbk);
|
||||
}
|
||||
pulldown_cmark::Event::End(pulldown_cmark::Tag::CodeBlock(_)) => {
|
||||
pulldown_cmark::Event::End(pulldown_cmark::Tag::CodeBlock(pulldown_cmark::CodeBlockKind::Fenced(extention_str))) => {
|
||||
if in_code_block {
|
||||
match replace_code_with_static_file(&code_to_highlight, input_file) {
|
||||
None => {}
|
||||
|
@ -243,6 +262,14 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
|
|||
let highlighted_html: String;
|
||||
if is_roc_code {
|
||||
highlighted_html = roc_highlight::highlight_roc_code(&code_to_highlight)
|
||||
} else if let Some(syntax) = syntax_set.find_syntax_by_token(&extention_str) {
|
||||
let mut h = HighlightLines::new(syntax, &theme_set.themes["base16-ocean.dark"]);
|
||||
|
||||
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
|
||||
for line in LinesWithEndings::from(&code_to_highlight) {
|
||||
html_generator.parse_html_for_line_which_includes_newline(line);
|
||||
}
|
||||
highlighted_html = format!("<pre><samp>{}</pre></samp>", html_generator.finalize())
|
||||
} else {
|
||||
highlighted_html = format!("<pre><samp>{}</pre></samp>", &code_to_highlight)
|
||||
}
|
||||
|
@ -361,4 +388,4 @@ fn replace_code_with_static_file(code: &str, input_file: &Path) -> Option<String
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -635,48 +635,78 @@ h4 {
|
|||
/* Comments `#` and Documentation comments `##` */
|
||||
samp .comment,
|
||||
code .comment {
|
||||
color: var(--green);
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
/* Number, String, Tag, Type literals */
|
||||
/* Number, String, Tag literals */
|
||||
samp .storage.type,
|
||||
code .storage.type,
|
||||
samp .string,
|
||||
code .string,
|
||||
samp .string.begin,
|
||||
code .string.begin,
|
||||
samp .string.end,
|
||||
code .string.end,
|
||||
samp .constant,
|
||||
code .constant,
|
||||
samp .literal,
|
||||
code .literal {
|
||||
color: var(--cyan);
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
/* Keywords and punctuation */
|
||||
samp .kw,
|
||||
samp .keyword,
|
||||
code .keyword,
|
||||
samp .punctuation.section,
|
||||
code .punctuation.section,
|
||||
samp .punctuation.separator,
|
||||
code .punctuation.separator,
|
||||
samp .punctuation.terminator,
|
||||
code .punctuation.terminator,
|
||||
samp .kw,
|
||||
code .kw {
|
||||
color: var(--magenta);
|
||||
}
|
||||
|
||||
/* Operators */
|
||||
samp .op,
|
||||
code .op {
|
||||
color: var(--orange);
|
||||
code .op,
|
||||
samp .keyword.operator,
|
||||
code .keyword.operator {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
/* Delimieters */
|
||||
samp .delimeter,
|
||||
code .delimeter {
|
||||
color: var(--gray);
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
/* Variables modules and field names */
|
||||
samp .function,
|
||||
code .function,
|
||||
samp .meta.group,
|
||||
code .meta.group,
|
||||
samp .meta.block,
|
||||
code .meta.block,
|
||||
samp .lowerident,
|
||||
code .lowerident {
|
||||
color: var(--blue);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
/* Types, Tags, and Modules */
|
||||
samp .type,
|
||||
code .type,
|
||||
samp .meta.path,
|
||||
code .meta.path,
|
||||
samp .upperident,
|
||||
code .upperident {
|
||||
color: var(--green);
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
samp .dim,
|
||||
code .dim {
|
||||
opacity: 0.55;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue