274 lines
9.5 KiB
Rust
274 lines
9.5 KiB
Rust
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<Expression>,
|
|
arguments: Vec<Expression>,
|
|
source_range: SourceRange,
|
|
}
|
|
|
|
impl Call {
|
|
pub fn new(callee: Expression, arguments: Vec<Expression>, 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<Diagnostic> {
|
|
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic> {
|
|
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<Diagnostic> {
|
|
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<Diagnostic> {
|
|
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>> {
|
|
self.callee.as_mut().type_check(symbol_table, types_table)?;
|
|
|
|
let mut diagnostics: Vec<Diagnostic> = 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<IrExpression> = 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
|
|
}
|
|
}
|