From 14d6de626e894c0c6efb2411f21ad00083b95a06 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 16:01:38 +0100 Subject: [PATCH 1/6] add module for effect_module --- compiler/load/src/effect_module.rs | 2 ++ compiler/load/src/lib.rs | 1 + 2 files changed, 3 insertions(+) create mode 100644 compiler/load/src/effect_module.rs diff --git a/compiler/load/src/effect_module.rs b/compiler/load/src/effect_module.rs new file mode 100644 index 0000000000..618071d3be --- /dev/null +++ b/compiler/load/src/effect_module.rs @@ -0,0 +1,2 @@ + +xx diff --git a/compiler/load/src/lib.rs b/compiler/load/src/lib.rs index dddd6f59b9..52a7a2b7f8 100644 --- a/compiler/load/src/lib.rs +++ b/compiler/load/src/lib.rs @@ -11,4 +11,5 @@ // re-enable this when working on performance optimizations than have it block PRs. #![allow(clippy::large_enum_variant)] pub mod docs; +pub mod effect_module; pub mod file; From 492f0dcc84d842d1ff10708cc02e4415c72e9a93 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 19:14:26 +0100 Subject: [PATCH 2/6] add Effect.always and Effect.map --- compiler/load/src/effect_module.rs | 801 ++++++++++++++++++++++++++++- compiler/load/src/file.rs | 440 ++-------------- examples/effect/Main.roc | 4 +- 3 files changed, 842 insertions(+), 403 deletions(-) diff --git a/compiler/load/src/effect_module.rs b/compiler/load/src/effect_module.rs index 618071d3be..12a2e735bb 100644 --- a/compiler/load/src/effect_module.rs +++ b/compiler/load/src/effect_module.rs @@ -1,2 +1,801 @@ +use roc_can::def::Declaration; +use roc_can::pattern::Pattern; +use roc_collections::all::SendMap; +use roc_module::ident::TagName; +use roc_module::symbol::Symbol; +use roc_region::all::{Located, Region}; +use roc_types::subs::{VarStore, Variable}; +use roc_types::types::Type; -xx +pub fn build_effect_builtins( + env: &mut roc_can::env::Env, + scope: &mut roc_can::scope::Scope, + effect_symbol: Symbol, + var_store: &mut VarStore, + exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>, + declarations: &mut Vec, +) { + // Effect.after : Effect a, (a -> Effect b) -> Effect b + { + let (symbol, def) = build_effect_after( + env, + scope, + effect_symbol, + TagName::Private(effect_symbol), + var_store, + ); + + exposed_vars_by_symbol.push((symbol, def.expr_var)); + declarations.push(Declaration::Declare(def)); + } + + // Effect.map : Effect a, (a -> b) -> Effect b + { + let (symbol, def) = build_effect_map( + env, + scope, + effect_symbol, + TagName::Private(effect_symbol), + var_store, + ); + + exposed_vars_by_symbol.push((symbol, def.expr_var)); + declarations.push(Declaration::Declare(def)); + } + + // Effect.always : a -> Effect a + { + let (symbol, def) = build_effect_always( + env, + scope, + effect_symbol, + TagName::Private(effect_symbol), + var_store, + ); + + exposed_vars_by_symbol.push((symbol, def.expr_var)); + declarations.push(Declaration::Declare(def)); + } +} + +fn build_effect_always( + env: &mut roc_can::env::Env, + scope: &mut roc_can::scope::Scope, + effect_symbol: Symbol, + effect_tag_name: TagName, + var_store: &mut VarStore, +) -> (Symbol, roc_can::def::Def) { + use roc_can::expr::Expr; + use roc_can::expr::Recursive; + + // Effect.always = \value -> @Effect \{} -> value + + let value_symbol = { + scope + .introduce( + "effect_always_value".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let inner_closure_symbol = { + scope + .introduce( + "effect_always_inner".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let always_symbol = { + scope + .introduce( + "always".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + // \{} -> value + let const_closure = { + let arguments = vec![( + var_store.fresh(), + Located::at_zero(empty_record_pattern(var_store)), + )]; + + let body = Expr::Var(value_symbol); + + Expr::Closure { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: inner_closure_symbol, + captured_symbols: vec![(value_symbol, var_store.fresh())], + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(body)), + } + }; + + // \value -> @Effect \{} -> value + let (function_var, always_closure) = { + // `@Effect \{} -> value` + let body = Expr::Tag { + variant_var: var_store.fresh(), + ext_var: var_store.fresh(), + name: effect_tag_name.clone(), + arguments: vec![(var_store.fresh(), Located::at_zero(const_closure))], + }; + + let arguments = vec![( + var_store.fresh(), + Located::at_zero(Pattern::Identifier(value_symbol)), + )]; + + let function_var = var_store.fresh(); + let closure = Expr::Closure { + function_type: function_var, + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: always_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(body)), + }; + + (function_var, closure) + }; + + use roc_can::annotation::IntroducedVariables; + + let mut introduced_variables = IntroducedVariables::default(); + + let signature = { + // Effect.always : a -> Effect a + let var_a = var_store.fresh(); + + introduced_variables.insert_named("a".into(), var_a); + + let effect_a = { + let actual = + build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store); + + Type::Alias( + effect_symbol, + vec![("a".into(), Type::Variable(var_a))], + Box::new(actual), + ) + }; + + let closure_var = var_store.fresh(); + introduced_variables.insert_wildcard(closure_var); + + Type::Function( + vec![Type::Variable(var_a)], + Box::new(Type::Variable(closure_var)), + Box::new(effect_a), + ) + }; + + let def_annotation = roc_can::def::Annotation { + signature, + introduced_variables, + aliases: SendMap::default(), + region: Region::zero(), + }; + + let pattern = Pattern::Identifier(always_symbol); + let mut pattern_vars = SendMap::default(); + pattern_vars.insert(always_symbol, function_var); + let def = roc_can::def::Def { + loc_pattern: Located::at_zero(pattern), + loc_expr: Located::at_zero(always_closure), + expr_var: function_var, + pattern_vars, + annotation: Some(def_annotation), + }; + + (always_symbol, def) +} + +fn build_effect_map( + env: &mut roc_can::env::Env, + scope: &mut roc_can::scope::Scope, + effect_symbol: Symbol, + effect_tag_name: TagName, + var_store: &mut VarStore, +) -> (Symbol, roc_can::def::Def) { + use roc_can::expr::Expr; + use roc_can::expr::Recursive; + use roc_module::operator::CalledVia; + + // Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {}) + + let thunk_symbol = { + scope + .introduce( + "effect_map_thunk".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let mapper_symbol = { + scope + .introduce( + "effect_map_mapper".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let map_symbol = { + scope + .introduce( + "map".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + // `thunk {}` + let force_thunk_call = { + let boxed = ( + var_store.fresh(), + Located::at_zero(Expr::Var(thunk_symbol)), + var_store.fresh(), + var_store.fresh(), + ); + + let arguments = vec![(var_store.fresh(), Located::at_zero(Expr::EmptyRecord))]; + Expr::Call(Box::new(boxed), arguments, CalledVia::Space) + }; + + // `toEffect (thunk {})` + let mapper_call = { + let boxed = ( + var_store.fresh(), + Located::at_zero(Expr::Var(mapper_symbol)), + var_store.fresh(), + var_store.fresh(), + ); + + let arguments = vec![(var_store.fresh(), Located::at_zero(force_thunk_call))]; + Expr::Call(Box::new(boxed), arguments, CalledVia::Space) + }; + + let inner_closure_symbol = { + scope + .introduce( + "effect_map_inner".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + // \{} -> mapper (thunk {}) + let inner_closure = { + let arguments = vec![( + var_store.fresh(), + Located::at_zero(empty_record_pattern(var_store)), + )]; + + Expr::Closure { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: inner_closure_symbol, + captured_symbols: vec![ + (thunk_symbol, var_store.fresh()), + (mapper_symbol, var_store.fresh()), + ], + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(mapper_call)), + } + }; + + let arguments = vec![ + ( + var_store.fresh(), + Located::at_zero(Pattern::AppliedTag { + whole_var: var_store.fresh(), + ext_var: var_store.fresh(), + tag_name: effect_tag_name.clone(), + arguments: vec![( + var_store.fresh(), + Located::at_zero(Pattern::Identifier(thunk_symbol)), + )], + }), + ), + ( + var_store.fresh(), + Located::at_zero(Pattern::Identifier(mapper_symbol)), + ), + ]; + + // `@Effect \{} -> (mapper (thunk {}))` + let body = Expr::Tag { + variant_var: var_store.fresh(), + ext_var: var_store.fresh(), + name: effect_tag_name.clone(), + arguments: vec![(var_store.fresh(), Located::at_zero(inner_closure))], + }; + + let function_var = var_store.fresh(); + let map_closure = Expr::Closure { + function_type: function_var, + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: map_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(body)), + }; + + use roc_can::annotation::IntroducedVariables; + + let mut introduced_variables = IntroducedVariables::default(); + + let signature = { + // Effect.map : Effect a, (a -> b) -> Effect b + let var_a = var_store.fresh(); + let var_b = var_store.fresh(); + + introduced_variables.insert_named("a".into(), var_a); + introduced_variables.insert_named("b".into(), var_b); + + let effect_a = { + let actual = + build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store); + + Type::Alias( + effect_symbol, + vec![("a".into(), Type::Variable(var_a))], + Box::new(actual), + ) + }; + + let effect_b = { + let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store); + + Type::Alias( + effect_symbol, + vec![("b".into(), Type::Variable(var_b))], + Box::new(actual), + ) + }; + + let closure_var = var_store.fresh(); + introduced_variables.insert_wildcard(closure_var); + let a_to_b = { + Type::Function( + vec![Type::Variable(var_a)], + Box::new(Type::Variable(closure_var)), + Box::new(Type::Variable(var_b)), + ) + }; + + let closure_var = var_store.fresh(); + introduced_variables.insert_wildcard(closure_var); + Type::Function( + vec![effect_a, a_to_b], + Box::new(Type::Variable(closure_var)), + Box::new(effect_b), + ) + }; + + let def_annotation = roc_can::def::Annotation { + signature, + introduced_variables, + aliases: SendMap::default(), + region: Region::zero(), + }; + + let pattern = Pattern::Identifier(map_symbol); + let mut pattern_vars = SendMap::default(); + pattern_vars.insert(map_symbol, function_var); + let def = roc_can::def::Def { + loc_pattern: Located::at_zero(pattern), + loc_expr: Located::at_zero(map_closure), + expr_var: function_var, + pattern_vars, + annotation: Some(def_annotation), + }; + + (map_symbol, def) +} + +fn build_effect_after( + env: &mut roc_can::env::Env, + scope: &mut roc_can::scope::Scope, + effect_symbol: Symbol, + effect_tag_name: TagName, + var_store: &mut VarStore, +) -> (Symbol, roc_can::def::Def) { + use roc_can::expr::Expr; + use roc_can::expr::Recursive; + use roc_module::operator::CalledVia; + + // Effect.after = \@Effect effect, toEffect -> toEffect (effect {}) + + let thunk_symbol = { + scope + .introduce( + "effect_after_thunk".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let to_effect_symbol = { + scope + .introduce( + "effect_after_toEffect".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let after_symbol = { + scope + .introduce( + "after".into(), + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + // `thunk {}` + let force_thunk_call = { + let boxed = ( + var_store.fresh(), + Located::at_zero(Expr::Var(thunk_symbol)), + var_store.fresh(), + var_store.fresh(), + ); + + let arguments = vec![(var_store.fresh(), Located::at_zero(Expr::EmptyRecord))]; + Expr::Call(Box::new(boxed), arguments, CalledVia::Space) + }; + + // `toEffect (thunk {})` + let to_effect_call = { + let boxed = ( + var_store.fresh(), + Located::at_zero(Expr::Var(to_effect_symbol)), + var_store.fresh(), + var_store.fresh(), + ); + + let arguments = vec![(var_store.fresh(), Located::at_zero(force_thunk_call))]; + Expr::Call(Box::new(boxed), arguments, CalledVia::Space) + }; + + let arguments = vec![ + ( + var_store.fresh(), + Located::at_zero(Pattern::AppliedTag { + whole_var: var_store.fresh(), + ext_var: var_store.fresh(), + tag_name: effect_tag_name.clone(), + arguments: vec![( + var_store.fresh(), + Located::at_zero(Pattern::Identifier(thunk_symbol)), + )], + }), + ), + ( + var_store.fresh(), + Located::at_zero(Pattern::Identifier(to_effect_symbol)), + ), + ]; + + let function_var = var_store.fresh(); + let after_closure = Expr::Closure { + function_type: function_var, + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: after_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(to_effect_call)), + }; + + use roc_can::annotation::IntroducedVariables; + + let mut introduced_variables = IntroducedVariables::default(); + + let signature = { + let var_a = var_store.fresh(); + let var_b = var_store.fresh(); + + introduced_variables.insert_named("a".into(), var_a); + introduced_variables.insert_named("b".into(), var_b); + + let effect_a = { + let actual = + build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store); + + Type::Alias( + effect_symbol, + vec![("a".into(), Type::Variable(var_a))], + Box::new(actual), + ) + }; + + let effect_b = { + let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store); + + Type::Alias( + effect_symbol, + vec![("b".into(), Type::Variable(var_b))], + Box::new(actual), + ) + }; + + let closure_var = var_store.fresh(); + introduced_variables.insert_wildcard(closure_var); + let a_to_effect_b = Type::Function( + vec![Type::Variable(var_a)], + Box::new(Type::Variable(closure_var)), + Box::new(effect_b.clone()), + ); + + let closure_var = var_store.fresh(); + introduced_variables.insert_wildcard(closure_var); + Type::Function( + vec![effect_a, a_to_effect_b], + Box::new(Type::Variable(closure_var)), + Box::new(effect_b), + ) + }; + + let def_annotation = roc_can::def::Annotation { + signature, + introduced_variables, + aliases: SendMap::default(), + region: Region::zero(), + }; + + let pattern = Pattern::Identifier(after_symbol); + let mut pattern_vars = SendMap::default(); + pattern_vars.insert(after_symbol, function_var); + let def = roc_can::def::Def { + loc_pattern: Located::at_zero(pattern), + loc_expr: Located::at_zero(after_closure), + expr_var: function_var, + pattern_vars, + annotation: Some(def_annotation), + }; + + (after_symbol, def) +} + +pub fn build_host_exposed_def( + env: &mut roc_can::env::Env, + scope: &mut roc_can::scope::Scope, + symbol: Symbol, + ident: &str, + effect_tag_name: TagName, + var_store: &mut VarStore, + annotation: roc_can::annotation::Annotation, +) -> roc_can::def::Def { + let expr_var = var_store.fresh(); + let pattern = Pattern::Identifier(symbol); + let mut pattern_vars = SendMap::default(); + pattern_vars.insert(symbol, expr_var); + + use roc_can::expr::Expr; + use roc_can::expr::Recursive; + + let mut arguments: Vec<(Variable, Located)> = Vec::new(); + let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new(); + let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new(); + + let def_body = { + match annotation.typ.shallow_dealias() { + Type::Function(args, _, _) => { + for i in 0..args.len() { + let name = format!("closure_arg_{}_{}", ident, i); + + let arg_symbol = { + let ident = name.clone().into(); + scope + .introduce( + ident, + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let arg_var = var_store.fresh(); + + arguments.push((arg_var, Located::at_zero(Pattern::Identifier(arg_symbol)))); + + captured_symbols.push((arg_symbol, arg_var)); + linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol))); + } + + let foreign_symbol_name = format!("roc_fx_{}", ident); + let low_level_call = Expr::ForeignCall { + foreign_symbol: foreign_symbol_name.into(), + args: linked_symbol_arguments, + ret_var: var_store.fresh(), + }; + + let effect_closure_symbol = { + let name = format!("effect_closure_{}", ident); + + let ident = name.into(); + scope + .introduce( + ident, + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let effect_closure = Expr::Closure { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: effect_closure_symbol, + captured_symbols, + recursive: Recursive::NotRecursive, + arguments: vec![( + var_store.fresh(), + Located::at_zero(empty_record_pattern(var_store)), + )], + loc_body: Box::new(Located::at_zero(low_level_call)), + }; + + let body = Expr::Tag { + variant_var: var_store.fresh(), + ext_var: var_store.fresh(), + name: effect_tag_name, + arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))], + }; + + Expr::Closure { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: symbol, + captured_symbols: std::vec::Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Located::at_zero(body)), + } + } + _ => { + // not a function + + let foreign_symbol_name = format!("roc_fx_{}", ident); + let low_level_call = Expr::ForeignCall { + foreign_symbol: foreign_symbol_name.into(), + args: linked_symbol_arguments, + ret_var: var_store.fresh(), + }; + + let effect_closure_symbol = { + let name = format!("effect_closure_{}", ident); + + let ident = name.into(); + scope + .introduce( + ident, + &env.exposed_ident_ids, + &mut env.ident_ids, + Region::zero(), + ) + .unwrap() + }; + + let empty_record_pattern = Pattern::RecordDestructure { + whole_var: var_store.fresh(), + ext_var: var_store.fresh(), + destructs: vec![], + }; + + let effect_closure = Expr::Closure { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + closure_ext_var: var_store.fresh(), + return_type: var_store.fresh(), + name: effect_closure_symbol, + captured_symbols, + recursive: Recursive::NotRecursive, + arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))], + loc_body: Box::new(Located::at_zero(low_level_call)), + }; + + Expr::Tag { + variant_var: var_store.fresh(), + ext_var: var_store.fresh(), + name: effect_tag_name, + arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))], + } + } + } + }; + + let def_annotation = roc_can::def::Annotation { + signature: annotation.typ, + introduced_variables: annotation.introduced_variables, + aliases: annotation.aliases, + region: Region::zero(), + }; + + roc_can::def::Def { + loc_pattern: Located::at_zero(pattern), + loc_expr: Located::at_zero(def_body), + expr_var, + pattern_vars, + annotation: Some(def_annotation), + } +} + +pub fn build_effect_actual( + effect_tag_name: TagName, + a_type: Type, + var_store: &mut VarStore, +) -> Type { + let closure_var = var_store.fresh(); + + Type::TagUnion( + vec![( + effect_tag_name, + vec![Type::Function( + vec![Type::EmptyRec], + Box::new(Type::Variable(closure_var)), + Box::new(a_type), + )], + )], + Box::new(Type::EmptyTagUnion), + ) +} + +#[inline(always)] +fn empty_record_pattern(var_store: &mut VarStore) -> Pattern { + Pattern::RecordDestructure { + whole_var: var_store.fresh(), + ext_var: var_store.fresh(), + destructs: vec![], + } +} diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index caefce7b3c..88b68301dd 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -8,7 +8,7 @@ use roc_builtins::std::{Mode, StdLib}; use roc_can::constraint::Constraint; use roc_can::def::Declaration; use roc_can::module::{canonicalize_module_defs, Module}; -use roc_collections::all::{default_hasher, MutMap, MutSet, SendMap}; +use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_constrain::module::{ constrain_imports, pre_constrain_imports, ConstrainableImports, Import, }; @@ -2253,399 +2253,6 @@ fn run_solve<'a>( } } -fn fabricate_effect_after( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, - effect_symbol: Symbol, - effect_tag_name: TagName, - var_store: &mut VarStore, -) -> (Symbol, roc_can::def::Def) { - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - use roc_module::operator::CalledVia; - - let thunk_symbol = { - scope - .introduce( - "thunk".into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - let to_effect_symbol = { - scope - .introduce( - "toEffect".into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - let after_symbol = { - scope - .introduce( - "after".into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - // `thunk {}` - let force_thunk_call = { - let boxed = ( - var_store.fresh(), - Located::at_zero(Expr::Var(thunk_symbol)), - var_store.fresh(), - var_store.fresh(), - ); - - let arguments = vec![(var_store.fresh(), Located::at_zero(Expr::EmptyRecord))]; - Expr::Call(Box::new(boxed), arguments, CalledVia::Space) - }; - - // `toEffect (thunk {})` - let to_effect_call = { - let boxed = ( - var_store.fresh(), - Located::at_zero(Expr::Var(to_effect_symbol)), - var_store.fresh(), - var_store.fresh(), - ); - - let arguments = vec![(var_store.fresh(), Located::at_zero(force_thunk_call))]; - Expr::Call(Box::new(boxed), arguments, CalledVia::Space) - }; - - use roc_can::pattern::Pattern; - let arguments = vec![ - ( - var_store.fresh(), - Located::at_zero(Pattern::AppliedTag { - whole_var: var_store.fresh(), - ext_var: var_store.fresh(), - tag_name: effect_tag_name.clone(), - arguments: vec![( - var_store.fresh(), - Located::at_zero(Pattern::Identifier(thunk_symbol)), - )], - }), - ), - ( - var_store.fresh(), - Located::at_zero(Pattern::Identifier(to_effect_symbol)), - ), - ]; - - let function_var = var_store.fresh(); - let after_closure = Expr::Closure { - function_type: function_var, - closure_type: var_store.fresh(), - closure_ext_var: var_store.fresh(), - return_type: var_store.fresh(), - name: after_symbol, - captured_symbols: Vec::new(), - recursive: Recursive::NotRecursive, - arguments, - loc_body: Box::new(Located::at_zero(to_effect_call)), - }; - - use roc_can::annotation::IntroducedVariables; - - let mut introduced_variables = IntroducedVariables::default(); - - let signature = { - let var_a = var_store.fresh(); - let var_b = var_store.fresh(); - - introduced_variables.insert_named("a".into(), var_a); - introduced_variables.insert_named("b".into(), var_b); - - let effect_a = { - let actual = - build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store); - - Type::Alias( - effect_symbol, - vec![("a".into(), Type::Variable(var_a))], - Box::new(actual), - ) - }; - - let effect_b = { - let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store); - - Type::Alias( - effect_symbol, - vec![("b".into(), Type::Variable(var_b))], - Box::new(actual), - ) - }; - - let closure_var = var_store.fresh(); - introduced_variables.insert_wildcard(closure_var); - let a_to_effect_b = Type::Function( - vec![Type::Variable(var_a)], - Box::new(Type::Variable(closure_var)), - Box::new(effect_b.clone()), - ); - - let closure_var = var_store.fresh(); - introduced_variables.insert_wildcard(closure_var); - Type::Function( - vec![effect_a, a_to_effect_b], - Box::new(Type::Variable(closure_var)), - Box::new(effect_b), - ) - }; - - let def_annotation = roc_can::def::Annotation { - signature, - introduced_variables, - aliases: SendMap::default(), - region: Region::zero(), - }; - - let pattern = Pattern::Identifier(after_symbol); - let mut pattern_vars = SendMap::default(); - pattern_vars.insert(after_symbol, function_var); - let def = roc_can::def::Def { - loc_pattern: Located::at_zero(pattern), - loc_expr: Located::at_zero(after_closure), - expr_var: function_var, - pattern_vars, - annotation: Some(def_annotation), - }; - - (after_symbol, def) -} - -fn fabricate_host_exposed_def( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, - symbol: Symbol, - ident: &str, - effect_tag_name: TagName, - var_store: &mut VarStore, - annotation: roc_can::annotation::Annotation, -) -> roc_can::def::Def { - use roc_can::pattern::Pattern; - let expr_var = var_store.fresh(); - let pattern = Pattern::Identifier(symbol); - let mut pattern_vars = SendMap::default(); - pattern_vars.insert(symbol, expr_var); - - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - - let mut arguments: Vec<(Variable, Located)> = Vec::new(); - let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new(); - let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new(); - - let def_body = { - match annotation.typ.shallow_dealias() { - Type::Function(args, _, _) => { - for i in 0..args.len() { - let name = format!("closure_arg_{}_{}", ident, i); - - let arg_symbol = { - let ident = name.clone().into(); - scope - .introduce( - ident, - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - let arg_var = var_store.fresh(); - - arguments.push((arg_var, Located::at_zero(Pattern::Identifier(arg_symbol)))); - - captured_symbols.push((arg_symbol, arg_var)); - linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol))); - } - - let foreign_symbol_name = format!("roc_fx_{}", ident); - let low_level_call = Expr::ForeignCall { - foreign_symbol: foreign_symbol_name.into(), - args: linked_symbol_arguments, - ret_var: var_store.fresh(), - }; - - let effect_closure_symbol = { - let name = format!("effect_closure_{}", ident); - - let ident = name.into(); - scope - .introduce( - ident, - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - let empty_record_pattern = Pattern::RecordDestructure { - whole_var: var_store.fresh(), - ext_var: var_store.fresh(), - destructs: vec![], - }; - - let effect_closure = Expr::Closure { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - closure_ext_var: var_store.fresh(), - return_type: var_store.fresh(), - name: effect_closure_symbol, - captured_symbols, - recursive: Recursive::NotRecursive, - arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))], - loc_body: Box::new(Located::at_zero(low_level_call)), - }; - - let body = Expr::Tag { - variant_var: var_store.fresh(), - ext_var: var_store.fresh(), - name: effect_tag_name, - arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))], - }; - - Expr::Closure { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - closure_ext_var: var_store.fresh(), - return_type: var_store.fresh(), - name: symbol, - captured_symbols: std::vec::Vec::new(), - recursive: Recursive::NotRecursive, - arguments, - loc_body: Box::new(Located::at_zero(body)), - } - } - _ => { - // not a function - - let foreign_symbol_name = format!("roc_fx_{}", ident); - let low_level_call = Expr::ForeignCall { - foreign_symbol: foreign_symbol_name.into(), - args: linked_symbol_arguments, - ret_var: var_store.fresh(), - }; - - let effect_closure_symbol = { - let name = format!("effect_closure_{}", ident); - - let ident = name.into(); - scope - .introduce( - ident, - &env.exposed_ident_ids, - &mut env.ident_ids, - Region::zero(), - ) - .unwrap() - }; - - let empty_record_pattern = Pattern::RecordDestructure { - whole_var: var_store.fresh(), - ext_var: var_store.fresh(), - destructs: vec![], - }; - - let effect_closure = Expr::Closure { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - closure_ext_var: var_store.fresh(), - return_type: var_store.fresh(), - name: effect_closure_symbol, - captured_symbols, - recursive: Recursive::NotRecursive, - arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))], - loc_body: Box::new(Located::at_zero(low_level_call)), - }; - Expr::Tag { - variant_var: var_store.fresh(), - ext_var: var_store.fresh(), - name: effect_tag_name, - arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))], - } - } - } - }; - - let def_annotation = roc_can::def::Annotation { - signature: annotation.typ, - introduced_variables: annotation.introduced_variables, - aliases: annotation.aliases, - region: Region::zero(), - }; - - roc_can::def::Def { - loc_pattern: Located::at_zero(pattern), - loc_expr: Located::at_zero(def_body), - expr_var, - pattern_vars, - annotation: Some(def_annotation), - } -} - -fn build_effect_actual(effect_tag_name: TagName, a_type: Type, var_store: &mut VarStore) -> Type { - let closure_var = var_store.fresh(); - - Type::TagUnion( - vec![( - effect_tag_name, - vec![Type::Function( - vec![Type::EmptyRec], - Box::new(Type::Variable(closure_var)), - Box::new(a_type), - )], - )], - Box::new(Type::EmptyTagUnion), - ) -} - -fn unpack_exposes_entries<'a>( - arena: &'a Bump, - entries: &'a [Located>], -) -> bumpalo::collections::Vec<'a, (&'a Located<&'a str>, &'a Located>)> { - use bumpalo::collections::Vec; - - let mut stack: Vec<&TypedIdent> = Vec::with_capacity_in(entries.len(), arena); - let mut output = Vec::with_capacity_in(entries.len(), arena); - - for entry in entries.iter() { - stack.push(&entry.value); - } - - while let Some(effects_entry) = stack.pop() { - match effects_entry { - TypedIdent::Entry { - ident, - spaces_before_colon: _, - ann, - } => { - output.push((ident, ann)); - } - TypedIdent::SpaceAfter(nested, _) | TypedIdent::SpaceBefore(nested, _) => { - stack.push(nested); - } - } - } - - output -} - fn fabricate_effects_module<'a>( arena: &'a Bump, module_ids: Arc>, @@ -2664,7 +2271,7 @@ fn fabricate_effects_module<'a>( let name = effects.type_name; let declared_name: ModuleName = name.into(); - let hardcoded_exposed_functions = vec![name, "after"]; + let hardcoded_exposed_functions = vec![name, "after", "map", "always"]; let exposed_ident_ids = { // Lock just long enough to perform the minimal operations necessary. @@ -2746,7 +2353,7 @@ fn fabricate_effects_module<'a>( let alias = { let a_var = var_store.fresh(); - let actual = build_effect_actual( + let actual = crate::effect_module::build_effect_actual( effect_tag_name.clone(), Type::Variable(a_var), &mut var_store, @@ -2790,7 +2397,7 @@ fn fabricate_effects_module<'a>( &mut var_store, ); - let def = fabricate_host_exposed_def( + let def = crate::effect_module::build_host_exposed_def( &mut can_env, &mut scope, symbol, @@ -2806,15 +2413,15 @@ fn fabricate_effects_module<'a>( } } - let (effect_after_symbol, def) = fabricate_effect_after( + // define Effect.after, Effect.map etc. + crate::effect_module::build_effect_builtins( &mut can_env, &mut scope, effect_symbol, - effect_tag_name, &mut var_store, + &mut exposed_vars_by_symbol, + &mut declarations, ); - exposed_vars_by_symbol.push((effect_after_symbol, def.expr_var)); - declarations.push(Declaration::Declare(def)); exposed_vars_by_symbol }; @@ -2872,6 +2479,37 @@ fn fabricate_effects_module<'a>( )) } +fn unpack_exposes_entries<'a>( + arena: &'a Bump, + entries: &'a [Located>], +) -> bumpalo::collections::Vec<'a, (&'a Located<&'a str>, &'a Located>)> { + use bumpalo::collections::Vec; + + let mut stack: Vec<&TypedIdent> = Vec::with_capacity_in(entries.len(), arena); + let mut output = Vec::with_capacity_in(entries.len(), arena); + + for entry in entries.iter() { + stack.push(&entry.value); + } + + while let Some(effects_entry) = stack.pop() { + match effects_entry { + TypedIdent::Entry { + ident, + spaces_before_colon: _, + ann, + } => { + output.push((ident, ann)); + } + TypedIdent::SpaceAfter(nested, _) | TypedIdent::SpaceBefore(nested, _) => { + stack.push(nested); + } + } + } + + output +} + fn canonicalize_and_constrain<'a>( arena: &'a Bump, module_ids: &ModuleIds, diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index 7c7110693b..ecfaf3ae0f 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -2,6 +2,8 @@ app Main provides [ main ] imports [ Effect ] main : Effect.Effect {} as Fx main = - Effect.putLine "Write a thing!" + Effect.always "Write a thing" + |> Effect.map (\line -> Str.concat line "!") + |> Effect.after (\line -> Effect.putLine line) |> Effect.after (\{} -> Effect.getLine) |> Effect.after (\line -> Effect.putLine line) From f08f3e7510bf0f6eb3f3e2a90ca2bc90ac8c4446 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 19:53:03 +0100 Subject: [PATCH 3/6] cleanup --- compiler/load/src/effect_module.rs | 122 ++++++++++++++--------------- compiler/load/src/file.rs | 12 ++- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/compiler/load/src/effect_module.rs b/compiler/load/src/effect_module.rs index 12a2e735bb..8715bab75d 100644 --- a/compiler/load/src/effect_module.rs +++ b/compiler/load/src/effect_module.rs @@ -1,51 +1,59 @@ -use roc_can::def::Declaration; +use roc_can::def::{Declaration, Def}; +use roc_can::env::Env; +use roc_can::expr::{Expr, Recursive}; use roc_can::pattern::Pattern; +use roc_can::scope::Scope; use roc_collections::all::SendMap; use roc_module::ident::TagName; +use roc_module::operator::CalledVia; use roc_module::symbol::Symbol; use roc_region::all::{Located, Region}; use roc_types::subs::{VarStore, Variable}; use roc_types::types::Type; +/// Functions that are always implemented for Effect +pub const BUILTIN_EFFECT_FUNCTIONS: [( + &str, + for<'r, 's, 't0, 't1> fn( + &'r mut Env<'s>, + &'t0 mut Scope, + Symbol, + TagName, + &'t1 mut VarStore, + ) -> (Symbol, Def), +); 3] = [ + // Effect.after : Effect a, (a -> Effect b) -> Effect b + ("after", build_effect_after), + // Effect.map : Effect a, (a -> b) -> Effect b + ("map", build_effect_map), + // Effect.always : a -> Effect a + ("always", build_effect_always), +]; + +// the Effects alias & associated functions +// +// A platform can define an Effect type in its header. It can have an arbitrary name +// (e.g. Task, IO), but we'll call it an Effect in general. +// +// From that name, we generate an effect module, an effect alias, and some functions. +// +// The effect alias is implemented as +// +// Effect a : [ @Effect ({} -> a) ] +// +// For this alias we implement the functions defined in BUILTIN_EFFECT_FUNCTIONS with the +// standard implementation. + pub fn build_effect_builtins( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, + env: &mut Env, + scope: &mut Scope, effect_symbol: Symbol, var_store: &mut VarStore, exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>, declarations: &mut Vec, ) { - // Effect.after : Effect a, (a -> Effect b) -> Effect b - { - let (symbol, def) = build_effect_after( - env, - scope, - effect_symbol, - TagName::Private(effect_symbol), - var_store, - ); - - exposed_vars_by_symbol.push((symbol, def.expr_var)); - declarations.push(Declaration::Declare(def)); - } - - // Effect.map : Effect a, (a -> b) -> Effect b - { - let (symbol, def) = build_effect_map( - env, - scope, - effect_symbol, - TagName::Private(effect_symbol), - var_store, - ); - - exposed_vars_by_symbol.push((symbol, def.expr_var)); - declarations.push(Declaration::Declare(def)); - } - - // Effect.always : a -> Effect a - { - let (symbol, def) = build_effect_always( + for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() { + let (symbol, def) = f( env, scope, effect_symbol, @@ -59,15 +67,12 @@ pub fn build_effect_builtins( } fn build_effect_always( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, + env: &mut Env, + scope: &mut Scope, effect_symbol: Symbol, effect_tag_name: TagName, var_store: &mut VarStore, -) -> (Symbol, roc_can::def::Def) { - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - +) -> (Symbol, Def) { // Effect.always = \value -> @Effect \{} -> value let value_symbol = { @@ -197,7 +202,7 @@ fn build_effect_always( let pattern = Pattern::Identifier(always_symbol); let mut pattern_vars = SendMap::default(); pattern_vars.insert(always_symbol, function_var); - let def = roc_can::def::Def { + let def = Def { loc_pattern: Located::at_zero(pattern), loc_expr: Located::at_zero(always_closure), expr_var: function_var, @@ -209,16 +214,12 @@ fn build_effect_always( } fn build_effect_map( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, + env: &mut Env, + scope: &mut Scope, effect_symbol: Symbol, effect_tag_name: TagName, var_store: &mut VarStore, -) -> (Symbol, roc_can::def::Def) { - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - use roc_module::operator::CalledVia; - +) -> (Symbol, Def) { // Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {}) let thunk_symbol = { @@ -416,7 +417,7 @@ fn build_effect_map( let pattern = Pattern::Identifier(map_symbol); let mut pattern_vars = SendMap::default(); pattern_vars.insert(map_symbol, function_var); - let def = roc_can::def::Def { + let def = Def { loc_pattern: Located::at_zero(pattern), loc_expr: Located::at_zero(map_closure), expr_var: function_var, @@ -428,16 +429,12 @@ fn build_effect_map( } fn build_effect_after( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, + env: &mut Env, + scope: &mut Scope, effect_symbol: Symbol, effect_tag_name: TagName, var_store: &mut VarStore, -) -> (Symbol, roc_can::def::Def) { - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - use roc_module::operator::CalledVia; - +) -> (Symbol, Def) { // Effect.after = \@Effect effect, toEffect -> toEffect (effect {}) let thunk_symbol = { @@ -590,7 +587,7 @@ fn build_effect_after( let pattern = Pattern::Identifier(after_symbol); let mut pattern_vars = SendMap::default(); pattern_vars.insert(after_symbol, function_var); - let def = roc_can::def::Def { + let def = Def { loc_pattern: Located::at_zero(pattern), loc_expr: Located::at_zero(after_closure), expr_var: function_var, @@ -602,22 +599,19 @@ fn build_effect_after( } pub fn build_host_exposed_def( - env: &mut roc_can::env::Env, - scope: &mut roc_can::scope::Scope, + env: &mut Env, + scope: &mut Scope, symbol: Symbol, ident: &str, effect_tag_name: TagName, var_store: &mut VarStore, annotation: roc_can::annotation::Annotation, -) -> roc_can::def::Def { +) -> Def { let expr_var = var_store.fresh(); let pattern = Pattern::Identifier(symbol); let mut pattern_vars = SendMap::default(); pattern_vars.insert(symbol, expr_var); - use roc_can::expr::Expr; - use roc_can::expr::Recursive; - let mut arguments: Vec<(Variable, Located)> = Vec::new(); let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new(); let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new(); @@ -762,7 +756,7 @@ pub fn build_host_exposed_def( region: Region::zero(), }; - roc_can::def::Def { + Def { loc_pattern: Located::at_zero(pattern), loc_expr: Located::at_zero(def_body), expr_var, diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 88b68301dd..9b4603e805 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2271,7 +2271,15 @@ fn fabricate_effects_module<'a>( let name = effects.type_name; let declared_name: ModuleName = name.into(); - let hardcoded_exposed_functions = vec![name, "after", "map", "always"]; + let hardcoded_effect_symbols = { + let mut functions: Vec<_> = crate::effect_module::BUILTIN_EFFECT_FUNCTIONS + .iter() + .map(|x| x.0) + .collect(); + functions.push(name); + + functions + }; let exposed_ident_ids = { // Lock just long enough to perform the minimal operations necessary. @@ -2307,7 +2315,7 @@ fn fabricate_effects_module<'a>( exposed.push(symbol); } - for hardcoded in hardcoded_exposed_functions { + for hardcoded in hardcoded_effect_symbols { // Use get_or_insert here because the ident_ids may already // created an IdentId for this, when it was imported exposed // in a dependent module. From 82723064c5e0a189813b3abec3f7dc47a6872662 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 20:02:12 +0100 Subject: [PATCH 4/6] clippy --- cli/tests/helpers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/tests/helpers.rs b/cli/tests/helpers.rs index fe40eb3419..31c62a12d5 100644 --- a/cli/tests/helpers.rs +++ b/cli/tests/helpers.rs @@ -70,7 +70,7 @@ pub fn run_cmd(cmd_name: &str, args: &[&str]) -> Out { let output = cmd .output() - .expect(&format!("failed to execute cmd `{}` in CLI test", cmd_name)); + .unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name)); Out { stdout: String::from_utf8(output.stdout).unwrap(), @@ -263,12 +263,12 @@ pub fn repl_eval(input: &str) -> Out { // Evaluate the expression stdin - .write_all("\n".as_bytes()) + .write_all(b"\n") .expect("Failed to write newline to stdin"); // Gracefully exit the repl stdin - .write_all(":exit\n".as_bytes()) + .write_all(b":exit\n") .expect("Failed to write :exit to stdin"); } From 0892451d470799d55dc6ae41991ceb1bc77c9419 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 20:03:10 +0100 Subject: [PATCH 5/6] more clippy --- compiler/load/src/effect_module.rs | 22 ++++++++++------------ compiler/load/src/file.rs | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/load/src/effect_module.rs b/compiler/load/src/effect_module.rs index 8715bab75d..9de9f3b348 100644 --- a/compiler/load/src/effect_module.rs +++ b/compiler/load/src/effect_module.rs @@ -12,16 +12,15 @@ use roc_types::subs::{VarStore, Variable}; use roc_types::types::Type; /// Functions that are always implemented for Effect -pub const BUILTIN_EFFECT_FUNCTIONS: [( - &str, - for<'r, 's, 't0, 't1> fn( - &'r mut Env<'s>, - &'t0 mut Scope, - Symbol, - TagName, - &'t1 mut VarStore, - ) -> (Symbol, Def), -); 3] = [ +type Builder = for<'r, 's, 't0, 't1> fn( + &'r mut Env<'s>, + &'t0 mut Scope, + Symbol, + TagName, + &'t1 mut VarStore, +) -> (Symbol, Def); + +pub const BUILTIN_EFFECT_FUNCTIONS: [(&str, Builder); 3] = [ // Effect.after : Effect a, (a -> Effect b) -> Effect b ("after", build_effect_after), // Effect.map : Effect a, (a -> b) -> Effect b @@ -172,8 +171,7 @@ fn build_effect_always( introduced_variables.insert_named("a".into(), var_a); let effect_a = { - let actual = - build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store); + let actual = build_effect_actual(effect_tag_name, Type::Variable(var_a), var_store); Type::Alias( effect_symbol, diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 9b4603e805..43573c65d6 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2362,7 +2362,7 @@ fn fabricate_effects_module<'a>( let a_var = var_store.fresh(); let actual = crate::effect_module::build_effect_actual( - effect_tag_name.clone(), + effect_tag_name, Type::Variable(a_var), &mut var_store, ); From 7e7fa4b4aac752f7bd96527d2776b06ac80c83e1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 9 Nov 2020 20:11:39 +0100 Subject: [PATCH 6/6] fix tag test --- compiler/gen/tests/gen_tags.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/gen/tests/gen_tags.rs b/compiler/gen/tests/gen_tags.rs index 3c74793292..b46be46962 100644 --- a/compiler/gen/tests/gen_tags.rs +++ b/compiler/gen/tests/gen_tags.rs @@ -88,7 +88,7 @@ mod gen_tags { } #[test] - fn applied_tag_just_unit() { + fn applied_tag_just_enum() { assert_evals_to!( indoc!( r#" @@ -105,7 +105,7 @@ mod gen_tags { "# ), (0, 2), - (i64, i64) + (i64, u8) ); }