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 | ||||
| 
 | ||||
| ## [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 | ||||
| 
 | ||||
| ### 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] | ||||
| fn if_scoping() { | ||||
|     let source_code = r#" | ||||
|  |  | |||
|  | @ -1480,32 +1480,45 @@ impl<'a, 'b> ExprTyper<'a, 'b> { | |||
|     ) -> Result<(Vec<TypedArg>, TypedExpr), Error> { | ||||
|         self.assert_no_assignment(&body)?; | ||||
| 
 | ||||
|         for arg in &args { | ||||
|             match &arg.arg_name { | ||||
|                 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(), | ||||
|                     ); | ||||
|         let (body_rigid_names, body_infer) = self.in_new_scope(|body_typer| { | ||||
|             let mut argument_names = HashMap::with_capacity(args.len()); | ||||
| 
 | ||||
|                     self.environment.init_usage( | ||||
|                         name.to_string(), | ||||
|                         EntityKind::Variable, | ||||
|                         arg.location, | ||||
|                     ); | ||||
|                 } | ||||
|                 ArgName::Named { .. } | ArgName::Discarded { .. } => (), | ||||
|             }; | ||||
|         } | ||||
|             for arg in &args { | ||||
|                 match &arg.arg_name { | ||||
|                     ArgName::Named { | ||||
|                         name, | ||||
|                         is_validator_param, | ||||
|                         location, | ||||
|                         .. | ||||
|                     } 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))?; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 rvcas
						rvcas