Work on type resolution, WIP.

This commit is contained in:
Jesse Brault 2026-08-29 20:35:29 -05:00
parent 10579e2ad2
commit a87b4efaf0
30 changed files with 719 additions and 52 deletions

View File

@ -36,4 +36,36 @@ impl Class {
functions,
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}
pub fn generic_parameters(&self) -> &[GenericParameter] {
&self.generic_parameters
}
pub fn constructor(&self) -> Option<&Constructor> {
self.constructor.as_ref()
}
pub fn fields(&self) -> &[Field] {
&self.fields
}
pub fn functions(&self) -> &[Function] {
&self.functions
}
}

View File

@ -5,11 +5,10 @@ use crate::source_range::SourceRange;
pub struct Constructor {
node_id: NodeId,
is_public: bool,
ctor_keyword_source_range: SourceRange,
is_public: bool,
parameters: Vec<Parameter>,
statements: Vec<Statement>,
scope_id: Option<usize>,
}
impl Constructor {
@ -22,14 +21,29 @@ impl Constructor {
) -> Self {
Self {
node_id,
is_public,
ctor_keyword_source_range,
is_public,
parameters,
statements,
scope_id: None,
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn ctor_keyword_source_range(&self) -> &SourceRange {
&self.ctor_keyword_source_range
}
pub fn is_public(&self) -> bool {
self.is_public
}
pub fn parameters(&self) -> &[Parameter] {
&self.parameters
}
pub fn statements(&self) -> &[Statement] {
&self.statements
}

View File

@ -3,6 +3,7 @@ use crate::ast::binary_expression::BinaryExpression;
use crate::ast::call::Call;
use crate::ast::double_literal::DoubleLiteral;
use crate::ast::identifier::Identifier;
use crate::ast::instance_self::InstanceSelf;
use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::path::Path;
@ -18,6 +19,7 @@ pub enum Expression {
Integer(IntegerLiteral),
Double(DoubleLiteral),
String(StringLiteral),
InstanceSelf(InstanceSelf),
}
impl Expression {
@ -31,6 +33,7 @@ impl Expression {
Expression::Integer(integer_literal) => integer_literal.node_id(),
Expression::Double(double_literal) => double_literal.node_id(),
Expression::String(string_literal) => string_literal.node_id(),
Expression::InstanceSelf(instance_self) => instance_self.node_id(),
}
}
@ -44,6 +47,7 @@ impl Expression {
Expression::Integer(integer_literal) => integer_literal.source_range(),
Expression::Double(double_literal) => double_literal.source_range(),
Expression::String(string_literal) => string_literal.source_range(),
Expression::InstanceSelf(instance_self) => instance_self.source_range(),
}
}
}

View File

@ -51,6 +51,10 @@ impl Field {
&self.declared_name_source_range
}
pub fn declared_type(&self) -> Option<&TypeUse> {
self.declared_type.as_ref().map(|t| &**t)
}
pub fn initializer(&self) -> Option<&Expression> {
self.initializer.as_ref().map(Box::as_ref)
}

View File

@ -0,0 +1,24 @@
use crate::ast::NodeId;
use crate::source_range::SourceRange;
pub struct InstanceSelf {
node_id: NodeId,
source_range: SourceRange,
}
impl InstanceSelf {
pub fn new(node_id: NodeId, source_range: SourceRange) -> Self {
Self {
node_id,
source_range,
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn source_range(&self) -> &SourceRange {
&self.source_range
}
}

View File

@ -12,6 +12,7 @@ pub mod field;
pub mod function;
pub mod generic_parameter;
pub mod identifier;
pub mod instance_self;
pub mod integer_literal;
pub mod let_statement;
pub mod negative_expression;

View File

@ -1,6 +1,6 @@
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::scope::ScopeId;
use crate::semantic_analysis::symbol::{ClassSymbol, FunctionSymbol, Symbol};
use crate::semantic_analysis::symbol::{ClassSymbol, FunctionSymbol, FunctionType, Symbol};
use crate::semantic_analysis::type_info::{FunctionTypeInfo, InstanceTypeInfo, TypeInfo};
use std::collections::HashMap;
@ -10,7 +10,7 @@ pub fn add_primitive_symbols_and_type_infos(ctx: &mut AnalysisContext, global_sc
None,
"core::String::len".into(),
true,
true,
FunctionType::InstanceMethod,
));
let string_len_symbol_id = ctx
.try_insert_symbol_in_scope(string_len_symbol, global_scope_id)
@ -24,6 +24,7 @@ pub fn add_primitive_symbols_and_type_infos(ctx: &mut AnalysisContext, global_sc
None,
"core::String".into(),
true,
None,
HashMap::new(),
string_class_methods,
));

View File

@ -1,3 +1,4 @@
use crate::ir::register_allocation::{VrCollector, VrUser};
use std::fmt::{Display, Formatter};
use std::rc::Rc;
@ -25,3 +26,7 @@ impl Display for IrAllocate {
write!(f, "alloc {}", self.class_fqn)
}
}
impl VrUser for IrAllocate {
fn vr_uses(&self, _vrs: &mut VrCollector) {}
}

View File

@ -5,9 +5,7 @@ use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_get_field_ref::IrGetFieldRef;
use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
use crate::ir::ir_read_field::IrReadField;
use crate::ir::ir_variable::IrVariableId;
use crate::ir::register_allocation::{VrCollector, VrUser};
use std::collections::HashSet;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
@ -58,7 +56,7 @@ impl VrUser for IrOperation {
IrOperation::Load(ir_expression) => ir_expression.vr_uses(vrs),
IrOperation::Binary(ir_binary) => ir_binary.vr_uses(vrs),
IrOperation::Call(ir_call) => ir_call.vr_uses(vrs),
IrOperation::Allocate(_) => {}
IrOperation::Allocate(ir_allocate) => ir_allocate.vr_uses(vrs),
}
}
}

View File

@ -2,9 +2,7 @@ use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_call::IrCall;
use crate::ir::ir_return::IrReturn;
use crate::ir::ir_set_field::IrSetField;
use crate::ir::ir_variable::IrVariableId;
use crate::ir::register_allocation::{VrCollector, VrUser};
use std::collections::HashSet;
#[derive(Debug)]
pub enum IrStatement {

View File

@ -1,9 +1,11 @@
use std::fmt::{Display, Formatter};
use std::rc::Rc;
pub type IrTypeInfoId = usize;
#[derive(Clone, Debug)]
pub enum IrTypeInfo {
Instance(IrInstanceTypeInfo),
String,
Int,
Double,
@ -15,3 +17,14 @@ impl Display for IrTypeInfo {
write!(f, "{:?}", self)
}
}
#[derive(Clone, Debug)]
pub struct IrInstanceTypeInfo {
class_fqn: Rc<str>,
}
impl IrInstanceTypeInfo {
pub fn new(class_fqn: Rc<str>) -> Self {
Self { class_fqn }
}
}

View File

@ -10,6 +10,7 @@ use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::function::Function;
use crate::ast::let_statement::LetStatement;
use crate::ast::statement::Statement;
use crate::ir::ir_allocate::IrAllocate;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator};
use crate::ir::ir_block::{IrBlock, IrBlockId};
@ -25,7 +26,7 @@ use crate::ir::ir_variable::IrVariable;
use crate::ir::ir_variable::{IrFreeVariables, IrStackFrameVariables, IrVariableInfo};
use crate::lowering::util::{return_type_info_to_ir_type_info, to_ir_type_info};
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::symbol::{Symbol, SymbolId};
use crate::semantic_analysis::symbol::{FunctionType, Symbol, SymbolId};
use crate::semantic_analysis::type_info::TypeInfo;
use std::collections::HashMap;
use std::ops::Neg;
@ -458,6 +459,9 @@ fn lower_expression_to_ir_operation(
Expression::String(string_literal) => {
IrOperation::Load(IrExpression::String(string_literal.content().into()))
}
Expression::InstanceSelf(instance_self) => {
todo!()
}
}
}
@ -559,6 +563,7 @@ fn lower_expression_to_ir_expression(
Expression::Integer(integer_literal) => IrExpression::Int(integer_literal.value()),
Expression::Double(double_literal) => IrExpression::Double(double_literal.value()),
Expression::String(string_literal) => IrExpression::String(string_literal.content().into()),
Expression::InstanceSelf(instance_self) => todo!(),
}
}
@ -570,9 +575,71 @@ fn lower_to_ir_call(
let callee_symbol_id = ctx.nodes_to_symbols()[&call.callee().node_id()];
let callee_symbol = &ctx.symbols()[callee_symbol_id];
match callee_symbol {
Symbol::Class(class_symbol) => {
match class_symbol.constructor_symbol_id() {
Some(constructor_symbol_id) => {
let constructor_symbol = ctx
.symbols()
.get(constructor_symbol_id)
.expect(&format!(
"Could not get constructor symbol for class {}",
class_symbol.fqn()
))
.unwrap_function_symbol();
let constructor_type_info_id = ctx
.symbols_to_type_infos()
.get(&constructor_symbol_id)
.expect(&format!(
"Could not get constructor type info id for class {}",
class_symbol.fqn()
));
let constructor_type_info =
ctx.type_infos()[*constructor_type_info_id].unwrap_constructor();
// get instance type info
let instance_type_info = ctx
.type_infos()
.get(constructor_type_info.instance_self_type_id())
.expect(&format!(
"Could not get instance type info for class {}",
class_symbol.fqn()
));
let ir_type_info = to_ir_type_info(ctx, instance_type_info);
// allocate a new object of this class
let ir_allocate_operation =
IrOperation::Allocate(IrAllocate::new(class_symbol.fqn_owned()));
let object_destination = fn_ctx.storage_env_mut().new_t_var(ir_type_info);
let ir_assign =
IrAssign::new(object_destination.clone(), ir_allocate_operation);
fn_ctx
.current_block_statements
.push(IrStatement::Assign(ir_assign));
// arguments
let mut arguments = vec![IrExpression::Variable(object_destination)];
for argument in call.arguments() {
arguments.push(lower_expression_to_ir_expression(argument, ctx, fn_ctx));
}
// call constructor
IrCall::new(
constructor_symbol.fqn_owned(),
arguments,
constructor_symbol.is_extern(),
)
}
None => {
todo!()
}
}
}
Symbol::Function(function_symbol) => {
let mut arguments = Vec::new();
if function_symbol.is_method() {
if matches!(
function_symbol.function_type(),
FunctionType::InstanceMethod
) {
let this = match call.callee() {
Expression::Path(path) => {
lower_expression_to_ir_expression(path.base(), ctx, fn_ctx)
@ -600,6 +667,6 @@ fn lower_to_ir_call(
function_symbol.is_extern(),
)
}
_ => panic!("Expected function symbol"),
_ => panic!("Expected function symbol, found {:?}", callee_symbol),
}
}

View File

@ -1,15 +1,16 @@
use crate::ir::ir_type_info::IrTypeInfo;
use crate::ir::ir_type_info::{IrInstanceTypeInfo, IrTypeInfo};
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::type_info::TypeInfo;
pub fn to_ir_type_info(ctx: &AnalysisContext, sa_type_info: &TypeInfo) -> IrTypeInfo {
match sa_type_info {
TypeInfo::Instance(instance_type_info) => {
let symbol = &ctx.symbols()[instance_type_info.class_symbol_id()];
if symbol.unwrap_class_symbol().fqn() == "core::String" {
let class_symbol =
&ctx.symbols()[instance_type_info.class_symbol_id()].unwrap_class_symbol();
if class_symbol.fqn() == "core::String" {
IrTypeInfo::String
} else {
todo!("Non-string Instance types")
IrTypeInfo::Instance(IrInstanceTypeInfo::new(class_symbol.fqn_owned()))
}
}
TypeInfo::Int => IrTypeInfo::Int,

View File

@ -14,6 +14,7 @@ use crate::ast::field::Field;
use crate::ast::function::Function;
use crate::ast::generic_parameter::GenericParameter;
use crate::ast::identifier::Identifier;
use crate::ast::instance_self::InstanceSelf;
use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression;
@ -1112,6 +1113,14 @@ impl<'a> Parser<'a> {
source_range,
)))
}
TokenKind::SelfKw => {
let source_range = SourceRange::new(current.start(), current.end());
self.advance();
Some(Expression::InstanceSelf(InstanceSelf::new(
self.next_node_id(),
source_range,
)))
}
_ => unreachable!("Unreachable token type found: {:?}", current.kind()),
}
}

View File

@ -7,12 +7,18 @@ pub fn get_name_for_type<'ctx>(type_info: &TypeInfo, ctx: &'ctx AnalysisContext)
TypeInfo::Function(_function_type_info) => {
todo!()
}
TypeInfo::Constructor(_constructor_type_info) => {
todo!()
}
TypeInfo::TypeConstructor(type_constructor_info) => {
let class_symbol = &ctx.symbols()[type_constructor_info.class_symbol_id()];
class_symbol.declared_name()
}
TypeInfo::Instance(instance_type_info) => {
let class_symbol_id = instance_type_info.class_symbol_id();
let class_symbol = &ctx.symbols()[class_symbol_id];
class_symbol.declared_name()
}
TypeInfo::String => "String",
TypeInfo::Int => "Int",
TypeInfo::Double => "Double",
TypeInfo::Void => "Void",

View File

@ -174,7 +174,7 @@ impl AnalysisContext {
&mut self,
to_insert: Symbol,
owner_node_id: NodeId,
) -> Result<(), Diagnostic> {
) -> Result<SymbolId, Diagnostic> {
let node_scope_id = self.nodes_to_scopes.get(&owner_node_id).expect(&format!(
"owner_node_id {} is not in self.nodes_to_scopes",
owner_node_id
@ -187,7 +187,7 @@ impl AnalysisContext {
} else {
let symbol_id = self.insert_symbol_in_scope(*node_scope_id, to_insert);
self.associate_node_to_symbol(owner_node_id, symbol_id);
Ok(())
Ok(symbol_id)
}
}
@ -255,10 +255,15 @@ impl AnalysisContext {
self.type_infos.len() - 1
}
pub fn insert_type_info_for_node(&mut self, type_info: TypeInfo, node_id: NodeId) {
pub fn insert_type_info_for_node(
&mut self,
type_info: TypeInfo,
node_id: NodeId,
) -> TypeInfoId {
self.type_infos.push(type_info);
self.nodes_to_type_infos
.insert(node_id, self.type_infos.len() - 1);
let type_info_id = self.type_infos.len() - 1;
self.nodes_to_type_infos.insert(node_id, type_info_id);
type_info_id
}
pub fn associate_symbol_to_type(&mut self, symbol_id: SymbolId, type_info_id: TypeInfoId) {

View File

@ -1,16 +1,21 @@
use crate::ast::assign_statement::AssignStatement;
use crate::ast::binary_expression::BinaryExpression;
use crate::ast::call::Call;
use crate::ast::class::Class;
use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::constructor::Constructor;
use crate::ast::expression::Expression;
use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::extern_function::ExternFunction;
use crate::ast::field::Field;
use crate::ast::function::Function;
use crate::ast::identifier::Identifier;
use crate::ast::instance_self::InstanceSelf;
use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::path::Path;
use crate::ast::statement::Statement;
use crate::ast::type_use::TypeUse;
use crate::semantic_analysis::analysis_context::AnalysisContext;
pub fn collect_scopes_compilation_unit(
@ -24,6 +29,9 @@ pub fn collect_scopes_compilation_unit(
for extern_function in compilation_unit.extern_functions() {
collect_scopes_extern_function(extern_function, ctx);
}
for class in compilation_unit.classes() {
collect_scopes_class(class, ctx);
}
ctx.pop_scope(); // compilation_unit
}
@ -38,13 +46,8 @@ fn collect_scopes_function(function: &Function, ctx: &mut AnalysisContext) {
for parameter in function.parameters() {
ctx.associate_node_to_current_scope(parameter.node_id());
}
match function.return_type() {
None => {
// no-op
}
Some(type_use) => {
ctx.associate_node_to_current_scope(type_use.node_id());
}
if let Some(type_use) = function.return_type() {
collect_scopes_type_use(type_use, ctx);
}
// push block scope for body
@ -67,10 +70,74 @@ fn collect_scopes_extern_function(extern_function: &ExternFunction, ctx: &mut An
for parameter in extern_function.parameters() {
ctx.associate_node_to_current_scope(parameter.node_id());
}
ctx.associate_node_to_current_scope(extern_function.return_type().node_id());
collect_scopes_type_use(extern_function.return_type(), ctx);
ctx.pop_scope(); // parameters
}
fn collect_scopes_class(class: &Class, ctx: &mut AnalysisContext) {
// associate class with current scope
ctx.associate_node_to_current_scope(class.node_id());
ctx.push_scope("class_scope");
if let Some(constructor) = class.constructor() {
collect_scopes_constructor(constructor, ctx);
}
for field in class.fields() {
collect_scopes_field(field, ctx);
}
for function in class.functions() {
collect_scopes_function(function, ctx);
}
ctx.pop_scope();
}
// Generally, this is very similar to the process for functions above
fn collect_scopes_constructor(constructor: &Constructor, ctx: &mut AnalysisContext) {
// associate constructor to current scope
ctx.associate_node_to_current_scope(constructor.node_id());
// push ctor params scope
ctx.push_scope("ctor_parameters");
// parameters' scope
for parameter in constructor.parameters() {
ctx.associate_node_to_current_scope(parameter.node_id());
}
// block scope for body
ctx.push_scope("ctor_body");
for statement in constructor.statements() {
collect_scopes_statement(statement, ctx);
}
ctx.pop_scope(); // body
ctx.pop_scope(); // parameters
}
fn collect_scopes_field(field: &Field, ctx: &mut AnalysisContext) {
// associate field with current scope
ctx.associate_node_to_current_scope(field.node_id());
// if typed, associate scope
if let Some(type_use) = field.declared_type() {
collect_scopes_type_use(type_use, ctx);
}
// if initializer, associate scope
if let Some(initializer) = field.initializer() {
collect_scopes_expression(initializer, ctx);
}
}
fn collect_scopes_type_use(type_use: &TypeUse, ctx: &mut AnalysisContext) {
ctx.associate_node_to_current_scope(type_use.node_id());
}
pub fn collect_scopes_statement(statement: &Statement, ctx: &mut AnalysisContext) {
match statement {
Statement::Let(let_statement) => collect_scopes_let_statement(let_statement, ctx),
@ -120,6 +187,9 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext)
Expression::Integer(_) => {}
Expression::Double(_) => {}
Expression::String(_) => {}
Expression::InstanceSelf(instance_self) => {
collect_scopes_instance_self(instance_self, ctx);
}
}
}
@ -153,3 +223,7 @@ fn collect_scopes_path(path: &Path, ctx: &mut AnalysisContext) {
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
ctx.associate_node_to_current_scope(identifier.node_id());
}
fn collect_scopes_instance_self(instance_self: &InstanceSelf, ctx: &mut AnalysisContext) {
ctx.associate_node_to_current_scope(instance_self.node_id());
}

View File

@ -1,10 +1,16 @@
use crate::ast::class::Class;
use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::constructor::Constructor;
use crate::ast::extern_function::ExternFunction;
use crate::ast::field::Field;
use crate::ast::function::Function;
use crate::ast::parameter::Parameter;
use crate::diagnostic::Diagnostics;
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
use crate::semantic_analysis::symbol::{
ClassSymbol, FieldSymbol, FunctionSymbol, FunctionType, ParameterSymbol, Symbol, SymbolId,
};
use std::collections::HashMap;
pub struct SymbolCollectionResult(pub Diagnostics);
@ -15,13 +21,22 @@ pub fn collect_symbols_compilation_unit(
let mut diagnostics = Diagnostics::new();
for function in compilation_unit.functions() {
collect_symbols_function(function, analysis_context, &mut diagnostics);
collect_symbols_function(
function,
analysis_context,
&mut diagnostics,
FunctionType::Function,
);
}
for extern_function in compilation_unit.extern_functions() {
collect_symbols_extern_function(extern_function, analysis_context, &mut diagnostics);
}
for class in compilation_unit.classes() {
collect_symbols_class(class, analysis_context, &mut diagnostics);
}
SymbolCollectionResult(diagnostics)
}
@ -29,6 +44,7 @@ fn collect_symbols_function(
function: &Function,
ctx: &mut AnalysisContext,
diagnostics: &mut Diagnostics,
function_type: FunctionType,
) {
let fqn = ctx.resolve_fqn(&function.declared_name()).into();
@ -38,7 +54,7 @@ fn collect_symbols_function(
Some(function.declared_name_source_range()),
fqn,
false,
false,
function_type,
));
// insert
@ -71,7 +87,7 @@ fn collect_symbols_extern_function(
Some(extern_function.declared_name_source_range()),
fqn,
true,
false,
FunctionType::Function,
));
// insert function symbol
@ -87,6 +103,102 @@ fn collect_symbols_extern_function(
}
}
fn collect_symbols_class(class: &Class, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) {
// Symbols for fields
// Ctor symbol (maybe just a function symbol?)
// Recurse on methods
// Class symbol for self
let mut field_symbol_ids = HashMap::new();
for field in class.fields() {
collect_symbols_field(field, ctx, diagnostics);
let maybe_field_symbol_id = ctx.nodes_to_symbols().get(&field.node_id());
match maybe_field_symbol_id {
Some(field_symbol_id) => {
field_symbol_ids.insert(field.declared_name_owned(), *field_symbol_id);
}
None => {}
}
}
let constructor_symbol_id = if let Some(constructor) = class.constructor() {
collect_symbols_constructor(constructor, ctx, diagnostics);
let maybe_constructor_symbol_id = ctx.nodes_to_symbols().get(&constructor.node_id());
match maybe_constructor_symbol_id {
Some(constructor_symbol_id) => Some(*constructor_symbol_id),
None => None,
}
} else {
None
};
let mut method_symbol_ids = HashMap::new();
for function in class.functions() {
collect_symbols_function(function, ctx, diagnostics, FunctionType::InstanceMethod);
let maybe_method_symbol_id = ctx.nodes_to_symbols().get(&function.node_id());
match maybe_method_symbol_id {
Some(method_symbol_id) => {
method_symbol_ids.insert(function.declared_name_owned(), *method_symbol_id);
}
None => {}
}
}
let class_symbol = Symbol::Class(ClassSymbol::new(
class.declared_name_owned(),
Some(class.declared_name_source_range().clone()),
ctx.resolve_fqn(&class.declared_name()).into(),
false,
constructor_symbol_id,
field_symbol_ids,
method_symbol_ids,
));
match ctx.try_insert_associate_symbol_in_node_scope(class_symbol, class.node_id()) {
Ok(_) => {}
Err(diagnostic) => {
diagnostics.push(diagnostic);
}
}
}
fn collect_symbols_field(field: &Field, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) {
let field_symbol = Symbol::Field(FieldSymbol::new(
field.declared_name_owned(),
Some(field.declared_name_source_range().clone()),
));
match ctx.try_insert_associate_symbol_in_node_scope(field_symbol, field.node_id()) {
Ok(_) => {}
Err(diagnostic) => {
diagnostics.push(diagnostic);
}
}
}
fn collect_symbols_constructor(
constructor: &Constructor,
ctx: &mut AnalysisContext,
diagnostics: &mut Diagnostics,
) {
for parameter in constructor.parameters() {
collect_symbols_parameter(parameter, ctx, diagnostics);
}
let function_symbol = Symbol::Function(FunctionSymbol::new(
"__ctor__".into(),
Some(constructor.ctor_keyword_source_range().clone()),
ctx.resolve_fqn("__ctor__").into(),
false,
FunctionType::Constructor,
));
match ctx.try_insert_associate_symbol_in_node_scope(function_symbol, constructor.node_id()) {
Ok(_) => {}
Err(diagnostic) => {
diagnostics.push(diagnostic);
}
}
}
fn collect_symbols_parameter(
parameter: &Parameter,
ctx: &mut AnalysisContext,

View File

@ -1,9 +1,15 @@
use crate::ast::class::Class;
use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::constructor::Constructor;
use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function;
use crate::ast::parameter::Parameter;
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::type_info::{FunctionTypeInfo, TypeInfo, TypeInfoId};
use crate::semantic_analysis::symbol::SymbolId;
use crate::semantic_analysis::type_info::{
ConstructorTypeInfo, FunctionTypeInfo, InstanceTypeInfo, TypeConstructorInfo, TypeInfo,
TypeInfoId,
};
pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) {
for function in compilation_unit.functions() {
@ -13,6 +19,10 @@ pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisConte
for extern_function in compilation_unit.extern_functions() {
collect_types_extern_function(extern_function, ctx);
}
for class in compilation_unit.classes() {
collect_types_class(class, ctx);
}
}
fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
@ -21,7 +31,16 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
let return_type_info = match function.return_type() {
None => TypeInfo::Void,
Some(type_use) => declared_name_to_type_info(ctx, type_use.declared_name()),
Some(type_use) => get_intrinsic_type(type_use.declared_name()).unwrap_or_else(|| {
let symbol_id = ctx
.nodes_to_symbols()
.get(&type_use.node_id())
.expect(&format!(
"Could not get symbol id for type_use {}",
type_use.declared_name()
));
resolve_type(ctx, *symbol_id)
}),
};
let return_type_info_id = ctx.insert_type_info(return_type_info);
@ -35,6 +54,21 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
ctx.associate_node_and_symbol_to_type(function.node_id(), function_type_info_id);
}
fn get_intrinsic_type(declared_name: &str) -> Option<TypeInfo> {
match declared_name {
"Any" => Some(TypeInfo::Any),
"Void" => Some(TypeInfo::Void),
"Int" => Some(TypeInfo::Int),
"Double" => Some(TypeInfo::Double),
_ => None,
}
}
fn resolve_type(ctx: &AnalysisContext, symbol_id: SymbolId) -> TypeInfo {
todo!()
}
#[deprecated]
fn declared_name_to_type_info(ctx: &AnalysisContext, declared_name: &str) -> TypeInfo {
match declared_name {
"Any" => TypeInfo::Any,
@ -48,7 +82,9 @@ fn declared_name_to_type_info(ctx: &AnalysisContext, declared_name: &str) -> Typ
"Int" => TypeInfo::Int,
"Double" => TypeInfo::Double,
"Void" => TypeInfo::Void,
_ => TypeInfo::__Error,
_ => {
panic!()
}
}
}
@ -86,3 +122,59 @@ fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut Ana
ctx.associate_node_and_symbol_to_type(extern_function.node_id(), function_type_info_id);
}
fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) {
let self_instance_type_info = TypeInfo::Instance(InstanceTypeInfo::new(
*ctx.nodes_to_symbols()
.get(&class.node_id())
.expect(&format!(
"Could not find class symbol for {} {}",
class.node_id(),
class.declared_name()
)),
));
let self_instance_type_info_id = ctx.insert_type_info(self_instance_type_info);
let constructor_type_info_id = if let Some(constructor) = class.constructor() {
Some(collect_types_constructor(
constructor,
ctx,
self_instance_type_info_id,
))
} else {
None
};
// todo: fields, methods
// Self type constructor
let type_constructor_type_info = TypeInfo::TypeConstructor(TypeConstructorInfo::new(
ctx.nodes_to_symbols()[&class.node_id()],
constructor_type_info_id,
));
let type_constructor_type_info_id = ctx.insert_type_info(type_constructor_type_info);
ctx.associate_node_and_symbol_to_type(class.node_id(), type_constructor_type_info_id);
}
fn collect_types_constructor(
constructor: &Constructor,
ctx: &mut AnalysisContext,
self_type_info_id: TypeInfoId,
) -> TypeInfoId {
let mut parameter_type_info_ids: Vec<TypeInfoId> = Vec::new();
for parameter in constructor.parameters() {
let type_info = declared_name_to_type_info(ctx, parameter.type_use().declared_name());
let type_info_id = ctx.insert_type_info(type_info);
ctx.associate_node_and_symbol_to_type(parameter.node_id(), type_info_id);
parameter_type_info_ids.push(type_info_id);
}
let constructor_type_info = TypeInfo::Constructor(ConstructorTypeInfo::new(
self_type_info_id,
parameter_type_info_ids,
));
let constructor_type_info_id = ctx.insert_type_info(constructor_type_info);
ctx.associate_node_and_symbol_to_type(constructor.node_id(), constructor_type_info_id);
constructor_type_info_id
}

View File

@ -25,6 +25,7 @@ mod resolve_names;
mod resolve_types;
pub mod scope;
pub mod symbol;
pub mod type_analysis;
pub mod type_info;
pub fn analyze_compilation_unit(

View File

@ -7,6 +7,7 @@ use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function;
use crate::ast::identifier::Identifier;
use crate::ast::instance_self::InstanceSelf;
use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::parameter::Parameter;
@ -205,6 +206,9 @@ fn resolve_names_expression(
Expression::Integer(_) => {}
Expression::Double(_) => {}
Expression::String(_) => {}
Expression::InstanceSelf(instance_self) => {
resolve_names_instance_self(instance_self, ctx, diagnostics, phase);
}
}
}
@ -270,3 +274,12 @@ fn resolve_names_identifier(
}
}
}
fn resolve_names_instance_self(
instance_self: &InstanceSelf,
ctx: &mut AnalysisContext,
diagnostics: &mut Diagnostics,
phase: ExpressionResolutionPhase,
) {
todo!()
}

View File

@ -7,6 +7,7 @@ use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::expression::Expression;
use crate::ast::function::Function;
use crate::ast::identifier::Identifier;
use crate::ast::instance_self::InstanceSelf;
use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::parameter::Parameter;
@ -163,6 +164,9 @@ fn resolve_types_expression(
string_literal.node_id(),
);
}
Expression::InstanceSelf(instance_self) => {
resolve_types_instance_self(instance_self, ctx);
}
}
}
@ -251,6 +255,70 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
let callee_type_info = ctx.get_type_info_for_node(call.callee().node_id());
match callee_type_info {
TypeInfo::TypeConstructor(type_constructor_info) => {
let class_symbol =
ctx.symbols()[type_constructor_info.class_symbol_id()].unwrap_class_symbol();
match class_symbol.constructor_symbol_id() {
Some(constructor_symbol_id) => {
let constructor_type_info_id =
ctx.symbols_to_type_infos()[&constructor_symbol_id];
let constructor_type_info = ctx
.type_infos()
.get(constructor_type_info_id)
.expect(&format!(
"Cannot get constructor type info for {}",
get_name_for_type(callee_type_info, ctx)
))
.unwrap_constructor();
// check arguments length
let arguments = call.arguments();
if arguments.len() != constructor_type_info.parameter_type_ids().len() {
todo!("Report wrong number of constructor args")
}
// check each argument type
let parameter_type_ids = constructor_type_info.parameter_type_ids();
if arguments.len() == parameter_type_ids.len() {
for i in 0..parameter_type_ids.len() {
let argument_type_info =
ctx.get_type_info_for_node(arguments[i].node_id());
let parameter_type_info =
ctx.get_type_info_by_id(parameter_type_ids[i]);
if !can_assign_right_to_left(
parameter_type_info,
argument_type_info,
ctx,
) {
let parameter_type_name =
get_name_for_type(parameter_type_info, ctx);
let argument_type_name = get_name_for_type(argument_type_info, ctx);
let message = format!(
"Incompatible types: cannot assign {} to {}",
argument_type_name, parameter_type_name
);
let diagnostic = Diagnostic::new(
&message,
arguments[i].source_range().start(),
arguments[i].source_range().end(),
);
diagnostics.push(diagnostic);
}
}
}
// set return type of call to be the "return" type of the constructor
ctx.associate_node_to_type(
call.node_id(),
constructor_type_info.instance_self_type_id(),
);
}
None => {
// Default, no-arg constructor
// check zero args
}
}
}
TypeInfo::Function(function_type_info) => {
// check arguments length
let arguments = call.arguments();
@ -389,3 +457,7 @@ fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext)
let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id());
ctx.associate_node_to_type(identifier.node_id(), type_info_id);
}
fn resolve_types_instance_self(instance_self: &InstanceSelf, ctx: &mut AnalysisContext) {
todo!()
}

