Skip to content

Commit 9076da0

Browse files
feat(type-checker): implement function call and block checking, update return and function definition
- Created `check_block_stmt` to handle scoped type checking of block statements - Created `check_func_call` to validate function call expressions - Updated `check_return_stmt` to ensure return type matches declared function type - Improved `check_func_def_stmt` to handle duplicate declarations and parameter validation These changes improve the accuracy and robustness of the type checker.
1 parent 4827d06 commit 9076da0

5 files changed

Lines changed: 73 additions & 25 deletions

File tree

src/environment/environment.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ impl<A: Clone> Environment<A> {
7777
pub fn set_global_functions(&mut self, global_functions: HashMap<Name, Function>) {
7878
self.globals.functions = global_functions;
7979
}
80-
8180

8281
pub fn map_variable(&mut self, var: Name, mutable: bool, value: A) -> () {
8382
match self.stack.front_mut() {
@@ -161,7 +160,7 @@ impl<A: Clone> Environment<A> {
161160
}
162161

163162
// The type checker ensures that each function is defined only once
164-
pub fn get_all_functions(&self) -> HashMap<Name,Function> {
163+
pub fn get_all_functions(&self) -> HashMap<Name, Function> {
165164
let mut all_functions = HashMap::new();
166165
for (name, func) in &self.globals.functions {
167166
all_functions.insert(name.clone(), func.clone());

src/interpreter/expression_eval.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ pub fn eval_function_call(
404404
new_env.set_current_func(&name);
405405
// Functions from the outer environment must be propagated to new_env to ensure access to external functions within the function body.
406406
// This also allows the function to reference itself, which enables recursion
407-
new_env.set_global_functions(env.get_all_functions());
407+
new_env.set_global_functions(env.get_all_functions());
408408

409409
for (formal, actual) in function_definition.params.iter().zip(args.iter()) {
410410
let value = match eval(actual.clone(), env)? {
@@ -416,8 +416,6 @@ pub fn eval_function_call(
416416
new_env.map_variable(formal.argument_name.clone(), false, value);
417417
}
418418

419-
420-
421419
// Execute the body of the function.
422420
match super::statement_execute::execute(
423421
*function_definition.body.as_ref().unwrap().clone(), //Push occurs here, because function body is a Statement::Block

src/interpreter/statement_execute.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,7 @@ pub fn execute_block(
211211
for stmt in stmts {
212212
match execute(stmt, &current_env)? {
213213
Computation::Continue(new_env) => current_env = new_env,
214-
Computation::Return(expr, mut new_env) =>
215-
{
214+
Computation::Return(expr, mut new_env) => {
216215
new_env.pop(); //expr has already been evaluated, so it is safe to pop here
217216
return Ok(Computation::Return(expr, new_env));
218217
}
@@ -234,8 +233,7 @@ pub fn execute_if_block(
234233
for stmt in stmts {
235234
match execute(stmt, &current_env)? {
236235
Computation::Continue(new_env) => current_env = new_env,
237-
Computation::Return(expr, new_env) =>
238-
{
236+
Computation::Return(expr, new_env) => {
239237
return Ok(Computation::Return(expr, new_env));
240238
}
241239
Computation::PropagateError(expr, new_env) => {

src/type_checker/expression_type_checker.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ pub fn check_expr(exp: Expression, env: &Environment<Type>) -> Result<Type, Erro
3535
Expression::Propagate(e) => check_propagate_type(*e, env),
3636
Expression::ListValue(elements) => check_list_value(&elements, env),
3737
Expression::Constructor(name, args) => check_adt_constructor(name, args, env),
38-
Expression::FuncCall(func_name, exp_vec) => check_func_call(func_name.clone(), exp_vec.clone(), env),
39-
38+
Expression::FuncCall(func_name, exp_vec) => {
39+
check_func_call(func_name.clone(), exp_vec.clone(), env)
40+
}
41+
4042
_ => Err("not implemented yet.".to_string()),
4143
}
4244
}
4345

44-
4546
fn check_func_call(
4647
func_name: Name,
4748
exp_vector: Vec<Expression>,

src/type_checker/statement_type_checker.rs

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::collections::HashSet;
2+
use std::fmt::format;
3+
14
use crate::environment::environment::Environment;
25
use crate::ir::ast::{Expression, Function, Name, Statement, Type, ValueConstructor};
36
use crate::type_checker::expression_type_checker::check_expr;
@@ -20,6 +23,7 @@ pub fn check_stmt(
2023
Statement::For(var, expr, stmt) => check_for_stmt(var, expr, stmt, env),
2124
Statement::FuncDef(function) => check_func_def_stmt(function, env),
2225
Statement::TypeDeclaration(name, cons) => check_adt_declarations_stmt(name, cons, env),
26+
Statement::Block(statements_vector) => check_block_statement(statements_vector, env),
2327
Statement::Return(exp) => check_return_stmt(exp, env),
2428
_ => Err("Not implemented yet".to_string()),
2529
}
@@ -34,6 +38,19 @@ fn check_squence_stmt(
3438
check_stmt(*stmt2, &new_env)
3539
}
3640

41+
fn check_block_statement(
42+
statements_vector: Vec<Statement>,
43+
env: &Environment<Type>,
44+
) -> Result<Environment<Type>, ErrorMessage> {
45+
let mut new_env = env.clone();
46+
new_env.push();
47+
for statement in &statements_vector {
48+
new_env = check_stmt(statement.clone(), &new_env)?;
49+
}
50+
new_env.pop();
51+
return Ok(new_env);
52+
}
53+
3754
fn check_assignment_stmt(
3855
name: Name,
3956
exp: Box<Expression>,
@@ -181,8 +198,30 @@ fn check_func_def_stmt(
181198
function: Function,
182199
env: &Environment<Type>,
183200
) -> Result<Environment<Type>, ErrorMessage> {
184-
let mut new_env = env.clone();
185-
new_env.push();
201+
let mut new_env = Environment::new();
202+
//new_env.push(); -> Push and pop will happen in check_block_statement
203+
new_env.set_current_func(&function.name);
204+
// Previous environment functions and the formal parameters are regarded as global
205+
new_env.set_global_functions(env.get_all_functions());
206+
207+
// Ensure that each function is defined only once
208+
if new_env.globals.functions.contains_key(&function.name) {
209+
return Err(format!(
210+
"Function {} is defined multiple times",
211+
function.name
212+
));
213+
}
214+
215+
// Ensure that no parameter names are repeated in the function's argument list
216+
let mut seen_names = HashSet::new();
217+
for arg in &function.params {
218+
if !seen_names.insert(arg.argument_name.clone()) {
219+
return Err(format!(
220+
"Duplicate parameter name '{}' found in function '{}'",
221+
arg.argument_name, function.name
222+
));
223+
}
224+
}
186225

187226
for formal_arg in function.params.iter() {
188227
new_env.map_variable(
@@ -192,13 +231,15 @@ fn check_func_def_stmt(
192231
);
193232
}
194233

234+
new_env.map_function(function.clone());
195235
if let Some(body) = function.body.clone() {
196-
new_env = check_stmt(*body, &new_env)?;
236+
check_stmt(*body, &new_env)?; //new_env is only used to check function body
197237
}
198-
new_env.pop();
199-
new_env.map_function(function);
238+
//new_env.pop();
200239

201-
Ok(new_env)
240+
let mut final_env = env.clone();
241+
final_env.map_function(function.clone());
242+
Ok(final_env) // if function body is ok, return original env with new function
202243
}
203244

204245
fn check_adt_declarations_stmt(
@@ -215,19 +256,30 @@ fn check_return_stmt(
215256
exp: Box<Expression>,
216257
env: &Environment<Type>,
217258
) -> Result<Environment<Type>, ErrorMessage> {
218-
let mut new_env = env.clone();
259+
let new_env = env.clone();
219260

220261
assert!(new_env.scoped_function());
221262

222263
let ret_type = check_expr(*exp, &new_env)?;
223264

224-
match new_env.lookup(&"return".to_string()) {
225-
Some(_) => Ok(new_env),
226-
None => {
227-
new_env.map_variable("return".to_string(), false, ret_type);
228-
Ok(new_env)
229-
}
265+
let current_func = env.lookup_function(&env.current_func);
266+
267+
if current_func.is_none() {
268+
return Err(format!("Type checker: No function to return from"));
269+
}
270+
271+
let current_func = current_func.unwrap();
272+
273+
if ret_type != current_func.kind {
274+
return Err(format!(
275+
"Error in function {}:
276+
Actual return type cannot be different from formal return type \n
277+
Actual return type: {:?} \n
278+
Formal return type: {:?}",
279+
env.current_func, ret_type, current_func.kind
280+
));
230281
}
282+
return Ok(new_env);
231283
}
232284

233285
fn merge_environments(

0 commit comments

Comments
 (0)