335 lines
12 KiB
Rust
335 lines
12 KiB
Rust
use crate::ast::diagnostic_factories::{
|
|
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::ast::ir_builder::IrBuilder;
|
|
use crate::ast::ir_util::get_or_init_field_pointer_variable;
|
|
use crate::diagnostic::Diagnostic;
|
|
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::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::rc::Rc;
|
|
|
|
pub struct Identifier {
|
|
name: String,
|
|
source_range: SourceRange,
|
|
scope_id: Option<usize>,
|
|
}
|
|
|
|
impl Identifier {
|
|
pub fn new(name: &str, source_range: SourceRange) -> Self {
|
|
Self {
|
|
name: name.into(),
|
|
source_range,
|
|
scope_id: None,
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
/// Check against recursively constructing this class.
|
|
fn check_self_constructor_use(
|
|
&self,
|
|
context_class_symbol: &ClassSymbol,
|
|
class_symbol: &ClassSymbol,
|
|
) -> Option<Diagnostic> {
|
|
// 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.
|
|
fn check_self_or_outer_field_use(
|
|
&self,
|
|
context_class_symbol: &ClassSymbol,
|
|
field_symbol: &FieldSymbol,
|
|
) -> Option<Diagnostic> {
|
|
// 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.
|
|
fn check_self_or_outer_method_use(
|
|
&self,
|
|
context_class_symbol: &ClassSymbol,
|
|
function_symbol: &FunctionSymbol,
|
|
) -> Option<Diagnostic> {
|
|
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
|
|
}
|
|
}
|
|
|
|
pub fn check_name_as_field_initializer(
|
|
&self,
|
|
symbol_table: &SymbolTable,
|
|
context_class_symbol: &ClassSymbol,
|
|
) -> Option<Diagnostic> {
|
|
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))
|
|
}
|
|
}
|
|
|
|
pub fn check_constructor_destination_name(
|
|
&self,
|
|
symbol_table: &SymbolTable,
|
|
class_symbol: &ClassSymbol,
|
|
) -> Option<Diagnostic> {
|
|
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))
|
|
}
|
|
}
|
|
|
|
pub fn check_constructor_local_name(
|
|
&self,
|
|
symbol_table: &SymbolTable,
|
|
context_class_symbol: &ClassSymbol,
|
|
) -> Option<Diagnostic> {
|
|
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))
|
|
}
|
|
}
|
|
|
|
pub fn check_method_local_name(
|
|
&self,
|
|
symbol_table: &SymbolTable,
|
|
context_class_symbol: &ClassSymbol,
|
|
) -> Option<Diagnostic> {
|
|
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.
|
|
pub fn check_static_fn_local_name(&self, symbol_table: &SymbolTable) -> Option<Diagnostic> {
|
|
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 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 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())
|
|
}
|
|
}
|
|
}
|
|
}
|