mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-02 19:32:17 +00:00
Handle aliases at call instead
This commit is contained in:
parent
b70d48fd33
commit
80770fae11
3 changed files with 126 additions and 98 deletions
|
@ -113,35 +113,21 @@ impl Annotation {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_arguments(&mut self, argument_count: usize, var_store: &mut VarStore) {
|
pub fn convert_to_fn(&mut self, argument_count: usize, var_store: &mut VarStore) {
|
||||||
match self.signature {
|
let mut arg_types = Vec::with_capacity(argument_count);
|
||||||
Type::Function(ref mut arg_types, _, _) => {
|
|
||||||
arg_types.reserve(argument_count);
|
|
||||||
|
|
||||||
for _ in 0..argument_count {
|
for _ in 0..argument_count {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
self.introduced_variables.insert_inferred(Loc::at_zero(var));
|
self.introduced_variables.insert_inferred(Loc::at_zero(var));
|
||||||
|
|
||||||
arg_types.push(Type::Variable(var));
|
arg_types.push(Type::Variable(var));
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let mut arg_types = Vec::with_capacity(argument_count);
|
|
||||||
|
|
||||||
for _ in 0..argument_count {
|
|
||||||
let var = var_store.fresh();
|
|
||||||
self.introduced_variables.insert_inferred(Loc::at_zero(var));
|
|
||||||
|
|
||||||
arg_types.push(Type::Variable(var));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.signature = Type::Function(
|
|
||||||
arg_types,
|
|
||||||
Box::new(Type::Variable(var_store.fresh())),
|
|
||||||
Box::new(self.signature.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.signature = Type::Function(
|
||||||
|
arg_types,
|
||||||
|
Box::new(Type::Variable(var_store.fresh())),
|
||||||
|
Box::new(self.signature.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3135,11 +3135,11 @@ impl Declarations {
|
||||||
Index::push_new(&mut self.function_bodies, loc_function_def);
|
Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||||
|
|
||||||
if let Some(annotation) = &mut self.annotations[index] {
|
if let Some(annotation) = &mut self.annotations[index] {
|
||||||
annotation.add_arguments(new_args_len, var_store);
|
annotation.convert_to_fn(new_args_len, var_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_var, annotation)) = self.host_exposed_annotations.get_mut(&index) {
|
if let Some((_var, annotation)) = self.host_exposed_annotations.get_mut(&index) {
|
||||||
annotation.add_arguments(new_args_len, var_store);
|
annotation.convert_to_fn(new_args_len, var_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.declarations[index] = DeclarationTag::Function(function_def_index);
|
self.declarations[index] = DeclarationTag::Function(function_def_index);
|
||||||
|
|
|
@ -11,7 +11,10 @@ use roc_can::{
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::{
|
||||||
|
subs::{VarStore, Variable},
|
||||||
|
types::Type,
|
||||||
|
};
|
||||||
|
|
||||||
struct LowerParams<'a> {
|
struct LowerParams<'a> {
|
||||||
home_id: ModuleId,
|
home_id: ModuleId,
|
||||||
|
@ -55,21 +58,13 @@ impl<'a> LowerParams<'a> {
|
||||||
|
|
||||||
match tag {
|
match tag {
|
||||||
Value => {
|
Value => {
|
||||||
let aliased = self.lower_expr(true, &mut decls.expressions[index].value);
|
self.lower_expr(&mut decls.expressions[index].value);
|
||||||
|
|
||||||
if let Some(new_arg) = self.home_params_argument() {
|
if let Some(new_arg) = self.home_params_argument() {
|
||||||
if !aliased {
|
// This module has params, and this is a top-level value,
|
||||||
// This module has params, and this is a top-level value,
|
// so we need to convert it into a function that takes them.
|
||||||
// so we need to convert it into a function that takes them.
|
|
||||||
|
|
||||||
decls.convert_value_to_function(index, vec![new_arg], self.var_store);
|
decls.convert_value_to_function(index, vec![new_arg], self.var_store);
|
||||||
} else {
|
|
||||||
// This value def is just aliasing another params extended def,
|
|
||||||
// we only need to fix the annotation
|
|
||||||
if let Some(ann) = &mut decls.annotations[index] {
|
|
||||||
ann.add_arguments(1, self.var_store);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Function(fn_def_index) | Recursive(fn_def_index) | TailRecursive(fn_def_index) => {
|
Function(fn_def_index) | Recursive(fn_def_index) | TailRecursive(fn_def_index) => {
|
||||||
|
@ -83,24 +78,25 @@ impl<'a> LowerParams<'a> {
|
||||||
.push((var, mark, pattern));
|
.push((var, mark, pattern));
|
||||||
|
|
||||||
if let Some(ann) = &mut decls.annotations[index] {
|
if let Some(ann) = &mut decls.annotations[index] {
|
||||||
ann.add_arguments(1, self.var_store);
|
if let Type::Function(args, _, _) = &mut ann.signature {
|
||||||
|
args.push(Type::Variable(var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lower_expr(false, &mut decls.expressions[index].value);
|
self.lower_expr(&mut decls.expressions[index].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Destructure(_) | Expectation | ExpectationFx => {
|
Destructure(_) | Expectation | ExpectationFx => {
|
||||||
self.lower_expr(false, &mut decls.expressions[index].value);
|
self.lower_expr(&mut decls.expressions[index].value);
|
||||||
}
|
}
|
||||||
MutualRecursion { .. } => {}
|
MutualRecursion { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_expr(&mut self, is_value_def: bool, expr: &mut Expr) -> bool {
|
fn lower_expr(&mut self, expr: &mut Expr) {
|
||||||
let mut expr_stack = vec![expr];
|
let mut expr_stack = vec![expr];
|
||||||
let mut aliased = false;
|
|
||||||
|
|
||||||
while let Some(expr) = expr_stack.pop() {
|
while let Some(expr) = expr_stack.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
|
@ -113,11 +109,9 @@ impl<'a> LowerParams<'a> {
|
||||||
} => {
|
} => {
|
||||||
// The module was imported with params, but it might not actually expect them.
|
// The module was imported with params, but it might not actually expect them.
|
||||||
// We should only lower if it does to prevent confusing type errors.
|
// We should only lower if it does to prevent confusing type errors.
|
||||||
if let Some(params) = self.imported_params.get(&symbol.module_id()) {
|
if let Some(arity) = self.get_imported_def_arity(symbol) {
|
||||||
let arity = params.arity_by_name.get(&symbol.ident_id()).unwrap();
|
|
||||||
|
|
||||||
*expr = self.lower_naked_params_var(
|
*expr = self.lower_naked_params_var(
|
||||||
*arity,
|
arity,
|
||||||
*symbol,
|
*symbol,
|
||||||
*var,
|
*var,
|
||||||
*params_symbol,
|
*params_symbol,
|
||||||
|
@ -127,12 +121,6 @@ impl<'a> LowerParams<'a> {
|
||||||
}
|
}
|
||||||
Var(symbol, var) => {
|
Var(symbol, var) => {
|
||||||
if let Some((params, arity)) = self.params_extended_home_symbol(symbol) {
|
if let Some((params, arity)) = self.params_extended_home_symbol(symbol) {
|
||||||
if is_value_def {
|
|
||||||
// Aliased top-level def, no need to lower
|
|
||||||
aliased = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
*expr = self.lower_naked_params_var(
|
*expr = self.lower_naked_params_var(
|
||||||
arity,
|
arity,
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -154,23 +142,52 @@ impl<'a> LowerParams<'a> {
|
||||||
} => {
|
} => {
|
||||||
// Calling an imported function with params
|
// Calling an imported function with params
|
||||||
|
|
||||||
// Extend arguments only if the imported module actually expects params
|
match self.get_imported_def_arity(&symbol) {
|
||||||
if self.imported_params.contains_key(&symbol.module_id()) {
|
Some(0) => {
|
||||||
args.push((
|
// We are calling a function but the top-level declaration has no arguments.
|
||||||
params_var,
|
// This can either be a function alias or a top-level def that returns functions
|
||||||
Loc::at_zero(Var(params_symbol, params_var)),
|
// under multiple branches.
|
||||||
));
|
// We call the value def with params, and apply the returned function to the original arguments.
|
||||||
}
|
fun.1.value = self.call_value_def_with_params(
|
||||||
|
symbol,
|
||||||
|
var,
|
||||||
|
params_symbol,
|
||||||
|
params_var,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
// The module expects params and they were provided, we need to extend the call.
|
||||||
|
fun.1.value = Var(symbol, var);
|
||||||
|
|
||||||
fun.1.value = Var(symbol, var);
|
args.push((
|
||||||
|
params_var,
|
||||||
|
Loc::at_zero(Var(params_symbol, params_var)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// The module expects no params, do not extend to prevent confusing type errors.
|
||||||
|
fun.1.value = Var(symbol, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Var(symbol, _var) => {
|
Var(symbol, _var) => {
|
||||||
if let Some((params, _)) = self.params_extended_home_symbol(&symbol) {
|
if let Some((params, arity)) = self.params_extended_home_symbol(&symbol)
|
||||||
// Calling a top-level function in the current module with params
|
{
|
||||||
args.push((
|
if arity == 0 {
|
||||||
params.whole_var,
|
// Calling the result of a top-level value def in the current module
|
||||||
Loc::at_zero(Var(params.whole_symbol, params.whole_var)),
|
fun.1.value = self.call_value_def_with_params(
|
||||||
));
|
symbol,
|
||||||
|
params.whole_var,
|
||||||
|
params.whole_symbol,
|
||||||
|
params.whole_var,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Calling a top-level function in the current module with params
|
||||||
|
args.push((
|
||||||
|
params.whole_var,
|
||||||
|
Loc::at_zero(Var(params.whole_symbol, params.whole_var)),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => expr_stack.push(&mut fun.1.value),
|
_ => expr_stack.push(&mut fun.1.value),
|
||||||
|
@ -370,14 +387,26 @@ impl<'a> LowerParams<'a> {
|
||||||
| AbilityMember(_, _, _) => { /* terminal */ }
|
| AbilityMember(_, _, _) => { /* terminal */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aliased
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unique_symbol(&mut self) -> Symbol {
|
fn unique_symbol(&mut self) -> Symbol {
|
||||||
Symbol::new(self.home_id, self.ident_ids.gen_unique())
|
Symbol::new(self.home_id, self.ident_ids.gen_unique())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn home_params_argument(&mut self) -> Option<(Variable, AnnotatedMark, Loc<Pattern>)> {
|
||||||
|
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 params_extended_home_symbol(&self, symbol: &Symbol) -> Option<(&ModuleParams, usize)> {
|
fn params_extended_home_symbol(&self, symbol: &Symbol) -> Option<(&ModuleParams, usize)> {
|
||||||
if symbol.module_id() == self.home_id {
|
if symbol.module_id() == self.home_id {
|
||||||
match self.home_params {
|
match self.home_params {
|
||||||
|
@ -392,6 +421,12 @@ impl<'a> LowerParams<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_imported_def_arity(&self, symbol: &Symbol) -> Option<usize> {
|
||||||
|
self.imported_params
|
||||||
|
.get(&symbol.module_id())
|
||||||
|
.and_then(|params| params.arity_by_name.get(&symbol.ident_id()).copied())
|
||||||
|
}
|
||||||
|
|
||||||
fn lower_naked_params_var(
|
fn lower_naked_params_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
|
@ -400,14 +435,6 @@ impl<'a> LowerParams<'a> {
|
||||||
params_symbol: Symbol,
|
params_symbol: Symbol,
|
||||||
params_var: Variable,
|
params_var: Variable,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var)));
|
|
||||||
let call_fn = Box::new((
|
|
||||||
self.var_store.fresh(),
|
|
||||||
Loc::at_zero(Var(symbol, var)),
|
|
||||||
self.var_store.fresh(),
|
|
||||||
self.var_store.fresh(),
|
|
||||||
));
|
|
||||||
|
|
||||||
if arity == 0 {
|
if arity == 0 {
|
||||||
// We are passing a top-level value that takes params, so we need to replace the Var
|
// We are passing a top-level value that takes params, so we need to replace the Var
|
||||||
// with a call that passes the params to get the final result.
|
// with a call that passes the params to get the final result.
|
||||||
|
@ -416,12 +443,7 @@ impl<'a> LowerParams<'a> {
|
||||||
// record = \... #params -> { doubled: value }
|
// record = \... #params -> { doubled: value }
|
||||||
// ↓
|
// ↓
|
||||||
// value #params
|
// value #params
|
||||||
Call(
|
self.call_value_def_with_params(symbol, var, params_symbol, params_var)
|
||||||
call_fn,
|
|
||||||
vec![params_arg],
|
|
||||||
// todo: custom called via
|
|
||||||
roc_module::called_via::CalledVia::Space,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// We are passing a top-level function that takes params, so we need to replace
|
// We are passing a top-level function that takes params, so we need to replace
|
||||||
// the Var with a closure that captures the params and passes them to the function.
|
// the Var with a closure that captures the params and passes them to the function.
|
||||||
|
@ -446,8 +468,17 @@ impl<'a> LowerParams<'a> {
|
||||||
call_arguments.push((var, Loc::at_zero(Var(sym, var))));
|
call_arguments.push((var, Loc::at_zero(Var(sym, var))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var)));
|
||||||
|
|
||||||
call_arguments.push(params_arg);
|
call_arguments.push(params_arg);
|
||||||
|
|
||||||
|
let call_fn = Box::new((
|
||||||
|
self.var_store.fresh(),
|
||||||
|
Loc::at_zero(Var(symbol, var)),
|
||||||
|
self.var_store.fresh(),
|
||||||
|
self.var_store.fresh(),
|
||||||
|
));
|
||||||
|
|
||||||
let body = Call(
|
let body = Call(
|
||||||
call_fn,
|
call_fn,
|
||||||
call_arguments,
|
call_arguments,
|
||||||
|
@ -475,17 +506,28 @@ impl<'a> LowerParams<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn home_params_argument(&mut self) -> Option<(Variable, AnnotatedMark, Loc<Pattern>)> {
|
|
||||||
match &self.home_params {
|
fn call_value_def_with_params(
|
||||||
Some(module_params) => {
|
&mut self,
|
||||||
let new_var = self.var_store.fresh();
|
symbol: Symbol,
|
||||||
Some((
|
var: Variable,
|
||||||
new_var,
|
params_symbol: Symbol,
|
||||||
AnnotatedMark::new(self.var_store),
|
params_var: Variable,
|
||||||
module_params.pattern(),
|
) -> Expr {
|
||||||
))
|
let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var)));
|
||||||
}
|
|
||||||
None => None,
|
let call_fn = Box::new((
|
||||||
}
|
self.var_store.fresh(),
|
||||||
|
Loc::at_zero(Var(symbol, var)),
|
||||||
|
self.var_store.fresh(),
|
||||||
|
self.var_store.fresh(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Call(
|
||||||
|
call_fn,
|
||||||
|
vec![params_arg],
|
||||||
|
// todo: custom called via
|
||||||
|
roc_module::called_via::CalledVia::Space,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue