use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_util::get_or_init_field_pointer_variable; use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes}; use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::diagnostic_factories::{ cannot_reassign_immutable_field, not_assignable, outer_class_field_usage, outer_class_method_usage, self_constructor_used_in_init, self_field_used_in_init, self_method_used_in_init, symbol_not_found, }; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_expression::IrExpression; use crate::ir::ir_operation::IrOperation; use crate::ir::ir_read_field::IrReadField; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; use crate::types_table::TypesTable; use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; pub struct Identifier { node_id: NodeId, name: Rc, source_range: SourceRange, scope_id: Option, } impl Identifier { pub fn new(node_id: NodeId, name: &str, source_range: SourceRange) -> Self { Self { node_id, name: name.into(), source_range, scope_id: None, } } pub fn node_id(&self) -> NodeId { self.node_id } pub fn name(&self) -> &str { &self.name } pub fn init_scope_id(&mut self, container_scope: usize) { self.scope_id = Some(container_scope); } pub fn scope_id(&self) -> usize { self.scope_id.unwrap() } pub fn resolve_name_static(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut names_table = NodesToSymbols::new(); match symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name) { None => { diagnostics.push(symbol_not_found(&self.name, &self.source_range)); } Some(expressible_symbol) => { names_table.insert(self.node_id, expressible_symbol.into_symbol()); } } (names_table, diagnostics) } pub fn resolve_name_field_init( &self, symbol_table: &SymbolTable, self_class_symbol: &ClassSymbol, ) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut names_table = NodesToSymbols::new(); let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(class_symbol) => { self.init_referring_to_class( self_class_symbol, &class_symbol, &mut names_table, &mut diagnostics, ); } ExpressibleSymbol::Field(field_symbol) => { self.init_referring_to_field( self_class_symbol, &field_symbol, &mut diagnostics, ); } ExpressibleSymbol::Function(function_symbol) => { self.init_referring_to_function( self_class_symbol, &function_symbol, &mut names_table, &mut diagnostics, ); } ExpressibleSymbol::Parameter(_) => { // Cannot get here, because classes cannot currently be declared in functions unreachable!() } ExpressibleSymbol::Variable(_) => { // Cannot get here, as classes cannot currently be declared in functions unreachable!() } } } else { diagnostics.push(symbol_not_found(&self.name, &self.source_range)); } (names_table, diagnostics) } pub fn resolve_name_ctor( &self, symbol_table: &SymbolTable, self_class_symbol: &ClassSymbol, ) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut names_table = NodesToSymbols::new(); let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(class_symbol) => { self.init_referring_to_class( self_class_symbol, &class_symbol, &mut names_table, &mut diagnostics, ); } ExpressibleSymbol::Field(field_symbol) => { self.init_referring_to_field( self_class_symbol, &field_symbol, &mut diagnostics, ); } ExpressibleSymbol::Function(function_symbol) => { self.init_referring_to_function( self_class_symbol, &function_symbol, &mut names_table, &mut diagnostics, ); } ExpressibleSymbol::Parameter(parameter_symbol) => { names_table.insert(self.node_id, Symbol::Parameter(parameter_symbol)); } ExpressibleSymbol::Variable(variable_symbol) => { names_table.insert(self.node_id, Symbol::Variable(variable_symbol)); } } } else { diagnostics.push(symbol_not_found(&self.name, &self.source_range)); } (names_table, diagnostics) } pub fn resolve_name_ctor_destination( &self, symbol_table: &SymbolTable, initialized_fields: &mut HashSet>, ) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut names_table = NodesToSymbols::new(); let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(_) => { // error diagnostics.push(not_assignable(&self.name, &self.source_range)); } ExpressibleSymbol::Field(field_symbol) => { // ok if field has not been initialized yet OR field is mutable if !initialized_fields.contains(&self.name) { initialized_fields.insert(self.name.clone()); names_table.insert(self.node_id, Symbol::Field(field_symbol)); } else if !field_symbol.is_mut() { // error since we are trying to reassign an immutable field diagnostics.push(cannot_reassign_immutable_field(&self.source_range)); } else { // mut is ok names_table.insert(self.node_id, Symbol::Field(field_symbol)); } } ExpressibleSymbol::Function(_) => { diagnostics.push(not_assignable(&self.name, &self.source_range)); } ExpressibleSymbol::Parameter(_) => { // assigning to parameter is an error // we may in the future allow mut on parameters, but it's probably pointless diagnostics.push(not_assignable(&self.name, &self.source_range)); } ExpressibleSymbol::Variable(variable_symbol) => { // ok names_table.insert(self.node_id, Symbol::Variable(variable_symbol)); } } } else { diagnostics.push(symbol_not_found(&self.name, &self.source_range)); } (names_table, diagnostics) } pub fn resolve_name_method( &self, symbol_table: &SymbolTable, _self_class_symbol: &ClassSymbol, // for future when we have paths? ) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut nodes_to_symbols = NodesToSymbols::new(); let maybe_expressible_symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); match maybe_expressible_symbol { None => { diagnostics.push(symbol_not_found(&self.name, &self.source_range)); } Some(expressible_symbol) => { nodes_to_symbols.insert(self.node_id, expressible_symbol.into_symbol()); } } (nodes_to_symbols, diagnostics) } fn init_referring_to_class( &self, self_class_symbol: &ClassSymbol, class_symbol: &Rc, names_table: &mut NodesToSymbols, diagnostics: &mut Diagnostics, ) { // Check against recursively constructing this class. // This is not future-proof, as we will eventually allow reference to the self class, which // would (theoretically) be assigned to an instance field. if self_class_symbol == class_symbol.as_ref() { diagnostics.push(self_constructor_used_in_init(&self.source_range)); } else { names_table.insert(self.node_id, Symbol::Class(class_symbol.clone())); } } fn init_referring_to_field( &self, self_class_symbol: &ClassSymbol, field_symbol: &FieldSymbol, diagnostics: &mut Diagnostics, ) { if self_class_symbol .fields() .contains_key(field_symbol.declared_name()) { diagnostics.push(self_field_used_in_init(&self.source_range)); } else { diagnostics.push(outer_class_field_usage(&self.source_range)); } } fn init_referring_to_function( &self, self_class_symbol: &ClassSymbol, function_symbol: &Rc, names_table: &mut NodesToSymbols, diagnostics: &mut Diagnostics, ) { if self_class_symbol .functions() .contains_key(function_symbol.declared_name()) { diagnostics.push(self_method_used_in_init(&self.source_range)); } else if function_symbol.is_method() { diagnostics.push(outer_class_method_usage(&self.source_range)); } else { names_table.insert(self.node_id, Symbol::Function(function_symbol.clone())); } } /// Check against recursively constructing this class. #[deprecated] fn check_self_constructor_use( &self, context_class_symbol: &ClassSymbol, class_symbol: &ClassSymbol, ) -> Option { // this is not future-proof, as we will eventually allow reference to the self class, which // would (theoretically) be assigned to an instance field if context_class_symbol == class_symbol { Some(self_constructor_used_in_init(&self.source_range)) } else { None } } /// Check against using this or outer class' bare fields. #[deprecated] fn check_self_or_outer_field_use( &self, context_class_symbol: &ClassSymbol, field_symbol: &FieldSymbol, ) -> Option { // Usage of a bare field will always be an error, whether in this class or an outer class if context_class_symbol .fields() .contains_key(field_symbol.declared_name()) { Some(self_field_used_in_init(&self.source_range)) } else { Some(outer_class_field_usage(&self.source_range)) } } /// Check against using self or outer class methods. #[deprecated] fn check_self_or_outer_method_use( &self, context_class_symbol: &ClassSymbol, function_symbol: &FunctionSymbol, ) -> Option { if context_class_symbol .functions() .contains_key(function_symbol.declared_name()) { // Can only use Self static functions, which we don't have yet Some(self_method_used_in_init(&self.source_range)) } else if function_symbol.is_method() { // Can only use outer class static functions, which we don't have yet Some(outer_class_method_usage(&self.source_range)) } else { None } } #[deprecated] pub fn check_name_as_field_initializer( &self, symbol_table: &SymbolTable, context_class_symbol: &ClassSymbol, ) -> Option { let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(class_symbol) => { self.check_self_constructor_use(context_class_symbol, &class_symbol) } ExpressibleSymbol::Field(field_symbol) => { self.check_self_or_outer_field_use(context_class_symbol, &field_symbol) } ExpressibleSymbol::Function(function_symbol) => { self.check_self_or_outer_method_use(context_class_symbol, &function_symbol) } ExpressibleSymbol::Parameter(_) => { // Cannot get here, because classes cannot currently be declared in functions unreachable!() } ExpressibleSymbol::Variable(_) => { // Cannot get here, as classes cannot currently be declared in functions unreachable!() } } } else { Some(symbol_not_found(&self.name, &self.source_range)) } } #[deprecated] pub fn check_constructor_destination_name( &self, symbol_table: &SymbolTable, class_symbol: &ClassSymbol, ) -> Option { let expressible_symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(expressible_symbol) = expressible_symbol { match expressible_symbol { ExpressibleSymbol::Class(_) => { panic!("Class is not an L value") } ExpressibleSymbol::Field(field_symbol) => { // This is just a stop-gap for now. We need to decide if we are going to do // field assignment analysis (whether it's initialized already, if it's mut, // etc.) during name checking or during type checking. None } ExpressibleSymbol::Function(_) => { panic!("Function is not an L value") } ExpressibleSymbol::Parameter(_) => { panic!("Parameter is not an L value") } ExpressibleSymbol::Variable(variable_symbol) => { // Again, a stop-gap. None } } } else { Some(symbol_not_found(&self.name, &self.source_range)) } } #[deprecated] pub fn check_constructor_local_name( &self, symbol_table: &SymbolTable, context_class_symbol: &ClassSymbol, ) -> Option { let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(class_symbol) => { self.check_self_constructor_use(context_class_symbol, &class_symbol) } ExpressibleSymbol::Field(field_symbol) => { self.check_self_or_outer_field_use(context_class_symbol, &field_symbol) } ExpressibleSymbol::Function(function_symbol) => { self.check_self_or_outer_method_use(context_class_symbol, &function_symbol) } ExpressibleSymbol::Parameter(_) => None, ExpressibleSymbol::Variable(_) => None, } } else { Some(symbol_not_found(&self.name, &self.source_range)) } } #[deprecated] pub fn check_method_local_name( &self, symbol_table: &SymbolTable, context_class_symbol: &ClassSymbol, ) -> Option { let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); if let Some(symbol) = symbol { match symbol { ExpressibleSymbol::Class(_) => { None // all class usages should be ok } ExpressibleSymbol::Field(field_symbol) => { // Must be a reference to a field in this class if context_class_symbol .fields() .contains_key(field_symbol.declared_name()) { None } else { Some(outer_class_field_usage(&self.source_range)) } } ExpressibleSymbol::Function(function_symbol) => { // Must be a method in this class if function_symbol.is_method() && !context_class_symbol .functions() .contains_key(function_symbol.declared_name()) { Some(outer_class_method_usage(&self.source_range)) } else { None } } ExpressibleSymbol::Parameter(_) => { None // ok } ExpressibleSymbol::Variable(_) => { None // ok } } } else { Some(symbol_not_found(&self.name, &self.source_range)) } } /// WARNING: this is not appropriate (yet) for class static functions. #[deprecated] pub fn check_static_fn_local_name(&self, symbol_table: &SymbolTable) -> Option { if symbol_table .find_expressible_symbol(self.scope_id.unwrap(), &self.name) .is_some() { None } else { Some(symbol_not_found(&self.name, &self.source_range)) } } pub fn source_range(&self) -> &SourceRange { &self.source_range } pub fn resolve_type( &self, resolved_symbols: &NodesToSymbols, resolved_symbol_type_infos: &SymbolsToTypes, ) -> (NodesToTypes, Diagnostics) { let self_symbol = resolved_symbols.get(&self.node_id).unwrap(); let type_info = resolved_symbol_type_infos.get(self_symbol).unwrap(); let mut resolved_types = NodesToTypes::new(); resolved_types.insert(self.node_id, type_info.clone()); (resolved_types, Diagnostics::new()) } #[deprecated] pub fn type_info<'a>( &self, symbol_table: &SymbolTable, types_table: &'a TypesTable, ) -> &'a TypeInfo { let expressible_symbol = symbol_table .find_expressible_symbol(self.scope_id.unwrap(), &self.name) .unwrap(); match expressible_symbol { ExpressibleSymbol::Class(class_symbol) => { types_table.class_types().get(&class_symbol).unwrap() } ExpressibleSymbol::Field(field_symbol) => { types_table.field_types().get(&field_symbol).unwrap() } ExpressibleSymbol::Function(function_symbol) => types_table .function_types() .get(&function_symbol) .expect(&format!( "Unable to get function type for {:?}", function_symbol )), ExpressibleSymbol::Parameter(parameter_symbol) => types_table .parameter_types() .get(¶meter_symbol) .unwrap(), ExpressibleSymbol::Variable(variable_symbol) => { types_table.variable_types().get(&variable_symbol).unwrap() } } } pub fn lower_to_ir_expression( &self, builder: &mut IrBuilder, nodes_to_symbols: &NodesToSymbols, symbols_to_types: &SymbolsToTypes, ) -> IrExpression { let symbol = nodes_to_symbols.get(&self.node_id).unwrap(); match &symbol.unwrap_expressible_symbol() { ExpressibleSymbol::Class(_class_symbol) => { todo!() } ExpressibleSymbol::Field(field_symbol) => { let field_type = symbols_to_types.get(symbol).unwrap(); let read_destination = Rc::new(RefCell::new(IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), field_type, ))); let ir_read_field = IrReadField::new( get_or_init_field_pointer_variable(builder, field_symbol, field_type).clone(), ); builder .current_block_mut() .add_statement(IrStatement::Assign(IrAssign::new( read_destination.clone(), IrOperation::ReadField(ir_read_field), ))); IrExpression::Variable(read_destination) } ExpressibleSymbol::Function(_function_symbol) => { todo!() } ExpressibleSymbol::Parameter(parameter_symbol) => IrExpression::Parameter( builder .parameters_map() .get(parameter_symbol) .unwrap() .clone(), ), ExpressibleSymbol::Variable(variable_symbol) => IrExpression::Variable( builder .local_variables() .get(variable_symbol) .unwrap() .clone(), ), } } pub fn ir_expression( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, ) -> IrExpression { let expressible_symbol = symbol_table .find_expressible_symbol(self.scope_id.unwrap(), &self.name) .unwrap(); match expressible_symbol { ExpressibleSymbol::Class(class_symbol) => { todo!() } ExpressibleSymbol::Field(field_symbol) => { let field_type = types_table.field_types().get(&field_symbol).unwrap(); let read_destination = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), field_type, ); let read_destination_as_rc = Rc::new(RefCell::new(read_destination)); let ir_read_field = IrReadField::new( get_or_init_field_pointer_variable(builder, &field_symbol, field_type).clone(), ); builder .current_block_mut() .add_statement(IrStatement::Assign(IrAssign::new( read_destination_as_rc.clone(), IrOperation::ReadField(ir_read_field), ))); IrExpression::Variable(read_destination_as_rc) } ExpressibleSymbol::Function(_) => { panic!("Cannot yet get ir-variable for FunctionSymbol") } ExpressibleSymbol::Parameter(parameter_symbol) => { let parameters_map = builder.parameters_map(); let ir_parameter = parameters_map.get(¶meter_symbol).unwrap(); IrExpression::Parameter(ir_parameter.clone()) } ExpressibleSymbol::Variable(variable_symbol) => { let ir_variable = builder.local_variables().get(&variable_symbol).unwrap(); IrExpression::Variable(ir_variable.clone()) } } } }