use crate::ast::expression::Expression; use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::field_symbol::FieldSymbol; use crate::symbol_table::{SymbolInsertError, SymbolTable}; use std::cell::RefCell; use std::rc::Rc; pub struct Field { declared_name: Rc, declared_name_source_range: SourceRange, is_public: bool, is_mut: bool, declared_type: Option>, initializer: Option>, field_symbol: Option>>, } impl Field { pub fn new( declared_name: &str, declared_name_source_range: SourceRange, is_public: bool, is_mut: bool, declared_type: Option, initializer: Option, ) -> Self { Self { declared_name: declared_name.into(), declared_name_source_range, is_public, is_mut, declared_type: declared_type.map(Box::new), initializer: initializer.map(Box::new), field_symbol: None, } } pub fn declared_name(&self) -> &str { &self.declared_name } pub fn declared_name_source_range(&self) -> &SourceRange { &self.declared_name_source_range } pub fn initializer(&self) -> Option<&Expression> { self.initializer.as_ref().map(Box::as_ref) } pub fn gather_declared_names( &mut self, symbol_table: &mut SymbolTable, field_index: usize, ) -> Result<(), Vec> { // 1. insert field symbol let to_insert = FieldSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), self.is_mut, ); let field_symbol = symbol_table .insert_field_symbol(to_insert) .map_err(|e| match e { SymbolInsertError::AlreadyDeclared(already_declared) => { vec![Diagnostic::new( &format!( "Symbol {} already declared in current scope.", already_declared.symbol().borrow().declared_name() ), self.declared_name_source_range.start(), self.declared_name_source_range.end(), )] } })?; // save for later self.field_symbol = Some(field_symbol); // set field index on symbol self.field_symbol .as_ref() .unwrap() .borrow_mut() .set_field_index(field_index); // 2. gather type_use and initializer, if present if let Some(type_use) = &mut self.declared_type { type_use.gather_declared_names(symbol_table)?; } if let Some(initializer) = &mut self.initializer { initializer.gather_declared_names(symbol_table)?; } Ok(()) } pub fn check_name_usages( &mut self, symbol_table: &SymbolTable, class_context: Option<&Rc>>, ) -> Result<(), Vec> { if let Some(type_use) = &mut self.declared_type { type_use.check_name_usages(symbol_table, class_context)?; } // This is going to get hairy, because users might attempt to use a field in an initializer // (for either this field, or another one) before it's actually initialized. As such, we // need a way to prevent lookup of current class' fields in the initializer. // For now, the following is okay so long as we don't start referencing things in the // initializers. if let Some(initializer) = self.initializer.as_mut() { initializer.check_name_usages(symbol_table)?; } Ok(()) } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; if let Some(type_use) = &mut self.declared_type { if let Some(mut type_use_diagnostics) = type_use.type_check(symbol_table).err() { diagnostics.append(&mut type_use_diagnostics); } } if let Some(initializer) = &mut self.initializer { if let Some(mut initializer_diagnostics) = initializer.type_check(symbol_table).err() { diagnostics.append(&mut initializer_diagnostics); } } if !diagnostics.is_empty() { return Err(diagnostics); } // Now check that types are assignable, and update field symbol's type info let field_type_info = match self.declared_type.as_ref() { Some(type_use) => { match self.initializer.as_ref() { Some(initializer) => { let initializer_type_info = initializer.type_info(); let declared_type_info = type_use.type_info(); if declared_type_info.is_assignable_from(initializer_type_info) { declared_type_info } else { return Err(vec![ Diagnostic::new( &format!( "Mismatched types: {} is not assignable to {}", initializer_type_info, declared_type_info ), initializer.source_range().start(), initializer.source_range().end(), ) .with_reporter(file!(), line!()), ]); } } None => { // easy: the declared type type_use.type_info() } } } None => { // type is the initializer match self.initializer.as_ref() { Some(initializer) => initializer.type_info(), None => { // this is an error return Err(vec![Diagnostic::new( "Field must have either a declared type or initializer, or both.", self.declared_name_source_range.start(), self.declared_name_source_range.end(), ).with_reporter(file!(), line!())]); } } } }; // update field symbol's type info self.field_symbol .as_mut() .unwrap() .borrow_mut() .set_type_info(field_type_info.clone()); if diagnostics.is_empty() { Ok(()) } else { Err(diagnostics) } } pub fn field_symbol(&self) -> &Rc> { self.field_symbol.as_ref().unwrap() } }