use crate::ast::diagnostic_factories::class_has_no_constructor; use crate::ast::expression::Expression; use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_call::IrCall; use crate::ir::ir_expression::IrExpression; use crate::source_range::SourceRange; use crate::symbol::callable_symbol::CallableSymbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; use crate::types_table::TypesTable; pub struct Call { callee: Box, arguments: Vec, source_range: SourceRange, } impl Call { pub fn new(callee: Expression, arguments: Vec, source_range: SourceRange) -> Self { Self { callee: callee.into(), arguments, source_range, } } pub fn callee(&self) -> &Expression { &self.callee } pub fn arguments(&self) -> Vec<&Expression> { self.arguments.iter().collect() } pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { self.callee.init_scopes(symbol_table, container_scope); for argument in &mut self.arguments { argument.init_scopes(symbol_table, container_scope); } } pub fn check_field_initializer_names( &self, symbol_table: &SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { let mut diagnostics: Vec = Vec::new(); diagnostics.append( &mut self .callee .check_field_initializer_names(symbol_table, class_symbol), ); for argument in &self.arguments { diagnostics .append(&mut argument.check_field_initializer_names(symbol_table, class_symbol)) } diagnostics } pub fn check_constructor_local_names( &self, symbol_table: &SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { let mut diagnostics = Vec::new(); for argument in &self.arguments { diagnostics .append(&mut argument.check_constructor_local_names(symbol_table, class_symbol)) } diagnostics.append( &mut self .callee .check_constructor_local_names(symbol_table, class_symbol), ); diagnostics } pub fn check_method_local_names( &self, symbol_table: &SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { let mut diagnostics = Vec::new(); for argument in &self.arguments { diagnostics.append(&mut argument.check_method_local_names(symbol_table, class_symbol)); } diagnostics.append( &mut self .callee .check_method_local_names(symbol_table, class_symbol), ); diagnostics } pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics: Vec = Vec::new(); for argument in &self.arguments { diagnostics.append(&mut argument.check_static_fn_local_names(symbol_table)); } diagnostics.append(&mut self.callee.check_static_fn_local_names(symbol_table)); diagnostics } pub fn type_check( &mut self, symbol_table: &SymbolTable, types_table: &TypesTable, ) -> Result<(), Vec> { self.callee.as_mut().type_check(symbol_table, types_table)?; let mut diagnostics: Vec = self .arguments .iter_mut() .map(|argument| argument.type_check(symbol_table, types_table)) .filter_map(Result::err) .flatten() .collect(); // check that callee is callable let callable_symbol = match self.callee.type_info(symbol_table, types_table) { TypeInfo::Function(function_symbol) => { CallableSymbol::Function(function_symbol.clone()) } TypeInfo::Class(class_symbol) => match class_symbol.constructor_symbol_owned() { None => { diagnostics.push(class_has_no_constructor( class_symbol.declared_name(), self.callee.source_range(), )); return Err(diagnostics); } Some(constructor_symbol) => CallableSymbol::Constructor(constructor_symbol), }, _ => { diagnostics.push(Diagnostic::new( &format!( "Receiver of type {} is not callable.", self.callee.type_info(symbol_table, types_table) ), self.callee.source_range().start(), self.callee.source_range().end(), )); return Err(diagnostics); } }; // check arguments length let parameters = callable_symbol.parameters(); if parameters.len() != self.arguments.len() { diagnostics.push(Diagnostic::new( &format!( "Wrong number of arguments; expected {} but found {}", parameters.len(), self.arguments.len() ), self.source_range.start(), self.source_range.end(), )); } if !diagnostics.is_empty() { return Err(diagnostics); } // check argument types for i in 0..parameters.len() { let parameter = ¶meters[i]; let argument = &self.arguments[i]; let parameter_type_info = types_table.parameter_types().get(parameter).unwrap(); let argument_type_info = argument.type_info(symbol_table, types_table); if !parameter_type_info.is_assignable_from(argument_type_info) { diagnostics.push(Diagnostic::new( &format!( "Mismatched types: expected {} but found {}", parameter_type_info, argument_type_info ), argument.source_range().start(), argument.source_range().end(), )) } } if diagnostics.is_empty() { Ok(()) } else { Err(diagnostics) } } fn get_callee_symbol(&self, symbol_table: &SymbolTable) -> CallableSymbol { match self.callee() { Expression::Identifier(identifier) => { let expressible_symbol = symbol_table .find_expressible_symbol(identifier.scope_id(), identifier.name()) .unwrap(); match expressible_symbol { ExpressibleSymbol::Function(function_symbol) => { CallableSymbol::Function(function_symbol.clone()) } ExpressibleSymbol::Class(class_symbol) => { match class_symbol.constructor_symbol_owned() { None => { panic!("Attempt to get non-existent constructor symbol") } Some(constructor_symbol) => { CallableSymbol::Constructor(constructor_symbol) } } } _ => panic!("Calling things other than functions not yet supported."), } } _ => panic!("Calling things other than identifiers not yet supported."), } } pub fn return_type_info<'a>( &self, symbol_table: &SymbolTable, types_table: &'a TypesTable, ) -> &'a TypeInfo { match self.get_callee_symbol(symbol_table) { CallableSymbol::Function(function_symbol) => types_table .function_return_types() .get(&function_symbol) .unwrap(), CallableSymbol::Constructor(constructor_symbol) => types_table .constructor_return_types() .get(&constructor_symbol) .unwrap(), } } pub fn to_ir( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, ) -> IrCall { let arguments: Vec = self .arguments .iter() .map(|argument| argument.to_ir_expression(builder, symbol_table, types_table)) .inspect(|expression| { if expression.is_none() { panic!("Attempt to pass non-expression") } }) .map(Option::unwrap) .collect(); let callable_symbol = self.get_callee_symbol(symbol_table); match callable_symbol { CallableSymbol::Function(function_symbol) => IrCall::new( fqn_parts_to_string(function_symbol.fqn_parts()), arguments, function_symbol.is_extern(), ), CallableSymbol::Constructor(constructor_symbol) => IrCall::new( fqn_parts_to_string(constructor_symbol.fqn_parts()), arguments, false, ), } } pub fn source_range(&self) -> &SourceRange { &self.source_range } }