From f2fd58e223bca864caa0dc56e452c4fe723e6e63 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 10 May 2022 21:37:08 +0200 Subject: [PATCH] working NQueens check --- bindgen/src/load.rs | 1 + compiler/can/src/def.rs | 103 +++ compiler/can/src/effect_module.rs | 14 +- compiler/can/src/expr.rs | 230 +++++- compiler/can/src/module.rs | 108 ++- compiler/can/src/traverse.rs | 4 + compiler/collections/src/soa.rs | 6 +- compiler/constrain/src/expr.rs | 679 +++++++++++++++--- compiler/constrain/src/module.rs | 3 +- compiler/load_internal/src/file.rs | 23 +- .../hello-world/zig-platform/helloZig.roc | 5 +- 11 files changed, 982 insertions(+), 194 deletions(-) diff --git a/bindgen/src/load.rs b/bindgen/src/load.rs index 8a613ca5e1..0baca5558c 100644 --- a/bindgen/src/load.rs +++ b/bindgen/src/load.rs @@ -68,6 +68,7 @@ pub fn load_types( let mut types = Types::default(); + let decls = vec![]; for decl in decls.into_iter() { let defs = match decl { Declaration::Declare(def) => { diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index f3b4b0569f..8978ac90b8 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -6,6 +6,7 @@ use crate::annotation::OwnedNamedOrAble; use crate::env::Env; use crate::expr::AnnotatedMark; use crate::expr::ClosureData; +use crate::expr::Declarations; use crate::expr::Expr::{self, *}; use crate::expr::{canonicalize_expr, Output, Recursive}; use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern}; @@ -745,6 +746,108 @@ impl DefOrdering { } } +#[inline(always)] +pub(crate) fn sort_can_defs_new( + env: &mut Env<'_>, + defs: CanDefs, + mut output: Output, +) -> (Declarations, Output) { + let CanDefs { + defs, + def_ordering, + aliases, + abilities_in_scope, + } = defs; + + // TODO: inefficient, but I want to make this what CanDefs contains in the future + let mut defs: Vec<_> = defs.into_iter().map(|x| x.unwrap()).collect(); + + let mut declarations = Declarations::with_capacity(defs.len()); + + output.abilities_in_scope = abilities_in_scope; + + for (symbol, alias) in aliases.into_iter() { + output.aliases.insert(symbol, alias); + } + + // We first perform SCC based on any reference, both variable usage and calls + // considering both value definitions and function bodies. This will spot any + // recursive relations between any 2 definitions. + let sccs = def_ordering.references.strongly_connected_components_all(); + + sccs.reorder(&mut defs); + + for group in sccs.groups().rev() { + match group.count_ones() { + 1 => { + // a group with a single Def, nice and simple + let def = defs.pop().unwrap(); + let index = group.first_one().unwrap(); + + if def_ordering.direct_references.get_row_col(index, index) { + // a definition like `x = x + 1`, which is invalid in roc + let symbol = def_ordering.get_symbol(index).unwrap(); + + let entries = vec![make_cycle_entry(symbol, &def)]; + + let problem = Problem::RuntimeError(RuntimeError::CircularDef(entries.clone())); + env.problem(problem); + + // Declaration::InvalidCycle(entries) + todo!("InvalidCycle: {:?}", entries) + } else if def_ordering.references.get_row_col(index, index) { + // this function calls itself, and must be typechecked as a recursive def + match def.loc_pattern.value { + Pattern::Identifier(symbol) => match def.loc_expr.value { + Closure(closure_data) => { + declarations.push_recursive_def( + Loc::at(def.loc_pattern.region, symbol), + Loc::at(def.loc_expr.region, closure_data), + def.expr_var, + def.annotation, + ); + } + _ => todo!(), + }, + _ => todo!(), + } + } else { + // Declaration::Declare(def) + match def.loc_pattern.value { + Pattern::Identifier(symbol) => match def.loc_expr.value { + Closure(closure_data) => { + declarations.push_function_def( + Loc::at(def.loc_pattern.region, symbol), + Loc::at(def.loc_expr.region, closure_data), + def.expr_var, + def.annotation, + ); + } + _ => { + declarations.push_value_def( + Loc::at(def.loc_pattern.region, symbol), + def.loc_expr, + def.expr_var, + def.annotation, + ); + } + }, + _ => todo!(), + } + } + } + group_length => { + let group_defs = defs.split_off(defs.len() - group_length); + + dbg!(&group_defs); + todo!(); + } + } + } + + (declarations, output) +} + #[inline(always)] pub(crate) fn sort_can_defs( env: &mut Env<'_>, diff --git a/compiler/can/src/effect_module.rs b/compiler/can/src/effect_module.rs index 7e055f3126..e2d52b502b 100644 --- a/compiler/can/src/effect_module.rs +++ b/compiler/can/src/effect_module.rs @@ -1,6 +1,6 @@ use crate::annotation::IntroducedVariables; use crate::def::{Declaration, Def}; -use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive}; +use crate::expr::{AnnotatedMark, ClosureData, Declarations, Expr, Recursive}; use crate::pattern::Pattern; use crate::scope::Scope; use roc_collections::{SendMap, VecSet}; @@ -38,7 +38,7 @@ pub(crate) fn build_effect_builtins( effect_symbol: Symbol, var_store: &mut VarStore, exposed_symbols: &mut VecSet, - declarations: &mut Vec, + declarations: &mut Declarations, generated_functions: HostedGeneratedFunctions, ) { macro_rules! helper { @@ -54,31 +54,31 @@ pub(crate) fn build_effect_builtins( if generated_functions.after { let def = helper!(build_effect_after); - declarations.push(Declaration::Declare(def)); + declarations.push_def(def); } // Effect.map : Effect a, (a -> b) -> Effect b if generated_functions.map { let def = helper!(build_effect_map); - declarations.push(Declaration::Declare(def)); + declarations.push_def(def); } // Effect.always : a -> Effect a if generated_functions.always { let def = helper!(build_effect_always); - declarations.push(Declaration::Declare(def)); + declarations.push_def(def); } // Effect.forever : Effect a -> Effect b if generated_functions.forever { let def = helper!(build_effect_forever); - declarations.push(Declaration::DeclareRec(vec![def])); + declarations.push_def(def); } // Effect.loop : a, (a -> Effect [ Step a, Done b ]) -> Effect b if generated_functions.loop_ { let def = helper!(build_effect_loop); - declarations.push(Declaration::DeclareRec(vec![def])); + declarations.push_def(def); } // Useful when working on functions in this module. By default symbols that we named do now diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index dd1129322d..5188671bc8 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -1,7 +1,7 @@ use crate::abilities::SpecializationId; use crate::annotation::{freshen_opaque_def, IntroducedVariables}; use crate::builtins::builtin_defs_map; -use crate::def::{can_defs_with_return, Def}; +use crate::def::{can_defs_with_return, Annotation, Def}; use crate::env::Env; use crate::num::{ finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result, @@ -1924,34 +1924,244 @@ pub struct Declarations { pub destructs: Vec, } +impl Default for Declarations { + fn default() -> Self { + Self::new() + } +} + impl Declarations { - pub fn len(&self) -> usize { - let mut accum = 0; - for tag in self.declarations.iter() { - match tag { - DeclarationTag::Function(_) => accum += 1, - DeclarationTag::Value => accum += 1, - DeclarationTag::Destructure(_) => accum += 1, - DeclarationTag::MutualRecursion(slice) => accum += slice.len(), + pub fn new() -> Self { + Self::with_capacity(0) + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + declarations: Vec::with_capacity(capacity), + variables: Vec::with_capacity(capacity), + symbols: Vec::with_capacity(capacity), + annotations: Vec::with_capacity(capacity), + function_bodies: Vec::with_capacity(capacity), + expressions: Vec::with_capacity(capacity), + destructs: Vec::new(), // number of destructs is probably low + } + } + + pub fn push_recursive_def( + &mut self, + symbol: Loc, + loc_closure_data: Loc, + expr_var: Variable, + annotation: Option, + ) -> usize { + let index = self.declarations.len(); + + let function_def = FunctionDef { + closure_type: loc_closure_data.value.closure_type, + closure_ext_var: loc_closure_data.value.closure_ext_var, + return_type: loc_closure_data.value.return_type, + captured_symbols: loc_closure_data.value.captured_symbols, + arguments: loc_closure_data.value.arguments, + }; + + let loc_function_def = Loc::at(loc_closure_data.region, function_def); + + let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def); + + let tag = match loc_closure_data.value.recursive { + Recursive::NotRecursive | Recursive::Recursive => { + DeclarationTag::Recursive(function_def_index) } + Recursive::TailRecursive => DeclarationTag::TailRecursive(function_def_index), + }; + + self.declarations.push(tag); + self.variables.push(expr_var); + self.symbols.push(symbol); + self.annotations.push(annotation); + + self.expressions.push(*loc_closure_data.value.loc_body); + + index + } + + pub fn push_function_def( + &mut self, + symbol: Loc, + loc_closure_data: Loc, + expr_var: Variable, + annotation: Option, + ) -> usize { + let index = self.declarations.len(); + + let function_def = FunctionDef { + closure_type: loc_closure_data.value.closure_type, + closure_ext_var: loc_closure_data.value.closure_ext_var, + return_type: loc_closure_data.value.return_type, + captured_symbols: loc_closure_data.value.captured_symbols, + arguments: loc_closure_data.value.arguments, + }; + + let loc_function_def = Loc::at(loc_closure_data.region, function_def); + + let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def); + + self.declarations + .push(DeclarationTag::Function(function_def_index)); + self.variables.push(expr_var); + self.symbols.push(symbol); + self.annotations.push(annotation); + + self.expressions.push(*loc_closure_data.value.loc_body); + + index + } + + pub fn push_value_def( + &mut self, + symbol: Loc, + loc_expr: Loc, + expr_var: Variable, + annotation: Option, + ) -> usize { + let index = self.declarations.len(); + + self.declarations.push(DeclarationTag::Value); + self.variables.push(expr_var); + self.symbols.push(symbol); + self.annotations.push(annotation); + + self.expressions.push(loc_expr); + + index + } + + pub fn push_def(&mut self, def: Def) { + match def.loc_pattern.value { + Pattern::Identifier(symbol) => match def.loc_expr.value { + Expr::Closure(closure_data) => match closure_data.recursive { + Recursive::NotRecursive => { + self.push_function_def( + Loc::at(def.loc_pattern.region, symbol), + Loc::at(def.loc_expr.region, closure_data), + def.expr_var, + def.annotation, + ); + } + + Recursive::Recursive | Recursive::TailRecursive => { + self.push_recursive_def( + Loc::at(def.loc_pattern.region, symbol), + Loc::at(def.loc_expr.region, closure_data), + def.expr_var, + def.annotation, + ); + } + }, + _ => { + self.push_value_def( + Loc::at(def.loc_pattern.region, symbol), + def.loc_expr, + def.expr_var, + def.annotation, + ); + } + }, + _ => todo!(), + } + } + + pub fn update_builtin_def(&mut self, index: usize, def: Def) { + match def.loc_pattern.value { + Pattern::Identifier(s) => assert_eq!(s, self.symbols[index].value), + _ => panic!(), } - accum + match def.loc_expr.value { + Expr::Closure(closure_data) => { + let function_def = FunctionDef { + closure_type: closure_data.closure_type, + closure_ext_var: closure_data.closure_ext_var, + return_type: closure_data.return_type, + captured_symbols: closure_data.captured_symbols, + arguments: closure_data.arguments, + }; + + let loc_function_def = Loc::at(def.loc_expr.region, function_def); + + let function_def_index = + Index::push_new(&mut self.function_bodies, loc_function_def); + + self.declarations[index] = DeclarationTag::Function(function_def_index); + self.expressions[index] = *closure_data.loc_body; + + // TODO investigate whether this matters, and if we can be more efficient here + self.variables[index] = def.expr_var; + } + _ => { + self.declarations[index] = DeclarationTag::Value; + self.expressions[index] = def.loc_expr; + + // TODO investigate whether this matters, and if we can be more efficient here + self.variables[index] = def.expr_var; + } + } + } + + pub fn len(&self) -> usize { + self.declarations.iter().map(|d| d.len()).sum() } pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn iter_top_down(&self) -> impl Iterator + '_ { + self.declarations.iter().scan(0, |state, e| { + let length_so_far = *state; + + *state += e.len(); + + Some((length_so_far, *e)) + }) + } + + pub fn iter_bottom_up(&self) -> impl Iterator + '_ { + self.declarations + .iter() + .rev() + .scan(self.declarations.len() - 1, |state, e| { + let length_so_far = *state; + + *state = length_so_far.saturating_sub(e.len()); + + Some((length_so_far, *e)) + }) + } } #[derive(Clone, Copy, Debug)] pub enum DeclarationTag { Value, Function(Index>), + Recursive(Index>), + TailRecursive(Index>), Destructure(Index), MutualRecursion(Slice), } +impl DeclarationTag { + fn len(self) -> usize { + match self { + DeclarationTag::Function(_) => 1, + DeclarationTag::Recursive(_) => 1, + DeclarationTag::TailRecursive(_) => 1, + DeclarationTag::Value => 1, + DeclarationTag::Destructure(_) => 1, + DeclarationTag::MutualRecursion(slice) => slice.len(), + } + } +} + #[derive(Clone, Debug)] pub struct FunctionDef { pub closure_type: Variable, diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 16d89916d4..3469023f03 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -1,9 +1,9 @@ use crate::abilities::AbilitiesStore; use crate::annotation::canonicalize_annotation; -use crate::def::{canonicalize_defs, sort_can_defs, Declaration, Def}; +use crate::def::{canonicalize_defs, Def}; use crate::effect_module::HostedGeneratedFunctions; use crate::env::Env; -use crate::expr::{ClosureData, Expr, Output}; +use crate::expr::{ClosureData, Declarations, Expr, Output}; use crate::operator::desugar_def; use crate::pattern::Pattern; use crate::scope::Scope; @@ -44,7 +44,7 @@ pub struct RigidVariables { pub struct ModuleOutput { pub aliases: MutMap, pub rigid_variables: RigidVariables, - pub declarations: Vec, + pub declarations: Declarations, pub exposed_imports: MutMap, pub lookups: Vec<(Symbol, Variable, Region)>, pub problems: Vec, @@ -351,7 +351,7 @@ pub fn canonicalize_module_defs<'a>( ..Default::default() }; - let (mut declarations, mut output) = sort_can_defs(&mut env, defs, new_output); + let (mut declarations, mut output) = crate::def::sort_can_defs_new(&mut env, defs, new_output); let symbols_from_requires = symbols_from_requires .iter() @@ -399,40 +399,39 @@ pub fn canonicalize_module_defs<'a>( ); } - use crate::def::Declaration::*; - for decl in declarations.iter_mut() { - match decl { - Declare(def) => { - for (symbol, _) in def.pattern_vars.iter() { - if exposed_but_not_defined.contains(symbol) { - // Remove this from exposed_symbols, - // so that at the end of the process, - // we can see if there were any - // exposed symbols which did not have - // corresponding defs. - exposed_but_not_defined.remove(symbol); - } - } + let mut index = 0; + while index < declarations.len() { + use crate::expr::DeclarationTag::*; + + let tag = declarations.declarations[index]; + + match tag { + Value => { + let symbol = &declarations.symbols[index].value; + + // Remove this from exposed_symbols, + // so that at the end of the process, + // we can see if there were any + // exposed symbols which did not have + // corresponding defs. + exposed_but_not_defined.remove(symbol); // Temporary hack: we don't know exactly what symbols are hosted symbols, // and which are meant to be normal definitions without a body. So for now // we just assume they are hosted functions (meant to be provided by the platform) - if has_no_implementation(&def.loc_expr.value) { + if has_no_implementation(&declarations.expressions[index].value) { match generated_info { GeneratedInfo::Builtin => { - let symbol = def.pattern_vars.iter().next().unwrap().0; match crate::builtins::builtin_defs_map(*symbol, var_store) { None => { panic!("A builtin module contains a signature without implementation for {:?}", symbol) } - Some(mut replacement_def) => { - replacement_def.annotation = def.annotation.take(); - *def = replacement_def; + Some(replacement_def) => { + declarations.update_builtin_def(index, replacement_def); } } } GeneratedInfo::Hosted { effect_symbol, .. } => { - let symbol = def.pattern_vars.iter().next().unwrap().0; let ident_id = symbol.ident_id(); let ident = scope .locals @@ -440,7 +439,9 @@ pub fn canonicalize_module_defs<'a>( .get_name(ident_id) .unwrap() .to_string(); - let def_annotation = def.annotation.clone().unwrap(); + + let def_annotation = declarations.annotations[index].clone().unwrap(); + let annotation = crate::annotation::Annotation { typ: def_annotation.signature, introduced_variables: def_annotation.introduced_variables, @@ -457,38 +458,28 @@ pub fn canonicalize_module_defs<'a>( annotation, ); - *def = hosted_def; + declarations.update_builtin_def(index, hosted_def); } _ => (), } } - } - DeclareRec(defs) => { - for def in defs { - for (symbol, _) in def.pattern_vars.iter() { - if exposed_but_not_defined.contains(symbol) { - // Remove this from exposed_symbols, - // so that at the end of the process, - // we can see if there were any - // exposed symbols which did not have - // corresponding defs. - exposed_but_not_defined.remove(symbol); - } - } - } - } - InvalidCycle(entries) => { - env.problems.push(Problem::BadRecursion(entries.to_vec())); + index += 1; } - Builtin(def) => { - // Builtins cannot be exposed in module declarations. - // This should never happen! - debug_assert!(def - .pattern_vars - .iter() - .all(|(symbol, _)| !exposed_but_not_defined.contains(symbol))); + Function(_) | Recursive(_) | TailRecursive(_) => { + let symbol = &declarations.symbols[index].value; + + // Remove this from exposed_symbols, + // so that at the end of the process, + // we can see if there were any + // exposed symbols which did not have + // corresponding defs. + exposed_but_not_defined.remove(symbol); + + index += 1; } + Destructure(_) => todo!(), + MutualRecursion(_) => todo!(), } } @@ -542,7 +533,7 @@ pub fn canonicalize_module_defs<'a>( annotation: None, }; - declarations.push(Declaration::Declare(def)); + declarations.push_def(def); } // Incorporate any remaining output.lookups entries into references. @@ -556,13 +547,14 @@ pub fn canonicalize_module_defs<'a>( referenced_values.extend(env.qualified_value_lookups.iter().copied()); referenced_types.extend(env.qualified_type_lookups.iter().copied()); - for declaration in declarations.iter_mut() { - match declaration { - Declare(def) => fix_values_captured_in_closure_def(def, &mut VecSet::default()), - DeclareRec(defs) => fix_values_captured_in_closure_defs(defs, &mut VecSet::default()), - InvalidCycle(_) | Builtin(_) => {} - } - } + // TODO + // for declaration in declarations.iter_mut() { + // match declaration { + // Declare(def) => fix_values_captured_in_closure_def(def, &mut VecSet::default()), + // DeclareRec(defs) => fix_values_captured_in_closure_defs(defs, &mut VecSet::default()), + // InvalidCycle(_) | Builtin(_) => {} + // } + // } ModuleOutput { scope, diff --git a/compiler/can/src/traverse.rs b/compiler/can/src/traverse.rs index 7a23cdf20f..881e73cd26 100644 --- a/compiler/can/src/traverse.rs +++ b/compiler/can/src/traverse.rs @@ -99,6 +99,10 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { visitor.visit_def(def); visitor.visit_expr(&body.value, body.region, var); } + Expr::LetBlock(declarations, body) => { + todo!("visit {:?}", declarations); + visitor.visit_expr(&body.value, body.region, var); + } Expr::Call(f, args, _called_via) => { let (fn_var, loc_fn, _closure_var, _ret_var) = &**f; walk_call(visitor, *fn_var, loc_fn, args); diff --git a/compiler/collections/src/soa.rs b/compiler/collections/src/soa.rs index 69e6abb830..1ff84f9b73 100644 --- a/compiler/collections/src/soa.rs +++ b/compiler/collections/src/soa.rs @@ -31,7 +31,7 @@ impl Index { } } - pub const fn index(&self) -> usize { + pub const fn index(self) -> usize { self.index as usize } @@ -42,6 +42,10 @@ impl Index { index } + + pub const fn as_slice(self) -> Slice { + Slice::new(self.index, 1) + } } #[derive(PartialEq, Eq)] diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index e683f69fb6..8dbca0f06f 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -9,9 +9,12 @@ use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, Exhausti use roc_can::expected::Expected::{self, *}; use roc_can::expected::PExpected; use roc_can::expr::Expr::{self, *}; -use roc_can::expr::{AccessorData, AnnotatedMark, ClosureData, Field, WhenBranch}; +use roc_can::expr::{ + AccessorData, AnnotatedMark, ClosureData, Declarations, Field, FunctionDef, WhenBranch, +}; use roc_can::pattern::Pattern; use roc_collections::all::{HumanIndex, MutMap, SendMap}; +use roc_collections::soa::{Index, Slice}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; @@ -87,7 +90,7 @@ fn constrain_untyped_args( } #[allow(clippy::too_many_arguments)] -fn constrain_closure( +fn constrain_untyped_closure( constraints: &mut Constraints, env: &Env, region: Region, @@ -432,7 +435,7 @@ pub fn constrain_expr( .. }) => { // shared code with function defs without an annotation - constrain_closure( + constrain_untyped_closure( constraints, env, region, @@ -936,98 +939,8 @@ pub fn constrain_expr( loop { match declarations.declarations[index] { roc_can::expr::DeclarationTag::Value => { - let loc_expr = &declarations.expressions[index]; - let loc_symbol = declarations.symbols[index]; - let expr_var = declarations.variables[index]; - let opt_annotation = &declarations.annotations[index]; - - match opt_annotation { - Some(annotation) => { - let arity = 1; - let rigids = &env.rigids; - let mut ftv = rigids.clone(); - - let InstantiateRigids { - signature, - new_rigid_variables, - new_infer_variables, - } = instantiate_rigids_simple( - &annotation.signature, - &annotation.introduced_variables, - &mut ftv, - ); - - let loc_pattern = Loc::at( - loc_symbol.region, - Pattern::Identifier(loc_symbol.value), - ); - - let annotation_expected = FromAnnotation( - loc_pattern, - arity, - AnnotationSource::TypedBody { - region: annotation.region, - }, - signature.clone(), - ); - - // TODO missing equality of annotation_expected with expr_var? - // but the signature is stored into the expr_var below?! - - let ret_constraint = constrain_expr( - constraints, - env, - loc_expr.region, - &loc_expr.value, - annotation_expected, - ); - - let cons = [ - ret_constraint, - // Store type into AST vars. We use Store so errors aren't reported twice - constraints.store( - signature.clone(), - expr_var, - std::file!(), - std::line!(), - ), - ]; - let expr_con = constraints.and_constraint(cons); - - body_con = constrain_def_make_constraint_simple( - constraints, - new_rigid_variables, - new_infer_variables, - expr_con, - body_con, - loc_symbol, - expr_var, - signature, - ); - } - None => { - let expr_type = Type::Variable(expr_var); - - let expr_con = constrain_expr( - constraints, - env, - loc_expr.region, - &loc_expr.value, - NoExpectation(expr_type), - ); - - body_con = constrain_def_make_constraint_simple( - constraints, - vec![], - vec![], - expr_con, - body_con, - loc_symbol, - expr_var, - Type::Variable(expr_var), - ); - } - } + body_con = + constrain_value_def(constraints, env, declarations, index, body_con); if index == 0 { break; @@ -1035,6 +948,7 @@ pub fn constrain_expr( index -= 1; } + roc_can::expr::DeclarationTag::Function(function_def_index) => { let loc_expr = &declarations.expressions[index]; let loc_symbol = declarations.symbols[index]; @@ -1210,7 +1124,7 @@ pub fn constrain_expr( None => { let expr_type = Type::Variable(expr_var); - let expr_con = constrain_closure( + let expr_con = constrain_untyped_closure( constraints, env, loc_function_def.region, @@ -1246,6 +1160,8 @@ pub fn constrain_expr( } roc_can::expr::DeclarationTag::Destructure(_) => todo!(), roc_can::expr::DeclarationTag::MutualRecursion(_) => todo!(), + roc_can::expr::DeclarationTag::Recursive(_) => todo!(), + roc_can::expr::DeclarationTag::TailRecursive(_) => todo!(), } } @@ -1493,6 +1409,288 @@ pub fn constrain_expr( } } +fn constrain_function_def( + constraints: &mut Constraints, + env: &Env, + declarations: &Declarations, + index: usize, + function_def_index: Index>, + body_con: Constraint, +) -> Constraint { + let loc_expr = &declarations.expressions[index]; + let loc_symbol = declarations.symbols[index]; + let expr_var = declarations.variables[index]; + let opt_annotation = &declarations.annotations[index]; + + let loc_function_def = &declarations.function_bodies[function_def_index.index()]; + let function_def = &loc_function_def.value; + + match opt_annotation { + Some(annotation) => { + let arity = annotation.signature.arity(); + let rigids = &env.rigids; + let mut ftv = rigids.clone(); + + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids_simple( + &annotation.signature, + &annotation.introduced_variables, + &mut ftv, + ); + + let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); + + // TODO missing equality of annotation_expected with expr_var? + // but the signature is stored into the expr_var below?! + + let region = loc_function_def.region; + + let loc_body_expr = loc_expr; + let mut argument_pattern_state = PatternState { + headers: SendMap::default(), + vars: Vec::with_capacity(function_def.arguments.len()), + constraints: Vec::with_capacity(1), + delayed_is_open_constraints: vec![], + }; + let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); + let ret_var = function_def.return_type; + let closure_var = function_def.closure_type; + let closure_ext_var = function_def.closure_ext_var; + + let (arg_types, signature_closure_type, ret_type) = match &signature { + Type::Function(arg_types, signature_closure_type, ret_type) => { + (arg_types, signature_closure_type, ret_type) + } + _ => todo!("TODO {:?}", (loc_symbol, &signature)), + }; + + // Type::Function(arg_types, signature_closure_type, ret_type), + let ret_type = *ret_type.clone(); + + vars.push(ret_var); + vars.push(closure_var); + vars.push(closure_ext_var); + + let mut def_pattern_state = PatternState::default(); + + def_pattern_state.headers.insert( + loc_symbol.value, + Loc { + region, + value: Type::Variable(expr_var), + }, + ); + + constrain_typed_function_arguments_simple( + constraints, + env, + loc_symbol.value, + &mut def_pattern_state, + &mut argument_pattern_state, + &function_def.arguments, + arg_types, + ); + + let closure_constraint = constrain_closure_size( + constraints, + loc_symbol.value, + region, + &function_def.captured_symbols, + closure_var, + closure_ext_var, + &mut vars, + ); + + let annotation_expected = FromAnnotation( + loc_pattern.clone(), + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + ret_type.clone(), + ); + + let ret_constraint = constrain_expr( + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + annotation_expected, + ); + + vars.push(expr_var); + let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints); + + let signature_closure_type = *signature_closure_type.clone(); + let signature_index = constraints.push_type(signature.clone()); + let cons = [ + constraints.let_constraint( + [], + argument_pattern_state.vars, + argument_pattern_state.headers, + defs_constraint, + ret_constraint, + ), + constraints.equal_types_var( + closure_var, + Expected::FromAnnotation( + loc_pattern.clone(), + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + signature_closure_type, + ), + Category::ClosureSize, + region, + ), + constraints.store_index(signature_index, expr_var, std::file!(), std::line!()), + constraints.store_index(signature_index, expr_var, std::file!(), std::line!()), + constraints.store(ret_type, ret_var, std::file!(), std::line!()), + closure_constraint, + ]; + + let expr_con = constraints.exists_many(vars, cons); + + constrain_def_make_constraint_simple( + constraints, + new_rigid_variables, + new_infer_variables, + expr_con, + body_con, + loc_symbol, + expr_var, + signature, + ) + } + None => { + let expr_type = Type::Variable(expr_var); + + let expr_con = constrain_untyped_closure( + constraints, + env, + loc_function_def.region, + NoExpectation(expr_type), + expr_var, + function_def.closure_type, + function_def.closure_ext_var, + function_def.return_type, + &function_def.arguments, + loc_expr, + &function_def.captured_symbols, + loc_symbol.value, + ); + + constrain_def_make_constraint_simple( + constraints, + vec![], + vec![], + expr_con, + body_con, + loc_symbol, + expr_var, + Type::Variable(expr_var), + ) + } + } +} + +fn constrain_value_def( + constraints: &mut Constraints, + env: &Env, + declarations: &Declarations, + index: usize, + body_con: Constraint, +) -> Constraint { + let loc_expr = &declarations.expressions[index]; + let loc_symbol = declarations.symbols[index]; + let expr_var = declarations.variables[index]; + let opt_annotation = &declarations.annotations[index]; + + match opt_annotation { + Some(annotation) => { + let arity = 1; + let rigids = &env.rigids; + let mut ftv = rigids.clone(); + + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids_simple( + &annotation.signature, + &annotation.introduced_variables, + &mut ftv, + ); + + let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); + + let annotation_expected = FromAnnotation( + loc_pattern, + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + signature.clone(), + ); + + // TODO missing equality of annotation_expected with expr_var? + // but the signature is stored into the expr_var below?! + + let ret_constraint = constrain_expr( + constraints, + env, + loc_expr.region, + &loc_expr.value, + annotation_expected, + ); + + let cons = [ + ret_constraint, + // Store type into AST vars. We use Store so errors aren't reported twice + constraints.store(signature.clone(), expr_var, std::file!(), std::line!()), + ]; + let expr_con = constraints.and_constraint(cons); + + constrain_def_make_constraint_simple( + constraints, + new_rigid_variables, + new_infer_variables, + expr_con, + body_con, + loc_symbol, + expr_var, + signature, + ) + } + None => { + let expr_type = Type::Variable(expr_var); + + let expr_con = constrain_expr( + constraints, + env, + loc_expr.region, + &loc_expr.value, + NoExpectation(expr_type), + ); + + constrain_def_make_constraint_simple( + constraints, + vec![], + vec![], + expr_con, + body_con, + loc_symbol, + expr_var, + Type::Variable(expr_var), + ) + } + } +} + /// Constrain a when branch, returning (variables in pattern, symbols introduced in pattern, pattern constraint, body constraint). /// We want to constraint all pattern constraints in a "when" before body constraints. #[inline(always)] @@ -1609,7 +1807,7 @@ fn constrain_empty_record( pub fn constrain_decls( constraints: &mut Constraints, home: ModuleId, - decls: &[Declaration], + declarations: &Declarations, ) -> Constraint { let mut constraint = Constraint::SaveTheEnvironment; @@ -1618,22 +1816,42 @@ pub fn constrain_decls( rigids: MutMap::default(), }; - for decl in decls.iter().rev() { + debug_assert_eq!(declarations.declarations.len(), declarations.symbols.len()); + + for (index, tag) in declarations.iter_top_down() { // Clear the rigids from the previous iteration. // rigids are not shared between top-level definitions env.rigids.clear(); - match decl { - Declaration::Declare(def) | Declaration::Builtin(def) => { - constraint = constrain_def(constraints, &env, def, constraint); + use roc_can::expr::DeclarationTag::*; + match tag { + Value => { + constraint = + constrain_value_def(constraints, &env, declarations, index, constraint); } - Declaration::DeclareRec(defs) => { - constraint = constrain_recursive_defs(constraints, &env, defs, constraint); + Function(function_def_index) => { + constraint = constrain_function_def( + constraints, + &env, + declarations, + index, + function_def_index, + constraint, + ); } - Declaration::InvalidCycle(_) => { - // invalid cycles give a canonicalization error. we skip them here. - continue; + Recursive(function_def_index) | TailRecursive(function_def_index) => { + // for the type it does not matter that a recursive call is a tail call + constraint = constrain_recursive_defs_simple( + constraints, + &env, + declarations, + index, + function_def_index.as_slice(), + constraint, + ); } + Destructure(_) => todo!(), + MutualRecursion(_) => todo!(), } } @@ -2381,6 +2599,255 @@ fn instantiate_rigids_simple( } } +fn constrain_recursive_defs_simple( + constraints: &mut Constraints, + env: &Env, + declarations: &Declarations, + start_index: usize, + defs: Slice>, + body_con: Constraint, +) -> Constraint { + rec_defs_help_simple( + constraints, + env, + declarations, + defs, + start_index, + body_con, + Info::with_capacity(defs.len()), + Info::with_capacity(defs.len()), + ) +} + +pub fn rec_defs_help_simple( + constraints: &mut Constraints, + env: &Env, + declarations: &Declarations, + defs: Slice>, + start_index: usize, + body_con: Constraint, + mut rigid_info: Info, + mut flex_info: Info, +) -> Constraint { + for (index, function_def_index) in (start_index..).zip(defs.indices()) { + let loc_expr = &declarations.expressions[index]; + let loc_symbol = declarations.symbols[index]; + let expr_var = declarations.variables[index]; + let opt_annotation = &declarations.annotations[index]; + + let loc_function_def = &declarations.function_bodies[function_def_index]; + let function_def = &loc_function_def.value; + + match opt_annotation { + None => { + let expr_type = Type::Variable(expr_var); + + let expr_con = constrain_untyped_closure( + constraints, + env, + loc_function_def.region, + NoExpectation(expr_type), + expr_var, + function_def.closure_type, + function_def.closure_ext_var, + function_def.return_type, + &function_def.arguments, + loc_expr, + &function_def.captured_symbols, + loc_symbol.value, + ); + + let def_con = expr_con; + + flex_info.vars = vec![expr_var]; + flex_info.constraints.push(def_con); + flex_info.def_types.insert( + loc_symbol.value, + Loc::at(loc_symbol.region, Type::Variable(expr_var)), + ); + } + + Some(annotation) => { + let arity = annotation.signature.arity(); + let rigids = &env.rigids; + let mut ftv = rigids.clone(); + + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids_simple( + &annotation.signature, + &annotation.introduced_variables, + &mut ftv, + ); + + let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); + + flex_info.vars.extend(new_infer_variables); + + let annotation_expected = FromAnnotation( + loc_pattern.clone(), + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + signature.clone(), + ); + + let (arg_types, _signature_closure_type, ret_type) = match &signature { + Type::Function(arg_types, signature_closure_type, ret_type) => { + (arg_types, signature_closure_type, ret_type) + } + _ => todo!("TODO {:?}", (loc_symbol, &signature)), + }; + + let expected = annotation_expected; + let region = loc_function_def.region; + + let loc_body_expr = loc_expr; + let mut argument_pattern_state = PatternState { + headers: SendMap::default(), + vars: Vec::with_capacity(function_def.arguments.len()), + constraints: Vec::with_capacity(1), + delayed_is_open_constraints: vec![], + }; + let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); + let ret_var = function_def.return_type; + let closure_var = function_def.closure_type; + let closure_ext_var = function_def.closure_ext_var; + let ret_type = *ret_type.clone(); + + vars.push(ret_var); + vars.push(closure_var); + vars.push(closure_ext_var); + + let mut def_pattern_state = PatternState::default(); + + def_pattern_state.headers.insert( + loc_symbol.value, + Loc { + region, + value: Type::Variable(expr_var), + }, + ); + + constrain_typed_function_arguments_simple( + constraints, + env, + loc_symbol.value, + &mut def_pattern_state, + &mut argument_pattern_state, + &function_def.arguments, + arg_types, + ); + + let pattern_types = function_def + .arguments + .iter() + .map(|a| Type::Variable(a.0)) + .collect(); + + let closure_constraint = constrain_closure_size( + constraints, + loc_symbol.value, + region, + &function_def.captured_symbols, + closure_var, + closure_ext_var, + &mut vars, + ); + + let fn_type = Type::Function( + pattern_types, + Box::new(Type::Variable(closure_var)), + Box::new(ret_type.clone()), + ); + + let expr_con = constrain_expr( + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + NoExpectation(ret_type.clone()), + ); + + vars.push(expr_var); + + let signature_index = constraints.push_type(signature); + let state_constraints = + constraints.and_constraint(argument_pattern_state.constraints); + let cons = [ + constraints.let_constraint( + [], + argument_pattern_state.vars, + argument_pattern_state.headers, + state_constraints, + expr_con, + ), + constraints.equal_types( + fn_type.clone(), + expected.clone(), + Category::Lambda, + region, + ), + // "fn_var is equal to the closure's type" - fn_var is used in code gen + // Store type into AST vars. We use Store so errors aren't reported twice + constraints.store_index(signature_index, expr_var, std::file!(), std::line!()), + constraints.store(ret_type, ret_var, std::file!(), std::line!()), + closure_constraint, + ]; + + let and_constraint = constraints.and_constraint(cons); + let def_con = constraints.exists(vars, and_constraint); + + rigid_info.vars.extend(&new_rigid_variables); + + rigid_info.constraints.push(constraints.let_constraint( + new_rigid_variables, + def_pattern_state.vars, + [], // no headers introduced (at this level) + def_con, + Constraint::True, + )); + rigid_info.def_types.extend(def_pattern_state.headers); + } + } + } + + let flex_constraints = constraints.and_constraint(flex_info.constraints); + let inner_inner = constraints.let_constraint( + [], + [], + flex_info.def_types.clone(), + Constraint::True, + flex_constraints, + ); + + let rigid_constraints = { + let mut temp = rigid_info.constraints; + temp.push(body_con); + + constraints.and_constraint(temp) + }; + + let inner = constraints.let_constraint( + [], + flex_info.vars, + flex_info.def_types, + inner_inner, + rigid_constraints, + ); + + constraints.let_constraint( + rigid_info.vars, + [], + rigid_info.def_types, + Constraint::True, + inner, + ) +} + fn constrain_recursive_defs( constraints: &mut Constraints, env: &Env, diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index ec913f44cd..65559357c3 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -4,6 +4,7 @@ use roc_can::abilities::AbilitiesStore; use roc_can::constraint::{Constraint, Constraints}; use roc_can::def::Declaration; use roc_can::expected::Expected; +use roc_can::expr::Declarations; use roc_can::pattern::Pattern; use roc_collections::all::MutMap; use roc_error_macros::internal_error; @@ -98,7 +99,7 @@ pub fn constrain_module( constraints: &mut Constraints, symbols_from_requires: Vec<(Loc, Loc)>, abilities_store: &AbilitiesStore, - declarations: &[Declaration], + declarations: &Declarations, home: ModuleId, ) -> Constraint { let constraint = crate::expr::constrain_decls(constraints, home, declarations); diff --git a/compiler/load_internal/src/file.rs b/compiler/load_internal/src/file.rs index 230582b30e..f8f9710047 100644 --- a/compiler/load_internal/src/file.rs +++ b/compiler/load_internal/src/file.rs @@ -9,6 +9,7 @@ use roc_builtins::std::borrow_stdlib; use roc_can::abilities::AbilitiesStore; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; use roc_can::def::Declaration; +use roc_can::expr::Declarations; use roc_can::module::{canonicalize_module_defs, Module}; use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecSet}; use roc_constrain::module::{ @@ -459,7 +460,7 @@ pub struct LoadedModule { pub solved: Solved, pub can_problems: MutMap>, pub type_problems: MutMap>, - pub declarations_by_id: MutMap>, + pub declarations_by_id: MutMap, pub exposed_to_host: MutMap, pub dep_idents: IdentIdsByModule, pub exposed_aliases: MutMap, @@ -520,7 +521,7 @@ struct ModuleHeader<'a> { #[derive(Debug)] struct ConstrainedModule { module: Module, - declarations: Vec, + declarations: Declarations, imported_modules: MutMap, constraints: Constraints, constraint: ConstraintSoa, @@ -536,7 +537,7 @@ pub struct TypeCheckedModule<'a> { pub layout_cache: LayoutCache<'a>, pub module_timing: ModuleTiming, pub solved_subs: Solved, - pub decls: Vec, + pub decls: Declarations, pub ident_ids: IdentIds, pub abilities_store: AbilitiesStore, } @@ -621,7 +622,7 @@ enum Msg<'a> { ident_ids: IdentIds, solved_module: SolvedModule, solved_subs: Solved, - decls: Vec, + decls: Declarations, dep_idents: IdentIdsByModule, module_timing: ModuleTiming, abilities_store: AbilitiesStore, @@ -718,7 +719,7 @@ struct State<'a> { pub ident_ids_by_module: SharedIdentIdsByModule, - pub declarations_by_id: MutMap>, + pub declarations_by_id: MutMap, pub exposed_symbols_by_module: MutMap>, @@ -879,7 +880,7 @@ enum BuildTask<'a> { constraints: Constraints, constraint: ConstraintSoa, var_store: VarStore, - declarations: Vec, + declarations: Declarations, dep_idents: IdentIdsByModule, cached_subs: CachedSubs, }, @@ -890,7 +891,7 @@ enum BuildTask<'a> { imported_module_thunks: &'a [Symbol], module_id: ModuleId, ident_ids: IdentIds, - decls: Vec, + decls: Declarations, exposed_to_host: ExposedToHost, abilities_store: AbilitiesStore, }, @@ -3544,7 +3545,7 @@ impl<'a> BuildTask<'a> { imported_modules: MutMap, exposed_types: &mut ExposedByModule, dep_idents: IdentIdsByModule, - declarations: Vec, + declarations: Declarations, cached_subs: CachedSubs, ) -> Self { let exposed_by_module = exposed_types.retain_modules(imported_modules.keys()); @@ -3724,7 +3725,7 @@ fn run_solve<'a>( constraints: Constraints, constraint: ConstraintSoa, var_store: VarStore, - decls: Vec, + decls: Declarations, dep_idents: IdentIdsByModule, cached_subs: CachedSubs, ) -> Msg<'a> { @@ -4188,7 +4189,7 @@ fn build_pending_specializations<'a>( imported_module_thunks: &'a [Symbol], home: ModuleId, mut ident_ids: IdentIds, - decls: Vec, + decls: Declarations, mut module_timing: ModuleTiming, mut layout_cache: LayoutCache<'a>, target_info: TargetInfo, @@ -4224,6 +4225,8 @@ fn build_pending_specializations<'a>( abilities_store: &mut abilities_store, }; + let decls = []; + // Add modules' decls to Procs for decl in decls { use roc_can::def::Declaration::*; diff --git a/examples/hello-world/zig-platform/helloZig.roc b/examples/hello-world/zig-platform/helloZig.roc index 6c7223111a..7c0a0b8291 100644 --- a/examples/hello-world/zig-platform/helloZig.roc +++ b/examples/hello-world/zig-platform/helloZig.roc @@ -3,4 +3,7 @@ app "helloZig" imports [] provides [ main ] to pf -main = "Hello, World!\n" +f : Str -> Str +f = \s -> Str.concat s "!" + +main = f "Hello, World!\n"