View File

@ -147,16 +147,18 @@ pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo, ctx: &Analysi
TypeInfo::Function(_) => {
panic!()
}
TypeInfo::Constructor(_) => {
panic!()
}
TypeInfo::TypeConstructor(_) => {
panic!()
}
TypeInfo::Instance(left_instance_type_info) => match right {
TypeInfo::Instance(right_instance_type_info) => {
left_instance_type_info.can_assign_from(right_instance_type_info, ctx)
}
_ => false,
},
TypeInfo::String => match right {
TypeInfo::String => true,
_ => false,
},
TypeInfo::Int => match right {
TypeInfo::Int => true,
_ => false,

View File

@ -51,6 +51,13 @@ impl Symbol {
_ => panic!("Attempt to unwrap {:?} as ClassSymbol", self),
}
}
pub fn unwrap_function_symbol(&self) -> &FunctionSymbol {
match self {
Symbol::Function(function_symbol) => function_symbol,
_ => panic!("Attempt to unwrap {:?} as FunctionSymbol", self),
}
}
}
#[derive(Debug)]
@ -59,6 +66,7 @@ pub struct ClassSymbol {
source_range: Option<SourceRange>,
fqn: Rc<str>,
is_extern: bool,
constructor_symbol_id: Option<SymbolId>,
field_symbol_ids: HashMap<Rc<str>, SymbolId>,
method_symbol_ids: HashMap<Rc<str>, SymbolId>,
}
@ -69,6 +77,7 @@ impl ClassSymbol {
source_range: Option<SourceRange>,
fqn: Rc<str>,
is_extern: bool,
constructor_symbol_id: Option<SymbolId>,
field_symbol_ids: HashMap<Rc<str>, SymbolId>,
method_symbol_ids: HashMap<Rc<str>, SymbolId>,
) -> Self {
@ -77,6 +86,7 @@ impl ClassSymbol {
source_range,
fqn,
is_extern,
constructor_symbol_id,
field_symbol_ids,
method_symbol_ids,
}
@ -98,10 +108,18 @@ impl ClassSymbol {
&self.fqn
}
pub fn fqn_owned(&self) -> Rc<str> {
self.fqn.clone()
}
pub fn can_assign_from(&self, right: &ClassSymbol, _ctx: &AnalysisContext) -> bool {
self.fqn == right.fqn // eventually, this will check inheritance logic, etc.
}
pub fn constructor_symbol_id(&self) -> Option<SymbolId> {
self.constructor_symbol_id
}
pub fn field_symbol_ids(&self) -> &HashMap<Rc<str>, SymbolId> {
&self.field_symbol_ids
}
@ -138,13 +156,21 @@ impl FieldSymbol {
}
}
#[derive(Debug)]
pub enum FunctionType {
Function,
Constructor,
InstanceMethod,
StaticMethod,
}
#[derive(Debug)]
pub struct FunctionSymbol {
declared_name: Rc<str>,
declared_name_source_range: Option<SourceRange>,
fqn: Rc<str>,
is_extern: bool,
is_method: bool,
function_type: FunctionType,
}
impl FunctionSymbol {
@ -153,14 +179,14 @@ impl FunctionSymbol {
declared_name_source_range: Option<SourceRange>,
fqn: Rc<str>,
is_extern: bool,
is_method: bool,
function_type: FunctionType,
) -> Self {
Self {
declared_name,
declared_name_source_range,
fqn,
is_extern,
is_method,
function_type,
}
}
@ -188,8 +214,8 @@ impl FunctionSymbol {
self.is_extern
}
pub fn is_method(&self) -> bool {
self.is_method
pub fn function_type(&self) -> &FunctionType {
&self.function_type
}
}

View File

@ -0,0 +1,25 @@
use crate::ast::class::Class;
use crate::ast::compilation_unit::CompilationUnit;
use crate::semantic_analysis::analysis_context::AnalysisContext;
use crate::semantic_analysis::type_info::{InstanceTypeInfo, TypeInfo};
pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) {
for class in compilation_unit.classes() {
collect_types_class(class, ctx);
}
}
fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) {
let class_symbol_id = *ctx
.nodes_to_symbols()
.get(&class.node_id())
.expect(&format!(
"Could not find class symbol for {} {}",
class.node_id(),
class.declared_name()
));
let self_instance_type_info = TypeInfo::Instance(InstanceTypeInfo::new(class_symbol_id));
let self_instance_type_info_id = ctx.insert_type_info(self_instance_type_info);
ctx.associate_symbol_to_type(class_symbol_id, self_instance_type_info_id)
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,3 @@
mod collect;
mod infer;
mod resolve;

View File

@ -0,0 +1 @@

View File

@ -7,14 +7,31 @@ pub type TypeInfoId = usize;
pub enum TypeInfo {
Any,
Function(FunctionTypeInfo),
Constructor(ConstructorTypeInfo),
TypeConstructor(TypeConstructorInfo),
Instance(InstanceTypeInfo),
String,
Int,
Double,
Void,
__Error,
}
impl TypeInfo {
pub fn unwrap_constructor(&self) -> &ConstructorTypeInfo {
match self {
TypeInfo::Constructor(constructor_type_info) => constructor_type_info,
_ => panic!("Attempt to unwrap {:?} as Constructor", self),
}
}
pub fn unwrap_type_constructor(&self) -> &TypeConstructorInfo {
match self {
TypeInfo::TypeConstructor(type_constructor_info) => type_constructor_info,
_ => panic!("Attempt to unwrap {:?} as TypeConstructor", self),
}
}
}
#[derive(Clone, Debug)]
pub struct FunctionTypeInfo {
parameter_type_info_ids: Vec<TypeInfoId>,
@ -48,6 +65,52 @@ impl FunctionTypeInfo {
}
}
#[derive(Clone, Debug)]
pub struct ConstructorTypeInfo {
instance_self_type_id: TypeInfoId,
parameter_type_ids: Vec<TypeInfoId>,
}
impl ConstructorTypeInfo {
pub fn new(instance_self_type_id: TypeInfoId, parameter_type_ids: Vec<TypeInfoId>) -> Self {
Self {
instance_self_type_id,
parameter_type_ids,
}
}
pub fn instance_self_type_id(&self) -> TypeInfoId {
self.instance_self_type_id
}
pub fn parameter_type_ids(&self) -> &[TypeInfoId] {
&self.parameter_type_ids
}
}
#[derive(Clone, Debug)]
pub struct TypeConstructorInfo {
class_symbol_id: SymbolId,
constructor_type_info_id: Option<TypeInfoId>,
}
impl TypeConstructorInfo {
pub fn new(class_symbol_id: SymbolId, constructor_type_info_id: Option<TypeInfoId>) -> Self {
Self {
class_symbol_id,
constructor_type_info_id,
}
}
pub fn class_symbol_id(&self) -> SymbolId {
self.class_symbol_id
}
pub fn constructor_type_info_id(&self) -> Option<TypeInfoId> {
self.constructor_type_info_id
}
}
#[derive(Clone, Debug)]
pub struct InstanceTypeInfo {
class_symbol_id: SymbolId,

View File

@ -182,10 +182,10 @@ mod e2e_tests {
let context = prepare_context(
"
class Foo
mut bar = 21
bar: Int
ctor(_bar: Int)
bar = _bar
pub ctor(bar: Int)
self.bar = bar
end
end