feat(aiken-lang): anonymous functions
@MartinSchere noticed a weird error where an unknown variable wasn't being reported the type checker was incorrectly scoping arguments for anonymous function definitions. Luckily his compilation failed due to a FreeUnique error during code gen which is good. But this may have been the source of other mysterious FreeUnique errors. I also noticed that anonymous function allowed arguments with the same name to be defined. `fn(arg, arg)` This now returns an error.
This commit is contained in:
parent
20edce2146
commit
98c61ca151
|
@ -1,5 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [next] - 2023-MM-DD
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **aiken-lang**: incorrect scoping for anonymous functions
|
||||||
|
- **aiken-lang**: duplicate arguments were allowed in anonymous functions
|
||||||
|
|
||||||
## v1.0.0-alpha - 2023-04-13
|
## v1.0.0-alpha - 2023-04-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -154,6 +154,56 @@ fn multi_validator_warning() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_function_scoping() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn reduce(list, f, i) {
|
||||||
|
todo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foo() {
|
||||||
|
let sum =
|
||||||
|
reduce(
|
||||||
|
[1, 2, 3],
|
||||||
|
fn(acc: Int, n: Int) { acc + n },
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
sum + acc
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::UnknownVariable { name, .. })) if name == "acc"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anonymous_function_dupicate_args() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn reduce(list, f, i) {
|
||||||
|
todo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foo() {
|
||||||
|
let sum =
|
||||||
|
reduce(
|
||||||
|
[1, 2, 3],
|
||||||
|
fn(acc: Int, acc: Int) { acc + acc },
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::DuplicateArgument { label, .. })) if label == "acc"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_scoping() {
|
fn if_scoping() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
|
|
@ -1480,32 +1480,45 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
) -> Result<(Vec<TypedArg>, TypedExpr), Error> {
|
) -> Result<(Vec<TypedArg>, TypedExpr), Error> {
|
||||||
self.assert_no_assignment(&body)?;
|
self.assert_no_assignment(&body)?;
|
||||||
|
|
||||||
for arg in &args {
|
let (body_rigid_names, body_infer) = self.in_new_scope(|body_typer| {
|
||||||
match &arg.arg_name {
|
let mut argument_names = HashMap::with_capacity(args.len());
|
||||||
ArgName::Named {
|
|
||||||
name,
|
|
||||||
is_validator_param,
|
|
||||||
..
|
|
||||||
} if !is_validator_param => {
|
|
||||||
self.environment.insert_variable(
|
|
||||||
name.to_string(),
|
|
||||||
ValueConstructorVariant::LocalVariable {
|
|
||||||
location: arg.location,
|
|
||||||
},
|
|
||||||
arg.tipo.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.environment.init_usage(
|
for arg in &args {
|
||||||
name.to_string(),
|
match &arg.arg_name {
|
||||||
EntityKind::Variable,
|
ArgName::Named {
|
||||||
arg.location,
|
name,
|
||||||
);
|
is_validator_param,
|
||||||
}
|
location,
|
||||||
ArgName::Named { .. } | ArgName::Discarded { .. } => (),
|
..
|
||||||
};
|
} if !is_validator_param => {
|
||||||
}
|
if let Some(duplicate_location) = argument_names.insert(name, location) {
|
||||||
|
return Err(Error::DuplicateArgument {
|
||||||
|
location: *location,
|
||||||
|
duplicate_location: *duplicate_location,
|
||||||
|
label: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let (body_rigid_names, body_infer) = (self.hydrator.rigid_names(), self.infer(body));
|
body_typer.environment.insert_variable(
|
||||||
|
name.to_string(),
|
||||||
|
ValueConstructorVariant::LocalVariable {
|
||||||
|
location: arg.location,
|
||||||
|
},
|
||||||
|
arg.tipo.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
body_typer.environment.init_usage(
|
||||||
|
name.to_string(),
|
||||||
|
EntityKind::Variable,
|
||||||
|
arg.location,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArgName::Named { .. } | ArgName::Discarded { .. } => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((body_typer.hydrator.rigid_names(), body_typer.infer(body)))
|
||||||
|
})?;
|
||||||
|
|
||||||
let body = body_infer.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names))?;
|
let body = body_infer.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names))?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue