Refactor class ast node.
This commit is contained in:
parent
7665a92678
commit
8724c07ae2
@ -1,3 +1,4 @@
|
|||||||
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
use crate::ast::constructor::Constructor;
|
use crate::ast::constructor::Constructor;
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
use crate::ast::field::Field;
|
use crate::ast::field::Field;
|
||||||
@ -5,18 +6,24 @@ use crate::ast::fqn_context::FqnContext;
|
|||||||
use crate::ast::fqn_util::fqn_parts_to_string;
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
use crate::ast::function::Function;
|
use crate::ast::function::Function;
|
||||||
use crate::ast::generic_parameter::GenericParameter;
|
use crate::ast::generic_parameter::GenericParameter;
|
||||||
|
use crate::ast::helpers::{
|
||||||
|
collect_diagnostics_mut, collect_diagnostics_single, map_symbol_insert_result,
|
||||||
|
resolve_ctor_name,
|
||||||
|
};
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::diagnostic::{Diagnostic, SecondaryLabel};
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::error_codes::{FIELD_MULTIPLE_INIT, FIELD_UNINIT};
|
||||||
use crate::ir::ir_class::{IrClass, IrField};
|
use crate::ir::ir_class::{IrClass, IrField};
|
||||||
use crate::ir::ir_function::IrFunction;
|
use crate::ir::ir_function::IrFunction;
|
||||||
use crate::maybe_return_diagnostics;
|
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
use crate::symbol::field_symbol::FieldSymbol;
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
||||||
use crate::symbol::Symbol;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::{ok_or_err_diagnostics, push_ok_or_push_errs};
|
||||||
use std::cell::RefCell;
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -39,7 +46,7 @@ impl Class {
|
|||||||
fields: Vec<Field>,
|
fields: Vec<Field>,
|
||||||
functions: Vec<Function>,
|
functions: Vec<Function>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Class {
|
Self {
|
||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
generic_parameters,
|
generic_parameters,
|
||||||
@ -50,284 +57,419 @@ impl Class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a [ClassSymbol] representing this class.
|
||||||
|
fn make_class_symbol(&self, fqn_context: &FqnContext) -> ClassSymbol {
|
||||||
|
ClassSymbol::new(
|
||||||
|
&self.declared_name,
|
||||||
|
self.declared_name_source_range.clone(),
|
||||||
|
fqn_context.resolve(&self.declared_name),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts the given [ClassSymbol] into the `symbol_table`, returning `Ok` if the insert was
|
||||||
|
/// successful, else `Err` with diagnostics.
|
||||||
|
fn insert_class_symbol(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
class_symbol: ClassSymbol,
|
||||||
|
) -> Result<Rc<RefCell<ClassSymbol>>, Vec<Diagnostic>> {
|
||||||
|
map_symbol_insert_result(
|
||||||
|
symbol_table.insert_class_symbol(class_symbol),
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets this class' class symbol to the given symbol.
|
||||||
|
fn set_class_symbol(&mut self, symbol: Rc<RefCell<ClassSymbol>>) {
|
||||||
|
self.class_symbol = Some(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers names of this class' generic parameters, returning `Ok` if all
|
||||||
|
/// [GenericParameter]s produced an `Ok(GenericParameterSymbol)`, else `Err`.
|
||||||
|
fn gather_generic_parameters_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<Vec<Rc<RefCell<GenericParameterSymbol>>>, Vec<Diagnostic>> {
|
||||||
|
let mut symbols: Vec<Rc<RefCell<GenericParameterSymbol>>> = vec![];
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
for generic_parameter in &mut self.generic_parameters {
|
||||||
|
push_ok_or_push_errs!(
|
||||||
|
generic_parameter.gather_declared_names(symbol_table),
|
||||||
|
symbols,
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_or_err_diagnostics!(symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [Ref] to this class' [ClassSymbol]. Only safe to call if the class symbol has been
|
||||||
|
/// set.
|
||||||
|
fn use_class_symbol(&self) -> Ref<ClassSymbol> {
|
||||||
|
self.class_symbol.as_ref().unwrap().borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [RefMut] to this class' [ClassSymbol]. Only safe to call if the class symbol has
|
||||||
|
/// been set.
|
||||||
|
fn use_class_symbol_mut(&mut self) -> RefMut<ClassSymbol> {
|
||||||
|
self.class_symbol.as_mut().unwrap().borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the given [GenericParameterSymbol]s on this class' [ClassSymbol].
|
||||||
|
fn set_generic_parameters_on_class_symbol(
|
||||||
|
&mut self,
|
||||||
|
generic_parameter_symbols: Vec<Rc<RefCell<GenericParameterSymbol>>>,
|
||||||
|
) {
|
||||||
|
self.use_class_symbol_mut()
|
||||||
|
.set_generic_parameters(generic_parameter_symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Ok` of [FieldSymbol]s if all of this class' fields [Field::gather_declared_names]
|
||||||
|
/// methods return `Ok`, else `Err` of [Diagnostic]s.
|
||||||
|
fn gather_fields_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<Vec<Rc<RefCell<FieldSymbol>>>, Vec<Diagnostic>> {
|
||||||
|
let mut symbols: Vec<Rc<RefCell<FieldSymbol>>> = vec![];
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
for (field_index, field) in self.fields.iter_mut().enumerate() {
|
||||||
|
push_ok_or_push_errs!(
|
||||||
|
field.gather_declared_names(symbol_table, field_index),
|
||||||
|
symbols,
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_or_err_diagnostics!(symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the given `field_symbols` [Vec] into a [HashMap] of field names to [FieldSymbol]s.
|
||||||
|
fn make_fields_hash_map(
|
||||||
|
field_symbols: Vec<Rc<RefCell<FieldSymbol>>>,
|
||||||
|
) -> HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> {
|
||||||
|
HashMap::from_iter(
|
||||||
|
field_symbols
|
||||||
|
.into_iter()
|
||||||
|
.map(|field| {
|
||||||
|
let name = field.borrow().declared_name_owned();
|
||||||
|
(name, field)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the field symbols property on the class' [ClassSymbol].
|
||||||
|
fn set_field_symbols_on_class_symbol(&mut self, field_symbols: Vec<Rc<RefCell<FieldSymbol>>>) {
|
||||||
|
self.use_class_symbol_mut()
|
||||||
|
.set_fields(Self::make_fields_hash_map(field_symbols))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [ConstructorSymbol] representing a default constructor appropriate for this class.
|
||||||
|
fn make_default_constructor(&self, fqn_context: &FqnContext) -> ConstructorSymbol {
|
||||||
|
ConstructorSymbol::new(
|
||||||
|
self.declared_name_source_range.clone(),
|
||||||
|
resolve_ctor_name(fqn_context),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers names in the class' [Constructor] and returns a [ConstructorSymbol], if the
|
||||||
|
/// constructor is present; else returns a symbol defining a default constructor.
|
||||||
|
fn gather_constructor_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
fqn_context: &FqnContext,
|
||||||
|
) -> Result<Rc<RefCell<ConstructorSymbol>>, Vec<Diagnostic>> {
|
||||||
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
constructor.gather_declared_names(symbol_table, fqn_context)
|
||||||
|
} else {
|
||||||
|
Ok(Rc::new(RefCell::new(
|
||||||
|
self.make_default_constructor(fqn_context),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the constructor symbol on this class' [ClassSymbol].
|
||||||
|
fn set_constructor_symbol_on_class_symbol(
|
||||||
|
&mut self,
|
||||||
|
constructor_symbol: Rc<RefCell<ConstructorSymbol>>,
|
||||||
|
) {
|
||||||
|
self.use_class_symbol_mut()
|
||||||
|
.set_constructor_symbol(constructor_symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers function names and returns `Ok` if no diagnostics, else `Err`.
|
||||||
|
fn gather_functions_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
fqn_context: &FqnContext,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_mut(&mut self.functions, |f| {
|
||||||
|
f.gather_declared_names(symbol_table, fqn_context, self.class_symbol.as_ref())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_declared_names(
|
pub fn gather_declared_names(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
fqn_context: &mut FqnContext,
|
fqn_context: &mut FqnContext,
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
// 1. insert class symbol
|
// 1. Make and insert class symbol
|
||||||
let to_insert = ClassSymbol::new(
|
let to_insert = self.make_class_symbol(fqn_context);
|
||||||
&self.declared_name,
|
let class_symbol = self.insert_class_symbol(symbol_table, to_insert)?;
|
||||||
self.declared_name_source_range.clone(),
|
self.set_class_symbol(class_symbol);
|
||||||
fqn_context.resolve(&self.declared_name),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 1a. Push class name on fqn
|
// 2. Push class name on fqn context
|
||||||
fqn_context.push(self.declared_name.clone());
|
fqn_context.push(self.declared_name.clone());
|
||||||
|
|
||||||
let class_symbol = symbol_table
|
// 3. push scope
|
||||||
.insert_class_symbol(to_insert)
|
|
||||||
.map_err(|e| match e {
|
|
||||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
|
||||||
let symbol = already_declared.symbol().borrow();
|
|
||||||
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(),
|
|
||||||
)
|
|
||||||
.with_secondary_labels(&[SecondaryLabel::new(
|
|
||||||
symbol.declared_name_source_range().start(),
|
|
||||||
symbol.declared_name_source_range().end(),
|
|
||||||
Some("Symbol declared here.".to_string()),
|
|
||||||
)])
|
|
||||||
.with_reporter(file!(), line!()),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// save symbol for later
|
|
||||||
self.class_symbol = Some(class_symbol);
|
|
||||||
|
|
||||||
// 2. push scope
|
|
||||||
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
||||||
|
|
||||||
// 3a. gather generic parameters
|
// 4. gather generic parameters
|
||||||
let mut generic_parameter_symbols: Vec<Rc<RefCell<GenericParameterSymbol>>> = vec![];
|
let generic_parameter_symbols = self
|
||||||
let mut generic_parameter_diagnostics: Vec<Diagnostic> = vec![];
|
.gather_generic_parameters_names(symbol_table)
|
||||||
for generic_parameter in &mut self.generic_parameters {
|
.inspect_err(|_| {
|
||||||
match generic_parameter.gather_declared_names(symbol_table) {
|
symbol_table.pop_scope();
|
||||||
Ok(generic_parameter_symbol) => {
|
fqn_context.pop();
|
||||||
generic_parameter_symbols.push(generic_parameter_symbol);
|
})?;
|
||||||
}
|
self.set_generic_parameters_on_class_symbol(generic_parameter_symbols);
|
||||||
Err(mut d) => {
|
|
||||||
generic_parameter_diagnostics.append(&mut d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maybe_return_diagnostics!(generic_parameter_diagnostics);
|
// 5. gather fields
|
||||||
// save generics to class symbol
|
let field_symbols = self.gather_fields_names(symbol_table).inspect_err(|_| {
|
||||||
self.class_symbol
|
symbol_table.pop_scope();
|
||||||
.as_mut()
|
fqn_context.pop();
|
||||||
.unwrap()
|
})?;
|
||||||
.borrow_mut()
|
self.set_field_symbols_on_class_symbol(field_symbols);
|
||||||
.set_generic_parameters(generic_parameter_symbols);
|
|
||||||
|
|
||||||
// 3b. gather fields
|
// 6. gather constructor
|
||||||
let mut field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> = HashMap::new();
|
let constructor_symbol = self
|
||||||
let mut fields_diagnostics: Vec<Diagnostic> = vec![];
|
.gather_constructor_names(symbol_table, fqn_context)
|
||||||
|
.inspect_err(|_| {
|
||||||
|
symbol_table.pop_scope();
|
||||||
|
fqn_context.pop();
|
||||||
|
})?;
|
||||||
|
self.set_constructor_symbol_on_class_symbol(constructor_symbol);
|
||||||
|
|
||||||
for (index, field) in self.fields.iter_mut().enumerate() {
|
// 7. gather functions
|
||||||
match field.gather_declared_names(symbol_table, index) {
|
self.gather_functions_names(symbol_table, fqn_context)
|
||||||
Ok(field_symbol) => {
|
.inspect_err(|_| {
|
||||||
field_symbols.insert(field.declared_name_owned(), field_symbol);
|
symbol_table.pop_scope();
|
||||||
}
|
fqn_context.pop();
|
||||||
Err(mut d) => {
|
})?;
|
||||||
fields_diagnostics.append(&mut d);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if !fields_diagnostics.is_empty() {
|
// 8. pop scope
|
||||||
return Err(fields_diagnostics);
|
|
||||||
} else {
|
|
||||||
self.class_symbol
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut()
|
|
||||||
.set_fields(field_symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. gather constructor
|
|
||||||
if let Some(constructor) = &mut self.constructor {
|
|
||||||
let constructor_symbol =
|
|
||||||
constructor.gather_declared_names(symbol_table, fqn_context)?;
|
|
||||||
self.class_symbol
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut()
|
|
||||||
.set_constructor_symbol(Some(constructor_symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. gather functions
|
|
||||||
// note: for each function, insert at index 0 a self parameter
|
|
||||||
let functions_diagnostics: Vec<Diagnostic> = self
|
|
||||||
.functions
|
|
||||||
.iter_mut()
|
|
||||||
.map(|function| {
|
|
||||||
function.gather_declared_names(
|
|
||||||
symbol_table,
|
|
||||||
fqn_context,
|
|
||||||
self.class_symbol.as_ref(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !functions_diagnostics.is_empty() {
|
|
||||||
return Err(functions_diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. pop scope
|
|
||||||
symbol_table.pop_scope();
|
symbol_table.pop_scope();
|
||||||
|
|
||||||
// 7. pop fqn part
|
// 9. pop fqn part
|
||||||
fqn_context.pop();
|
fqn_context.pop();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
fn check_generics_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
let generics_diagnostics: Vec<Diagnostic> = self
|
collect_diagnostics_mut(&mut self.generic_parameters, |gp| {
|
||||||
.generic_parameters
|
gp.check_name_usages(symbol_table, self.class_symbol.as_ref())
|
||||||
.iter_mut()
|
|
||||||
.map(|generic_parameter| {
|
|
||||||
generic_parameter.check_name_usages(symbol_table, self.class_symbol.as_ref())
|
|
||||||
})
|
})
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
maybe_return_diagnostics!(generics_diagnostics);
|
|
||||||
|
|
||||||
self.constructor
|
|
||||||
.as_mut()
|
|
||||||
.map(|constructor| {
|
|
||||||
constructor.check_name_usages(symbol_table, self.class_symbol.as_ref())
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let fields_diagnostics: Vec<Diagnostic> = self
|
|
||||||
.fields
|
|
||||||
.iter_mut()
|
|
||||||
.map(|field| field.check_name_usages(symbol_table, self.class_symbol.as_ref()))
|
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
if !fields_diagnostics.is_empty() {
|
|
||||||
return Err(fields_diagnostics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the [Constructor]'s name usages if present.
|
||||||
|
fn check_constructor_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
if let Some(constructor) = &mut self.constructor {
|
if let Some(constructor) = &mut self.constructor {
|
||||||
constructor.check_name_usages(symbol_table, self.class_symbol.as_ref())?;
|
constructor.check_name_usages(symbol_table, self.class_symbol.as_ref())?
|
||||||
}
|
}
|
||||||
|
|
||||||
let functions_diagnostics: Vec<Diagnostic> = self
|
|
||||||
.functions
|
|
||||||
.iter_mut()
|
|
||||||
.map(|function| function.check_name_usages(symbol_table, self.class_symbol.as_ref()))
|
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if functions_diagnostics.is_empty() {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(functions_diagnostics)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
fn check_fields_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
collect_diagnostics_mut(&mut self.fields, |f| {
|
||||||
|
f.check_name_usages(symbol_table, self.class_symbol.as_ref())
|
||||||
self.fields
|
})
|
||||||
.iter_mut()
|
|
||||||
.map(|field| field.type_check(symbol_table))
|
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
|
||||||
|
|
||||||
if !diagnostics.is_empty() {
|
|
||||||
return Err(diagnostics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_functions_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_mut(&mut self.functions, |f| {
|
||||||
|
f.check_name_usages(symbol_table, self.class_symbol.as_ref())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.check_generics_names(symbol_table)?;
|
||||||
|
self.check_constructor_names(symbol_table)?;
|
||||||
|
self.check_fields_names(symbol_table)?;
|
||||||
|
self.check_functions_names(symbol_table)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_generics(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_mut(&mut self.generic_parameters, |gp| {
|
||||||
|
gp.type_check(symbol_table)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_fields(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_mut(&mut self.fields, |f| f.type_check(symbol_table))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_constructor(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
if let Some(constructor) = &mut self.constructor {
|
if let Some(constructor) = &mut self.constructor {
|
||||||
match constructor.type_check(symbol_table) {
|
constructor.type_check(symbol_table)?;
|
||||||
Ok(_) => {}
|
|
||||||
Err(mut constructor_diagnostics) => {
|
|
||||||
diagnostics.append(&mut constructor_diagnostics);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !diagnostics.is_empty() {
|
fn type_check_functions(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
return Err(diagnostics);
|
collect_diagnostics_mut(&mut self.functions, |f| f.type_check(symbol_table))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.functions
|
/// Returns all field names with declared initializers.
|
||||||
.iter_mut()
|
fn field_names_with_initializers(&self) -> HashSet<&str> {
|
||||||
.map(|function| function.type_check(symbol_table))
|
let mut set: HashSet<&str> = HashSet::new();
|
||||||
.filter_map(Result::err)
|
|
||||||
.flatten()
|
|
||||||
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
|
||||||
|
|
||||||
if !diagnostics.is_empty() {
|
|
||||||
return Err(diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to determine if fields are initialized or not (the latter is an error).
|
|
||||||
// First phase: check all fields, then check constructor, leaving pending those things that
|
|
||||||
// are fields <- initialized by constructor. Then circle back to fields and check all are
|
|
||||||
// initialized
|
|
||||||
let mut initialized_field_names: HashSet<&str> = HashSet::new();
|
|
||||||
|
|
||||||
for field in &self.fields {
|
for field in &self.fields {
|
||||||
if field.initializer().is_some() {
|
if field.initializer().is_some() {
|
||||||
initialized_field_names.insert(field.declared_name());
|
set.insert(field.declared_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the destination of the given [AssignStatement] matches a field, returns an
|
||||||
|
/// `Ok(Some(field_name))` only if the field is not already in the `fields_already_init` set,
|
||||||
|
/// AND, if the field is immutable, the field is not initialized more than once in the
|
||||||
|
/// constructor. Otherwise, returns an `Err(Diagnostic)`.
|
||||||
|
fn check_ctor_assign_statement<'a>(
|
||||||
|
&self,
|
||||||
|
assign_statement: &'a AssignStatement,
|
||||||
|
fields_already_init: &HashSet<&&str>,
|
||||||
|
) -> Result<Option<&'a str>, Diagnostic> {
|
||||||
|
match assign_statement.destination() {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
// find matching field symbol, if there is one
|
||||||
|
if let Some(field_symbol) = self.use_class_symbol().fields().get(identifier.name())
|
||||||
|
{
|
||||||
|
// check that we don't init more than once IF field is immutable
|
||||||
|
if fields_already_init.contains(&identifier.name())
|
||||||
|
&& !field_symbol.borrow().is_mut()
|
||||||
|
{
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&format!("Immutable field {} cannot be initialized more than once in constructor.", identifier.name()),
|
||||||
|
identifier.source_range().start(),
|
||||||
|
identifier.source_range().end(),
|
||||||
|
).with_reporter(file!(), line!())
|
||||||
|
.with_error_code(FIELD_MULTIPLE_INIT);
|
||||||
|
Err(diagnostic)
|
||||||
|
} else {
|
||||||
|
Ok(Some(identifier.name()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Found a non-L Value destination"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check constructor
|
/// Returns an `Ok(HashSet<&str>)` containing the names of all fields initialized in the
|
||||||
|
/// constructor, provided that the following are true:
|
||||||
|
///
|
||||||
|
/// - The field is not initialized more than once in the constructor
|
||||||
|
/// - The field is not also initialized with a declared initializer.
|
||||||
|
///
|
||||||
|
/// If the above are not met, returns `Err(diagnostics)`.
|
||||||
|
fn get_fields_init_in_ctor<'a>(
|
||||||
|
&self,
|
||||||
|
fields_with_declared_initializers: &HashSet<&str>,
|
||||||
|
) -> Result<HashSet<&str>, Vec<Diagnostic>> {
|
||||||
|
let mut constructor_inits: HashSet<&str> = HashSet::new();
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
if let Some(constructor) = &self.constructor {
|
if let Some(constructor) = &self.constructor {
|
||||||
for statement in constructor.statements() {
|
for statement in constructor.statements() {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::Assign(assign_statement) => match assign_statement.destination() {
|
Statement::Assign(assign_statement) => {
|
||||||
Expression::Identifier(identifier) => {
|
let fields_init_so_far = constructor_inits
|
||||||
if self
|
.union(fields_with_declared_initializers)
|
||||||
.class_symbol
|
.collect::<HashSet<_>>();
|
||||||
.as_ref()
|
match self
|
||||||
.unwrap()
|
.check_ctor_assign_statement(assign_statement, &fields_init_so_far)
|
||||||
.borrow()
|
|
||||||
.fields()
|
|
||||||
.contains_key(identifier.name())
|
|
||||||
{
|
{
|
||||||
initialized_field_names.insert(identifier.name());
|
Ok(maybe_init_field) => match maybe_init_field {
|
||||||
|
None => {}
|
||||||
|
Some(init_field) => {
|
||||||
|
constructor_inits.insert(init_field);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => panic!("Found a non L Value assign lhs"),
|
|
||||||
},
|
},
|
||||||
|
Err(diagnostic) => {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok_or_err_diagnostics!(constructor_inits, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
// check that all fields are present in the hash set
|
/// Checks that all declared fields in this `Class` are present in the `all_inits` set. If so,
|
||||||
for field in &self.fields {
|
/// returns `Ok`, else `Err`.
|
||||||
if !initialized_field_names.contains(field.declared_name()) {
|
fn check_all_fields_in_init_set(
|
||||||
diagnostics.push(
|
&self,
|
||||||
Diagnostic::new(
|
all_inits: &HashSet<&&str>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_single(&self.fields, |field| {
|
||||||
|
if all_inits.contains(&field.declared_name()) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Diagnostic::new(
|
||||||
&format!("Field {} is not initialized.", field.declared_name()),
|
&format!("Field {} is not initialized.", field.declared_name()),
|
||||||
field.declared_name_source_range().start(),
|
field.declared_name_source_range().start(),
|
||||||
field.declared_name_source_range().end(),
|
field.declared_name_source_range().end(),
|
||||||
)
|
)
|
||||||
.with_primary_label_message(
|
.with_primary_label_message("Must be initialized in declaration or constructor.")
|
||||||
"Must be initialized in declaration or constructor.",
|
.with_reporter(file!(), line!())
|
||||||
)
|
.with_error_code(FIELD_UNINIT))
|
||||||
.with_reporter(file!(), line!()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
/// Checks that all fields are initialized, either at their declaration or in the constructor.
|
||||||
|
/// Immutable fields may be only initialized once, either at their declaration or once in the
|
||||||
|
/// constructor. Mutable fields may be initialized either at their declaration, or at least once
|
||||||
|
/// in the constructor.
|
||||||
|
fn check_field_initialization(&self) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
// We need to determine if fields are initialized or not (the latter is an error).
|
||||||
|
// First phase: check all fields, then check constructor, leaving pending those things that
|
||||||
|
// are fields <- initialized by constructor. Then circle back to fields and check all are
|
||||||
|
// initialized
|
||||||
|
let field_names_with_initializers = self.field_names_with_initializers();
|
||||||
|
let field_names_init_in_constructor =
|
||||||
|
self.get_fields_init_in_ctor(&field_names_with_initializers)?;
|
||||||
|
let combined = field_names_with_initializers
|
||||||
|
.union(&field_names_init_in_constructor)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// check that all fields are present in the hash set
|
||||||
|
self.check_all_fields_in_init_set(&combined)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(diagnostics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.type_check_generics(symbol_table)?;
|
||||||
|
self.type_check_fields(symbol_table)?;
|
||||||
|
self.type_check_constructor(symbol_table)?;
|
||||||
|
self.type_check_functions(symbol_table)?;
|
||||||
|
self.check_field_initialization()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec<IrFunction>) {
|
pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec<IrFunction>) {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use crate::ast::class::Class;
|
|||||||
use crate::ast::extern_function::ExternFunction;
|
use crate::ast::extern_function::ExternFunction;
|
||||||
use crate::ast::fqn_context::FqnContext;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
use crate::ast::function::Function;
|
use crate::ast::function::Function;
|
||||||
use crate::ast::helpers::iter_phase;
|
use crate::ast::helpers::collect_diagnostics_into_mut;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::diagnostics_result;
|
use crate::diagnostics_result;
|
||||||
use crate::ir::ir_class::IrClass;
|
use crate::ir::ir_class::IrClass;
|
||||||
@ -49,19 +49,19 @@ impl CompilationUnit {
|
|||||||
let mut fqn_context = FqnContext::new(); // in the future, we'll push the pkg/ns on here
|
let mut fqn_context = FqnContext::new(); // in the future, we'll push the pkg/ns on here
|
||||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.functions,
|
&mut self.functions,
|
||||||
|f| f.gather_declared_names(symbol_table, &fqn_context, None),
|
|f| f.gather_declared_names(symbol_table, &fqn_context, None),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.extern_functions,
|
&mut self.extern_functions,
|
||||||
|ef| ef.gather_declared_names(symbol_table, &fqn_context),
|
|ef| ef.gather_declared_names(symbol_table, &fqn_context),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.classes,
|
&mut self.classes,
|
||||||
|c| c.gather_declared_names(symbol_table, &mut fqn_context),
|
|c| c.gather_declared_names(symbol_table, &mut fqn_context),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
@ -75,19 +75,19 @@ impl CompilationUnit {
|
|||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.functions,
|
&mut self.functions,
|
||||||
|f| f.check_name_usages(symbol_table, None),
|
|f| f.check_name_usages(symbol_table, None),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.extern_functions,
|
&mut self.extern_functions,
|
||||||
|ef| ef.check_name_usages(symbol_table, None),
|
|ef| ef.check_name_usages(symbol_table, None),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.classes,
|
&mut self.classes,
|
||||||
|c| c.check_name_usages(symbol_table),
|
|c| c.check_name_usages(symbol_table),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
@ -99,19 +99,19 @@ impl CompilationUnit {
|
|||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.functions,
|
&mut self.functions,
|
||||||
|f| f.type_check(symbol_table),
|
|f| f.type_check(symbol_table),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.extern_functions,
|
&mut self.extern_functions,
|
||||||
|ef| ef.type_check(symbol_table),
|
|ef| ef.type_check(symbol_table),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
|
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.classes,
|
&mut self.classes,
|
||||||
|c| c.type_check(symbol_table),
|
|c| c.type_check(symbol_table),
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use crate::ast::field::Field;
|
use crate::ast::field::Field;
|
||||||
use crate::ast::fqn_context::FqnContext;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
use crate::ast::fqn_util::fqn_parts_to_string;
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::ast::helpers::resolve_ctor_name;
|
||||||
use crate::ast::ir_builder::IrBuilder;
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
@ -62,7 +63,8 @@ impl Constructor {
|
|||||||
// insert constructor symbol
|
// insert constructor symbol
|
||||||
let to_insert = ConstructorSymbol::new(
|
let to_insert = ConstructorSymbol::new(
|
||||||
self.ctor_keyword_source_range.clone(),
|
self.ctor_keyword_source_range.clone(),
|
||||||
fqn_context.resolve("ctor"), // ctor is a keyword at the language level, should not be callable via normal means
|
resolve_ctor_name(fqn_context),
|
||||||
|
false,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let constructor_symbol =
|
let constructor_symbol =
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::ast::fqn_context::FqnContext;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
use crate::ast::helpers::{gather_oks, iter_phase, map_symbol_insert_result};
|
use crate::ast::helpers::{collect_diagnostics_into_mut, gather_oks, map_symbol_insert_result};
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
@ -127,7 +127,7 @@ impl ExternFunction {
|
|||||||
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
|
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.parameters,
|
&mut self.parameters,
|
||||||
|p| p.check_name_usages(symbol_table, class_context),
|
|p| p.check_name_usages(symbol_table, class_context),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -180,7 +180,7 @@ impl ExternFunction {
|
|||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.parameters,
|
&mut self.parameters,
|
||||||
|p| p.type_check(symbol_table),
|
|p| p.type_check(symbol_table),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use crate::ast::fqn_context::FqnContext;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
use crate::ast::fqn_util::fqn_parts_to_string;
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
use crate::ast::helpers::{
|
use crate::ast::helpers::{
|
||||||
gather_oks, iter_phase, iter_phase_enumerated, map_symbol_insert_result,
|
collect_diagnostics_into_enumerated_mut, collect_diagnostics_into_mut, gather_oks,
|
||||||
|
map_symbol_insert_result,
|
||||||
};
|
};
|
||||||
use crate::ast::ir_builder::IrBuilder;
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
@ -141,7 +142,7 @@ impl Function {
|
|||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
self.return_type.as_mut_slice(),
|
self.return_type.as_mut_slice(),
|
||||||
|type_use| type_use.gather_declared_names(symbol_table),
|
|type_use| type_use.gather_declared_names(symbol_table),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -154,7 +155,7 @@ impl Function {
|
|||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.statements,
|
&mut self.statements,
|
||||||
|s| s.gather_declared_names(symbol_table),
|
|s| s.gather_declared_names(symbol_table),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -198,7 +199,7 @@ impl Function {
|
|||||||
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
|
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.parameters,
|
&mut self.parameters,
|
||||||
|p| p.check_name_usages(symbol_table, class_context),
|
|p| p.check_name_usages(symbol_table, class_context),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -243,7 +244,7 @@ impl Function {
|
|||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.statements,
|
&mut self.statements,
|
||||||
|s| s.check_name_usages(symbol_table),
|
|s| s.check_name_usages(symbol_table),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -282,7 +283,7 @@ impl Function {
|
|||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
iter_phase(
|
collect_diagnostics_into_mut(
|
||||||
&mut self.parameters,
|
&mut self.parameters,
|
||||||
|p| p.type_check(symbol_table),
|
|p| p.type_check(symbol_table),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
@ -309,7 +310,7 @@ impl Function {
|
|||||||
let return_type_info = self.get_return_type_info();
|
let return_type_info = self.get_return_type_info();
|
||||||
let statements_len = self.statements.len();
|
let statements_len = self.statements.len();
|
||||||
|
|
||||||
iter_phase_enumerated(
|
collect_diagnostics_into_enumerated_mut(
|
||||||
&mut self.statements,
|
&mut self.statements,
|
||||||
|i, s| {
|
|i, s| {
|
||||||
let is_last = i == statements_len - 1;
|
let is_last = i == statements_len - 1;
|
||||||
|
|||||||
@ -1,36 +1,77 @@
|
|||||||
|
use crate::ast::fqn_context::FqnContext;
|
||||||
use crate::diagnostic::{Diagnostic, SecondaryLabel};
|
use crate::diagnostic::{Diagnostic, SecondaryLabel};
|
||||||
|
use crate::diagnostics_result;
|
||||||
use crate::error_codes::SYMBOL_ALREADY_DECLARED;
|
use crate::error_codes::SYMBOL_ALREADY_DECLARED;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol_table::SymbolInsertError;
|
use crate::symbol_table::SymbolInsertError;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Iterates through all `ts`, running the `phase` function, and pushing returned `Diagnostic`s
|
/// Iterates through all `ts`, running the `f` function, and pushing returned `Diagnostic`s
|
||||||
/// into `diagnostics`.
|
/// into `diagnostics`.
|
||||||
pub fn iter_phase<T>(
|
pub fn collect_diagnostics_into_mut<T>(
|
||||||
ts: &mut [T],
|
ts: &mut [T],
|
||||||
mut phase: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
|
mut f: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
ts.iter_mut()
|
ts.iter_mut()
|
||||||
.map(|t| phase(t))
|
.map(|t| f(t))
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
.for_each(|d| diagnostics.push(d));
|
.for_each(|d| diagnostics.push(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `iter_phase` but enumerated.
|
/// Like `collect_diagnostics_into` but enumerated.
|
||||||
pub fn iter_phase_enumerated<T>(
|
pub fn collect_diagnostics_into_enumerated_mut<T>(
|
||||||
ts: &mut [T],
|
ts: &mut [T],
|
||||||
mut phase: impl FnMut(usize, &mut T) -> Result<(), Vec<Diagnostic>>,
|
mut f: impl FnMut(usize, &mut T) -> Result<(), Vec<Diagnostic>>,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
ts.iter_mut()
|
ts.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, t)| phase(i, t))
|
.map(|(i, t)| f(i, t))
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
.for_each(|d| diagnostics.push(d));
|
.for_each(|d| diagnostics.push(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn collect_diagnostics_mut<T>(
|
||||||
|
ts: &mut [T],
|
||||||
|
mut f: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let diagnostics = ts
|
||||||
|
.iter_mut()
|
||||||
|
.map(|t| f(t))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_diagnostics<T>(
|
||||||
|
ts: &[T],
|
||||||
|
f: impl Fn(&T) -> Result<(), Vec<Diagnostic>>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let diagnostics = ts
|
||||||
|
.iter()
|
||||||
|
.map(|t| f(t))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_diagnostics_single<T>(
|
||||||
|
ts: &[T],
|
||||||
|
f: impl Fn(&T) -> Result<(), Diagnostic>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let diagnostics = ts
|
||||||
|
.iter()
|
||||||
|
.map(|t| f(t))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map_symbol_insert_result<T>(
|
pub fn map_symbol_insert_result<T>(
|
||||||
insert_result: Result<T, SymbolInsertError>,
|
insert_result: Result<T, SymbolInsertError>,
|
||||||
declared_name_source_range: &SourceRange,
|
declared_name_source_range: &SourceRange,
|
||||||
@ -77,3 +118,7 @@ pub fn gather_oks<T, R>(
|
|||||||
}
|
}
|
||||||
rs
|
rs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_ctor_name(fqn_context: &FqnContext) -> Vec<Rc<str>> {
|
||||||
|
fqn_context.resolve("ctor") // ctor is a keyword at the language level, should not be callable via normal means
|
||||||
|
}
|
||||||
|
|||||||
@ -62,3 +62,24 @@ macro_rules! diagnostics_result {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ok_or_err_diagnostics {
|
||||||
|
( $to_return: expr, $diagnostics: expr ) => {
|
||||||
|
if $diagnostics.is_empty() {
|
||||||
|
Ok($to_return)
|
||||||
|
} else {
|
||||||
|
Err($diagnostics)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! push_ok_or_push_errs {
|
||||||
|
( $result: expr, $oks: expr, $errs: expr ) => {
|
||||||
|
match $result {
|
||||||
|
Ok(inner) => $oks.push(inner),
|
||||||
|
Err(mut errs) => $errs.append(&mut errs),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -7,3 +7,5 @@ pub const ASSIGN_NO_L_VALUE: ErrorCode = 17;
|
|||||||
pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18;
|
pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18;
|
||||||
pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19;
|
pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19;
|
||||||
pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20;
|
pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20;
|
||||||
|
pub const FIELD_MULTIPLE_INIT: ErrorCode = 21;
|
||||||
|
pub const FIELD_UNINIT: ErrorCode = 22;
|
||||||
|
|||||||
@ -53,11 +53,8 @@ impl ClassSymbol {
|
|||||||
&self.generic_parameters
|
&self.generic_parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_constructor_symbol(
|
pub fn set_constructor_symbol(&mut self, constructor_symbol: Rc<RefCell<ConstructorSymbol>>) {
|
||||||
&mut self,
|
self.constructor_symbol = Some(constructor_symbol);
|
||||||
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
|
|
||||||
) {
|
|
||||||
self.constructor_symbol = constructor_symbol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fields(&mut self, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>) {
|
pub fn set_fields(&mut self, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>) {
|
||||||
|
|||||||
@ -5,22 +5,25 @@ use std::cell::RefCell;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct ConstructorSymbol {
|
pub struct ConstructorSymbol {
|
||||||
ctor_keyword_source_range: SourceRange,
|
keyword_source_range: SourceRange,
|
||||||
fqn_parts: Vec<Rc<str>>,
|
fqn_parts: Vec<Rc<str>>,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
|
is_default: bool,
|
||||||
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstructorSymbol {
|
impl ConstructorSymbol {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ctor_keyword_source_range: SourceRange,
|
keyword_source_range: SourceRange,
|
||||||
fqn_parts: Vec<Rc<str>>,
|
fqn_parts: Vec<Rc<str>>,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
|
is_default: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctor_keyword_source_range,
|
keyword_source_range,
|
||||||
fqn_parts,
|
fqn_parts,
|
||||||
is_extern,
|
is_extern,
|
||||||
|
is_default,
|
||||||
parameters: None,
|
parameters: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,6 +55,6 @@ impl Symbol for ConstructorSymbol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn declared_name_source_range(&self) -> &SourceRange {
|
fn declared_name_source_range(&self) -> &SourceRange {
|
||||||
&self.ctor_keyword_source_range
|
&self.keyword_source_range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user