diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 218f97ee68..d21e161e81 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -737,6 +737,8 @@ mod cli_run { App2.baseUrl: http://api.example.com/two App1.getUser 1: https://api.example.com/one/users/1 App2.getUser 2: http://api.example.com/two/users/2 + App1.getPost 1: https://api.example.com/one/posts/1 + App2.getPost 2: http://api.example.com/two/posts/2 "# ), UseValgrind::No, diff --git a/crates/cli/tests/module_params/Api.roc b/crates/cli/tests/module_params/Api.roc index 8d7b96569c..dbc9434197 100644 --- a/crates/cli/tests/module_params/Api.roc +++ b/crates/cli/tests/module_params/Api.roc @@ -1,4 +1,4 @@ -module { appId, protocol } -> [baseUrl, getUser] +module { appId, protocol } -> [baseUrl, getUser, getPost] baseUrl : Str baseUrl = @@ -7,4 +7,9 @@ baseUrl = getUser : U32 -> Str getUser = \userId -> + # purposefully not using baseUrl to test top-level fn referencing param protocol "api.example.com/$(appId)/users/$(Num.toStr userId)" + +getPost : U32 -> Str +getPost = \postId -> + "$(baseUrl)/posts/$(Num.toStr postId)" diff --git a/crates/cli/tests/module_params/app.roc b/crates/cli/tests/module_params/app.roc index 5af0aee6cd..9af8c0b0fe 100644 --- a/crates/cli/tests/module_params/app.roc +++ b/crates/cli/tests/module_params/app.roc @@ -14,4 +14,6 @@ main = App2.baseUrl: $(App2.baseUrl) App1.getUser 1: $(App1.getUser 1) App2.getUser 2: $(App2.getUser 2) + App1.getPost 1: $(App1.getPost 1) + App2.getPost 2: $(App2.getPost 2) """ diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index e111c728e8..0d3cd43afe 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -5090,6 +5090,8 @@ fn canonicalize_and_constrain<'a>( // lower module params roc_lower_params::lower( + module_id, + // todo(agus): borrow params? module_output.module_params.clone(), &mut module_output.declarations, &mut var_store, diff --git a/crates/compiler/lower_params/src/lib.rs b/crates/compiler/lower_params/src/lib.rs index a014a9896b..b1885d4b5d 100644 --- a/crates/compiler/lower_params/src/lib.rs +++ b/crates/compiler/lower_params/src/lib.rs @@ -8,23 +8,39 @@ use roc_can::{ module::ModuleParams, pattern::Pattern, }; +use roc_module::symbol::{IdentId, ModuleId, Symbol}; use roc_region::all::Loc; use roc_types::subs::{VarStore, Variable}; use roc_types::types::Type; struct LowerParams<'a> { - // todo: remove var as we can't use it + home_id: ModuleId, + /// Top-level idents that we need to extend in a module with params. Empty if no params. + home_top_level_idents: Vec, home_params: Option, var_store: &'a mut VarStore, } pub fn lower( + home_id: ModuleId, home_params: Option, decls: &mut Declarations, var_store: &mut VarStore, ) { + let home_top_level_idents = if home_params.is_some() { + decls + .symbols + .iter() + .map(|loc_sym| loc_sym.value.ident_id()) + .collect() + } else { + vec![] + }; + let mut env = LowerParams { + home_id, home_params, + home_top_level_idents, var_store, }; @@ -76,20 +92,6 @@ pub fn lower( } impl<'a> LowerParams<'a> { - fn home_params_argument(&mut self) -> Option<(Variable, AnnotatedMark, Loc)> { - match &self.home_params { - Some(module_params) => { - let new_var = self.var_store.fresh(); - Some(( - new_var, - AnnotatedMark::new(self.var_store), - module_params.pattern(), - )) - } - None => None, - } - } - fn lower_expr(&mut self, expr: &mut Expr) { match expr { ParamsVar { @@ -98,19 +100,18 @@ impl<'a> LowerParams<'a> { params_symbol, params_var, } => { - let params_arg = (*params_var, Loc::at_zero(Var(*params_symbol, *params_var))); - - *expr = Call( - Box::new(( - self.var_store.fresh(), - Loc::at_zero(Var(*symbol, *var)), - self.var_store.fresh(), - self.var_store.fresh(), - )), - vec![params_arg], - // todo: custom called via - roc_module::called_via::CalledVia::Space, - ); + // A referece to a top-level value def in an imported module with params + *expr = self.call_params_var(*symbol, *var, *params_symbol, *params_var); + } + Var(symbol, var) => { + if symbol.module_id() == self.home_id + && self.home_top_level_idents.contains(&symbol.ident_id()) + { + // A reference to a top-level value def in the home module with params + let params = self.home_params.as_ref().unwrap(); + *expr = + self.call_params_var(*symbol, *var, params.whole_symbol, params.whole_var); + } } Call(fun, args, _called_via) => { for arg in args.iter_mut() { @@ -119,6 +120,7 @@ impl<'a> LowerParams<'a> { } match fun.1.value { + // A call to a function in an imported module with params ParamsVar { symbol, var, @@ -148,4 +150,39 @@ impl<'a> LowerParams<'a> { _ => { /* todo */ } } } + + fn call_params_var( + &mut self, + symbol: Symbol, + var: Variable, + params_symbol: Symbol, + params_var: Variable, + ) -> Expr { + let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var))); + + Call( + Box::new(( + self.var_store.fresh(), + Loc::at_zero(Var(symbol, var)), + self.var_store.fresh(), + self.var_store.fresh(), + )), + vec![params_arg], + // todo: custom called via + roc_module::called_via::CalledVia::Space, + ) + } + fn home_params_argument(&mut self) -> Option<(Variable, AnnotatedMark, Loc)> { + match &self.home_params { + Some(module_params) => { + let new_var = self.var_store.fresh(); + Some(( + new_var, + AnnotatedMark::new(self.var_store), + module_params.pattern(), + )) + } + None => None, + } + } }