got type system working
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
// adds a and b, but also 4 for some reason
|
// adds a and b, but also 4 for some reason
|
||||||
fn add(a: I32, b: I32): I32 {
|
fn add(a: I64, b: I64): I64 {
|
||||||
let foo = 4; // because I feel like it
|
let foo = 4; // because I feel like it
|
||||||
let test_float: F32 = {
|
let test_float: F64 = {
|
||||||
10.2
|
10.2
|
||||||
};
|
};
|
||||||
test_float = 5.0;
|
test_float = 5.0;
|
||||||
a + b + foo
|
a + b + foo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtract(a: I32, b: I32): I32 {
|
fn subtract(a: I64, b: I64): I64 {
|
||||||
a - b
|
a - b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ fn i_hate_this(a: F64): F64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unit_function() {
|
fn unit_function() {
|
||||||
let a: I32 = 4;
|
let a: I64 = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(): I32 {
|
fn main(): I64 {
|
||||||
add(4, subtract(5, 2))
|
add(4, subtract(5, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,28 +39,28 @@ fn returns_user(): User {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_id(): U64 {
|
fn get_user_id(): I64 {
|
||||||
let user = returns_user();
|
let user = returns_user();
|
||||||
user.id = 5;
|
user.id = 5;
|
||||||
return user.id;
|
return user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_method(user: User): U64 {
|
fn use_method(user: User): I64 {
|
||||||
return user.get_id();
|
return user.get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
id: U64,
|
id: I64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
fn new(id: U64): Self {
|
fn new(id: I64): Self {
|
||||||
return Self{
|
return Self{
|
||||||
id: id,
|
id: id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(self: Self): U64 {
|
fn get_id(self: Self): I64 {
|
||||||
return self.id;
|
return self.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ pub LiteralInt: String = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub SpannedLiteralInt: ast::LiteralInt = {
|
pub SpannedLiteralInt: ast::LiteralInt = {
|
||||||
<literal_int:Spanned<LiteralInt>> => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("i64".to_string())}
|
<literal_int:Spanned<LiteralInt>> => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("I64".to_string())}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub LiteralFloat: String = {
|
pub LiteralFloat: String = {
|
||||||
@@ -45,7 +45,7 @@ pub LiteralFloat: String = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub SpannedLiteralFloat: ast::LiteralFloat = {
|
pub SpannedLiteralFloat: ast::LiteralFloat = {
|
||||||
<literal_float:Spanned<LiteralFloat>> => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())}
|
<literal_float:Spanned<LiteralFloat>> => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("F64".to_string())}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub Identifier: String = {
|
pub Identifier: String = {
|
||||||
@@ -149,7 +149,7 @@ pub Statement: ast::Statement = {
|
|||||||
|
|
||||||
pub Block: ast::Block = {
|
pub Block: ast::Block = {
|
||||||
"{" <v:(<Statement>)*> <e:Expression?> "}" => match e {
|
"{" <v:(<Statement>)*> <e:Expression?> "}" => match e {
|
||||||
None => ast::Block{statements: v, type_: ast::new_never()},
|
None => ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)},
|
||||||
Some(e) => {
|
Some(e) => {
|
||||||
let mut v = v;
|
let mut v = v;
|
||||||
v.push(ast::Statement::Expression(e));
|
v.push(ast::Statement::Expression(e));
|
||||||
@@ -160,7 +160,7 @@ pub Block: ast::Block = {
|
|||||||
|
|
||||||
pub TypeUsage: ast::TypeUsage = {
|
pub TypeUsage: ast::TypeUsage = {
|
||||||
<n:SpannedIdentifier> => ast::TypeUsage::Named(ast::NamedTypeUsage{name: n}),
|
<n:SpannedIdentifier> => ast::TypeUsage::Named(ast::NamedTypeUsage{name: n}),
|
||||||
"fn" "(" <args:Comma<TypeUsage>> ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::TypeUsage::new_unknown(&id_generator))}),
|
"fn" "(" <args:Comma<TypeUsage>> ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}),
|
||||||
"fn" "(" <args:Comma<TypeUsage>> ")" ":" <rt:TypeUsage> => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}),
|
"fn" "(" <args:Comma<TypeUsage>> ")" ":" <rt:TypeUsage> => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ pub VariableDeclaration: ast::VariableDeclaration = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub FunctionDeclaration: ast::FunctionDeclaration = {
|
pub FunctionDeclaration: ast::FunctionDeclaration = {
|
||||||
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" => ast::FunctionDeclaration{name: n, arguments: args, return_type: ast::TypeUsage::new_unknown(&id_generator)},
|
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" => ast::FunctionDeclaration{name: n, arguments: args, return_type: ast::new_unit()},
|
||||||
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" ":" <rt:TypeUsage> => ast::FunctionDeclaration{name: n, arguments: args, return_type: rt},
|
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" ":" <rt:TypeUsage> => ast::FunctionDeclaration{name: n, arguments: args, return_type: rt},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ fn main() {
|
|||||||
println!("resolved ast: {:#?}", &resolved_ast);
|
println!("resolved ast: {:#?}", &resolved_ast);
|
||||||
let type_checker = type_checking::TypeChecker{};
|
let type_checker = type_checking::TypeChecker{};
|
||||||
let (checked_ast, subst) = type_checker.with_module(&resolved_ast);
|
let (checked_ast, subst) = type_checker.with_module(&resolved_ast);
|
||||||
println!("checked ast: {:#?}", &resolved_ast);
|
println!("checked ast: {:#?}", &checked_ast);
|
||||||
println!("substitutions: {:#?}", &subst);
|
println!("substitutions: {:#?}", &subst);
|
||||||
|
|
||||||
// let context = Context::create();
|
// let context = Context::create();
|
||||||
|
|||||||
@@ -212,8 +212,13 @@ impl TypeAliasResolver {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::Subexpression::LiteralStruct(literal_struct) => {
|
ast::Subexpression::LiteralStruct(literal_struct) => {
|
||||||
|
let result = resolve_type(ctx, &ast::NamedTypeUsage{name: literal_struct.name.clone()});
|
||||||
|
let new_name = match &result {
|
||||||
|
ast::TypeUsage::Named(named) => { named.name.clone() },
|
||||||
|
_ => panic!("LiteralStruct resolved to non-named-type"),
|
||||||
|
};
|
||||||
ast::Subexpression::LiteralStruct(ast::LiteralStruct{
|
ast::Subexpression::LiteralStruct(ast::LiteralStruct{
|
||||||
name: literal_struct.name.clone(),
|
name: new_name.clone(),
|
||||||
fields: literal_struct.fields.iter().map(|field|{
|
fields: literal_struct.fields.iter().map(|field|{
|
||||||
(field.0.clone(), self.with_expression(ctx, &field.1))
|
(field.0.clone(), self.with_expression(ctx, &field.1))
|
||||||
}).collect(),
|
}).collect(),
|
||||||
|
|||||||
@@ -4,34 +4,36 @@ use crate::ast;
|
|||||||
|
|
||||||
pub type SubstitutionMap = HashMap<String, ast::TypeUsage>;
|
pub type SubstitutionMap = HashMap<String, ast::TypeUsage>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum NamedEntity {
|
pub enum NamedEntity {
|
||||||
TypeDeclaration(ast::TypeDeclaration),
|
TypeDeclaration(ast::TypeDeclaration),
|
||||||
Variable(ast::TypeUsage),
|
Variable(ast::TypeUsage),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
struct Context {
|
struct Context {
|
||||||
pub current_function_return: Option<ast::TypeUsage>,
|
pub current_function_return: Option<ast::TypeUsage>,
|
||||||
pub environment: HashMap<String, ast::NamedEntity>,
|
pub impls: HashMap<String, ast::Impl>,
|
||||||
|
pub environment: HashMap<String, NamedEntity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
fn add_variable(&self, name: String, type_usage: &ast::TypeUsage) -> Context {
|
fn add_variable(&self, name: String, type_usage: &ast::TypeUsage) -> Context {
|
||||||
let mut ctx = self.clone();
|
let mut ctx = self.clone();
|
||||||
ctx.environment[name] = NamedEntity::Variable(type_usage.clone());
|
ctx.environment.insert(name.to_string(), NamedEntity::Variable(type_usage.clone()));
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_type(&self, name: String, type_decl: &ast::TypeDeclaration) -> Context {
|
fn add_type(&self, name: String, type_decl: &ast::TypeDeclaration) -> Context {
|
||||||
let mut ctx = self.clone();
|
let mut ctx = self.clone();
|
||||||
ctx.environment[name] = NamedEntity::TypeDeclaration(type_decl.clone());
|
ctx.environment.insert(name.to_string(), NamedEntity::TypeDeclaration(type_decl.clone()));
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_function_return(&self, function: ast::TypeUsage) -> Context {
|
fn set_current_function_return(&self, function: &ast::TypeUsage) -> Context {
|
||||||
let mut ctx = self.clone();
|
let mut ctx = self.clone();
|
||||||
ctx.current_function_return = Some(function);
|
ctx.current_function_return = Some(function.clone());
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,75 +42,90 @@ fn apply_substitution(substitution: &SubstitutionMap, type_: &ast::TypeUsage) ->
|
|||||||
match type_ {
|
match type_ {
|
||||||
ast::TypeUsage::Named(named) => ast::TypeUsage::Named(named.clone()),
|
ast::TypeUsage::Named(named) => ast::TypeUsage::Named(named.clone()),
|
||||||
ast::TypeUsage::Unknown(unknown) => {
|
ast::TypeUsage::Unknown(unknown) => {
|
||||||
if substitution.contains_key(unknown.name) {
|
if substitution.contains_key(&unknown.name) {
|
||||||
ast::TypeUsage::Unknown(substitution[unknown.name].clone())
|
substitution[&unknown.name].clone()
|
||||||
} else {
|
} else {
|
||||||
ast::TypeUsage::Unknown(unknown.clone())
|
ast::TypeUsage::Unknown(unknown.clone())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ast::TypeUsage::Function(function) => {
|
ast::TypeUsage::Function(function) => {
|
||||||
ast::TypeUsage::Function(FunctionTypeUsage{
|
ast::TypeUsage::Function(ast::FunctionTypeUsage{
|
||||||
arguments: function.arguments.iter().map(|arg| {
|
arguments: function.arguments.iter().map(|arg| {
|
||||||
apply_substitution(substitution, arg)
|
apply_substitution(substitution, arg)
|
||||||
}).collect(),
|
}).collect(),
|
||||||
return_type: apply_substitution(substitution, function.return_type),
|
return_type: Box::new(apply_substitution(substitution, &function.return_type)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose_substitutions(s1: SubstitutionMap, s2: SubstitutionMap) -> SubstitutionMap {
|
fn compose_substitutions(s1: &SubstitutionMap, s2: &SubstitutionMap) -> SubstitutionMap {
|
||||||
let mut result = SubstitutionMap::new();
|
let mut result = SubstitutionMap::new();
|
||||||
for k in s2.keys() {
|
for k in s2.keys() {
|
||||||
result[k] = apply_substitution(s1, s2[k]);
|
result.insert(k.to_string(), apply_substitution(s1, &s2[k]));
|
||||||
}
|
}
|
||||||
return s1.into_iter().chain(result).collect();
|
return s1.into_iter().map(|(k, v)| (k.clone(), v.clone())).chain(result).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify(t1: ast::TypeUsage, t2: ast::TypeUsage) -> SubstitutionMap {
|
fn unify(t1: &ast::TypeUsage, t2: &ast::TypeUsage) -> SubstitutionMap {
|
||||||
match (t1, t2) {
|
match (t1, t2) {
|
||||||
(ast::TypeUsage::Named(named1), ast::TypeUsage::Named(named2)) => {
|
(ast::TypeUsage::Named(named1), ast::TypeUsage::Named(named2)) => {
|
||||||
|
// if named1.name.name.value == "!" || named2.name.name.value == "!" {
|
||||||
|
// return SubstitutionMap::new(); // never matches with everything
|
||||||
|
// }
|
||||||
if named1.name.name.value == named2.name.name.value {
|
if named1.name.name.value == named2.name.name.value {
|
||||||
return SubstitutionMap::new()
|
return SubstitutionMap::new();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
if let ast::TypeUsage::Unknown(unknown) = t1 {
|
match t1 {
|
||||||
return var_bind(unknown.name, t2);
|
ast::TypeUsage::Unknown(unknown) => {
|
||||||
|
return var_bind(&unknown.name, t2);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
if let ast::TypeUsage::Unknown(unknown) = t2 {
|
match t2 {
|
||||||
return var_bind(unknown.name, t1);
|
ast::TypeUsage::Unknown(unknown) => {
|
||||||
|
return var_bind(&unknown.name, t1);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
match (t1, t2) {
|
match (t1, t2) {
|
||||||
(ast::TypeUsage::Function(f1), ast::TypeUsage::Function(f2)) => {
|
(ast::TypeUsage::Function(f1), ast::TypeUsage::Function(f2)) => {
|
||||||
let mut result = unify(f1.return_type, f2.return_type);
|
let mut result = unify(&*f1.return_type, &*f2.return_type);
|
||||||
if f1.arguments.len() != f2.arguments.len() {
|
if f1.arguments.len() != f2.arguments.len() {
|
||||||
panic!("Argument lengths don't match");
|
panic!("Argument lengths don't match");
|
||||||
}
|
}
|
||||||
for (i, _) in f1.arguments.iter().enumerate() {
|
for (i, _) in f1.arguments.iter().enumerate() {
|
||||||
result = compose_substitutions(result, unify(apply_substitution(result, f1.arguments[i]), apply_substitution(result, f2.arguments[i])));
|
result = compose_substitutions(&result, &unify(&apply_substitution(&result, &f1.arguments[i]), &apply_substitution(&result, &f2.arguments[i])));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
println!("problem:\n{:?}\n{:?}", t1, t2);
|
||||||
panic!("Mismatched unification types");
|
panic!("Mismatched unification types");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_bind(name: &str, t: ast::TypeUsage) -> SubstitutionMap {
|
fn var_bind(name: &str, t: &ast::TypeUsage) -> SubstitutionMap {
|
||||||
if let ast::TypeUsage::Unknown(unknown) = t && name == unknown.name {
|
match t {
|
||||||
return SubstitutionMap::new();
|
ast::TypeUsage::Unknown(unknown) => {
|
||||||
|
if name == unknown.name {
|
||||||
|
return SubstitutionMap::new();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
if contains(t, name) {
|
if contains(t, name) {
|
||||||
panic!("Type contains a reference to itself")
|
panic!("Type contains a reference to itself")
|
||||||
}
|
}
|
||||||
let mut substitution = SubstitutionMap::new();
|
let mut substitution = SubstitutionMap::new();
|
||||||
substitution[name] = t;
|
substitution.insert(name.to_string(), t.clone());
|
||||||
return substitution;
|
return substitution;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains(t: ast::TypeUsage, name: &str) -> bool {
|
fn contains(t: &ast::TypeUsage, name: &str) -> bool {
|
||||||
match t {
|
match t {
|
||||||
ast::TypeUsage::Named(_) => {
|
ast::TypeUsage::Named(_) => {
|
||||||
return false
|
return false
|
||||||
@@ -117,11 +134,11 @@ fn contains(t: ast::TypeUsage, name: &str) -> bool {
|
|||||||
unknown.name == name
|
unknown.name == name
|
||||||
},
|
},
|
||||||
ast::TypeUsage::Function(f) => {
|
ast::TypeUsage::Function(f) => {
|
||||||
if contains(f.return_type, name) {
|
if contains(&*f.return_type, name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for arg in f.arguments.iter() {
|
for arg in f.arguments.iter() {
|
||||||
if contains(arg, name) {
|
if contains(&arg.clone(), name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,46 +154,77 @@ impl TypeChecker {
|
|||||||
pub fn with_module(self: &Self, module: &ast::Module) -> (ast::Module, SubstitutionMap) {
|
pub fn with_module(self: &Self, module: &ast::Module) -> (ast::Module, SubstitutionMap) {
|
||||||
let mut ctx = Context{
|
let mut ctx = Context{
|
||||||
environment: HashMap::new(), //TODO: builtins
|
environment: HashMap::new(), //TODO: builtins
|
||||||
|
impls: HashMap::new(),
|
||||||
|
current_function_return: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for item in module.items.iter() {
|
for item in module.items.iter() {
|
||||||
match item {
|
match item {
|
||||||
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => {
|
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => {
|
||||||
ctx.declarations.push(ast::NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())));
|
ctx.environment.insert(struct_.name.name.value.to_string(), NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())));
|
||||||
},
|
},
|
||||||
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => {
|
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => {
|
||||||
ctx.declarations.push(ast::NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())));
|
ctx.environment.insert(alias.name.name.value.to_string(), NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())));
|
||||||
|
},
|
||||||
|
ast::ModuleItem::Function(function) => {
|
||||||
|
let function_type = ast::FunctionTypeUsage{
|
||||||
|
arguments: function.declaration.arguments.iter().map(|arg|{arg.type_.clone()}).collect(),
|
||||||
|
return_type: Box::new(function.declaration.return_type.clone()),
|
||||||
|
};
|
||||||
|
ctx.environment.insert(function.declaration.name.name.value.to_string(), NamedEntity::Variable(ast::TypeUsage::Function(function_type)));
|
||||||
|
},
|
||||||
|
ast::ModuleItem::Impl(impl_) => {
|
||||||
|
ctx.impls.insert(impl_.struct_name.name.value.to_string(), impl_.clone());
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ast::Module{
|
let mut subst = SubstitutionMap::new();
|
||||||
|
let result = ast::Module{
|
||||||
items: module.items.iter().map(|item|{
|
items: module.items.iter().map(|item|{
|
||||||
match item {
|
match item {
|
||||||
ast::ModuleItem::Function(function) => {
|
ast::ModuleItem::Function(function) => {
|
||||||
ast::ModuleItem::Function(self.with_function(&ctx, function))
|
let (func, fn_subst) = self.with_function(&ctx, &subst, function);
|
||||||
|
subst = compose_substitutions(&subst, &fn_subst);
|
||||||
|
ast::ModuleItem::Function(func)
|
||||||
},
|
},
|
||||||
ast::ModuleItem::TypeDeclaration(type_declaration) => {
|
ast::ModuleItem::TypeDeclaration(type_declaration) => {
|
||||||
ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration))
|
let (ty_decl, ty_subst) = self.with_type_declaration(&ctx, type_declaration);
|
||||||
|
subst = compose_substitutions(&subst, &ty_subst);
|
||||||
|
ast::ModuleItem::TypeDeclaration(ty_decl)
|
||||||
},
|
},
|
||||||
ast::ModuleItem::Impl(impl_) => {
|
ast::ModuleItem::Impl(impl_) => {
|
||||||
ast::ModuleItem::Impl(self.with_impl(&ctx, impl_))
|
let (impl_result, impl_subst) = self.with_impl(&ctx, &subst, impl_);
|
||||||
|
subst = compose_substitutions(&subst, &impl_subst);
|
||||||
|
ast::ModuleItem::Impl(impl_result)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
};
|
};
|
||||||
|
return (result, subst);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> (ast::Function, SubstitutionMap) {
|
fn with_function(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, function: &ast::Function) -> (ast::Function, SubstitutionMap) {
|
||||||
// add args to env
|
// add args to env
|
||||||
let mut function_ctx = ctx.set_current_function_return(function.declaration.return_type.clone());
|
let mut function_ctx = ctx.set_current_function_return(&function.declaration.return_type.clone());
|
||||||
for arg in function.declaration.arguments.iter() {
|
for arg in function.declaration.arguments.iter() {
|
||||||
function_ctx = function_ctx.add_variable(arg.name.to_string(), arg.type_.clone());
|
function_ctx = function_ctx.add_variable(arg.name.name.value.to_string(), &arg.type_.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (block, substitution) = self.with_block(&function_ctx, incoming_substitutions, &function.block);
|
||||||
|
let mut substitution = compose_substitutions(incoming_substitutions, &substitution);
|
||||||
|
match &block.type_ {
|
||||||
|
ast::TypeUsage::Named(named) => {
|
||||||
|
if named.name.name.value != "!" {
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&function.declaration.return_type, &block.type_));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&function.declaration.return_type, &block.type_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (block, substitution) = self.with_block(function_ctx, &function.block);
|
|
||||||
let substitution = unify(block.type_, function.declaration.return_type);
|
|
||||||
|
|
||||||
return (ast::Function{
|
return (ast::Function{
|
||||||
declaration: ast::FunctionDeclaration{
|
declaration: ast::FunctionDeclaration{
|
||||||
@@ -193,8 +241,8 @@ impl TypeChecker {
|
|||||||
fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> (ast::TypeDeclaration, SubstitutionMap) {
|
fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> (ast::TypeDeclaration, SubstitutionMap) {
|
||||||
match type_declaration {
|
match type_declaration {
|
||||||
ast::TypeDeclaration::Struct(struct_) => {
|
ast::TypeDeclaration::Struct(struct_) => {
|
||||||
let (result, substitution) = self.with_struct_declaration(ctx, struct_);
|
let result = self.with_struct_declaration(ctx, struct_);
|
||||||
return (ast::TypeDeclaration::Struct(result), substitution);
|
return (ast::TypeDeclaration::Struct(result), SubstitutionMap::new());
|
||||||
},
|
},
|
||||||
ast::TypeDeclaration::Primitive(primitive) => {
|
ast::TypeDeclaration::Primitive(primitive) => {
|
||||||
return (ast::TypeDeclaration::Primitive(primitive.clone()), SubstitutionMap::new());
|
return (ast::TypeDeclaration::Primitive(primitive.clone()), SubstitutionMap::new());
|
||||||
@@ -217,174 +265,327 @@ impl TypeChecker {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> (ast::Impl, SubstitutionMap) {
|
fn with_impl(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, impl_: &ast::Impl) -> (ast::Impl, SubstitutionMap) {
|
||||||
let mut substitutions = SubstitutionMap::new();
|
let mut substitutions = incoming_substitutions.clone();
|
||||||
return (ast::Impl{
|
return (ast::Impl{
|
||||||
struct_name: impl_.struct_name.clone(),
|
struct_name: impl_.struct_name.clone(),
|
||||||
functions: impl_.functions.iter().map(|f|{
|
functions: impl_.functions.iter().map(|f|{
|
||||||
let (result, function_subs) = self.with_function(&ctx, f);
|
let (result, function_subs) = self.with_function(&ctx, &substitutions, f);
|
||||||
substitutions = compose_substitutions(substitutions, function_subs)
|
substitutions = compose_substitutions(&substitutions, &function_subs);
|
||||||
result
|
result
|
||||||
}).collect(),
|
}).collect(),
|
||||||
}, substitutions);
|
}, substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> (ast::Block, SubstitutionMap) {
|
fn with_block(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, block: &ast::Block) -> (ast::Block, SubstitutionMap) {
|
||||||
let mut substitutions = SubstitutionMap::new();
|
let mut substitutions = incoming_substitutions.clone();
|
||||||
let mut block_ctx = ctx.clone();
|
let mut block_ctx = ctx.clone();
|
||||||
return ast::Block{
|
// if return it's always never
|
||||||
statements: block.statements.iter().map(|s| {
|
// if last is expression it's that else unit
|
||||||
let (statement_ctx, result, statement_substitutions) = self.with_statement(block_ctx, s);
|
let mut has_return = false;
|
||||||
block_ctx = statement_ctx
|
for statement in block.statements.iter() {
|
||||||
substitutions = compose_substitutions(substitutions, statement_substitutions);
|
match statement {
|
||||||
result
|
ast::Statement::Return(_) => {
|
||||||
}).collect(),
|
has_return = true;
|
||||||
type_: block.type_.clone(),
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let statements = block.statements.iter().map(|s| {
|
||||||
|
let (statement_ctx, result, statement_substitutions) = self.with_statement(&block_ctx, &substitutions, s);
|
||||||
|
block_ctx = statement_ctx;
|
||||||
|
substitutions = compose_substitutions(&substitutions, &statement_substitutions);
|
||||||
|
result
|
||||||
|
}).collect();
|
||||||
|
if !has_return {
|
||||||
|
match block.statements.last().unwrap() {
|
||||||
|
ast::Statement::Expression(expr) => {
|
||||||
|
substitutions = compose_substitutions(&substitutions, &unify(&block.type_, &expr.type_));
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
substitutions = compose_substitutions(&substitutions, &unify(&block.type_, &ast::new_unit()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result_type = if has_return {
|
||||||
|
ast::new_never()
|
||||||
|
} else {
|
||||||
|
apply_substitution(&substitutions, &block.type_)
|
||||||
};
|
};
|
||||||
|
let block_result = ast::Block{
|
||||||
|
statements: statements,
|
||||||
|
type_: result_type,
|
||||||
|
};
|
||||||
|
return (block_result, substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> (Context, ast::Statement, SubstitutionMap) {
|
fn with_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::Statement) -> (Context, ast::Statement, SubstitutionMap) {
|
||||||
|
|
||||||
match statement {
|
match statement {
|
||||||
ast::Statement::Return(return_statement) => {
|
ast::Statement::Return(return_statement) => {
|
||||||
let (result, subst) = self.with_return_statement(ctx, return_statement);
|
let (result, subst) = self.with_return_statement(ctx, incoming_substitutions, return_statement);
|
||||||
|
let subst = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
return (ctx.clone(), ast::Statement::Return(result), subst);
|
return (ctx.clone(), ast::Statement::Return(result), subst);
|
||||||
},
|
},
|
||||||
ast::Statement::Let(let_statement) => {
|
ast::Statement::Let(let_statement) => {
|
||||||
let (let_ctx, result, subst) = self.with_let_statement(ctx, let_statement);
|
let (let_ctx, result, subst) = self.with_let_statement(ctx, incoming_substitutions, let_statement);
|
||||||
|
let subst = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
return (let_ctx, ast::Statement::Let(result), subst);
|
return (let_ctx, ast::Statement::Let(result), subst);
|
||||||
},
|
},
|
||||||
ast::Statement::Assignment(assignment_statement) => {
|
ast::Statement::Assignment(assignment_statement) => {
|
||||||
let (result, subst) = self.with_assignment_statement(ctx, assignment_statement);
|
let (result, subst) = self.with_assignment_statement(ctx, incoming_substitutions, assignment_statement);
|
||||||
|
let subst = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
return (ctx.clone(), ast::Statement::Assignment(result), subst);
|
return (ctx.clone(), ast::Statement::Assignment(result), subst);
|
||||||
},
|
},
|
||||||
ast::Statement::Expression(expression) => {
|
ast::Statement::Expression(expression) => {
|
||||||
let (result, subst) = self.with_expression(ctx, expression);
|
let (result, subst) = self.with_expression(ctx, incoming_substitutions, expression);
|
||||||
|
let subst = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
return (ctx.clone(), ast::Statement::Expression(result), subst);
|
return (ctx.clone(), ast::Statement::Expression(result), subst);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> (ast::ReturnStatement, SubstitutionMap) {
|
fn with_return_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::ReturnStatement) -> (ast::ReturnStatement, SubstitutionMap) {
|
||||||
let (result, subst) = self.with_expression(ctx, &statement.source);
|
let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.source);
|
||||||
let substitution = compose_substitutions(subst, unify(result.type_, ctx.current_function_return));
|
let mut substitution = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
|
let mut is_never = false;
|
||||||
|
match &result.type_ {
|
||||||
|
ast::TypeUsage::Named(named) => {
|
||||||
|
if named.name.name.value == "!" {
|
||||||
|
is_never = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if !is_never {
|
||||||
|
substitution = compose_substitutions(&subst, &unify(&ctx.current_function_return.as_ref().unwrap(), &result.type_));
|
||||||
|
}
|
||||||
|
|
||||||
return (ast::ReturnStatement{
|
return (ast::ReturnStatement{
|
||||||
source: result,
|
source: result,
|
||||||
}, substitution);
|
}, substitution);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> (Context, ast::LetStatement, SubstitutionMap) {
|
fn with_let_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::LetStatement) -> (Context, ast::LetStatement, SubstitutionMap) {
|
||||||
let (result, subst) = self.with_expression(ctx, &statement.expression);
|
let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression);
|
||||||
let let_ctx = ctx.add_variable(statement.variable_name.clone(), result.type_);
|
let let_ctx = ctx.add_variable(statement.variable_name.name.value.clone(), &result.type_);
|
||||||
let substitution = compose_substitutions(subst, unify(&statement.type_, result.type_));
|
let substitution = compose_substitutions(&subst, &unify(&statement.type_, &result.type_));
|
||||||
return (let_ctx, ast::LetStatement{
|
return (let_ctx, ast::LetStatement{
|
||||||
variable_name: statement.variable_name.clone(),
|
variable_name: statement.variable_name.clone(),
|
||||||
expression: result,
|
expression: result,
|
||||||
type_: &statement.type_.clone(),
|
type_: apply_substitution(&substitution, &statement.type_),
|
||||||
}, substitution);
|
}, substitution);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> ast::AssignmentStatement {
|
fn with_assignment_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::AssignmentStatement) -> (ast::AssignmentStatement, SubstitutionMap) {
|
||||||
|
let (expr, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression);
|
||||||
|
let mut substitution = compose_substitutions(&incoming_substitutions, &subst);
|
||||||
|
|
||||||
return ast::AssignmentStatement{
|
let result_as = ast::AssignmentStatement{
|
||||||
source: match &statement.source {
|
source: match &statement.source {
|
||||||
ast::AssignmentTarget::Variable(variable) => {
|
ast::AssignmentTarget::Variable(variable) => {
|
||||||
let assignment_subs = compose_substitutions(subs, unify(&expr.type_, &variable.type_));
|
substitution = compose_substitutions(&substitution, &unify(&variable.type_, &expr.type_));
|
||||||
(ast::AssignmentTarget::Variable(ast::VariableUsage{
|
ast::AssignmentTarget::Variable(ast::VariableUsage{
|
||||||
name: variable.name.clone(),
|
name: variable.name.clone(),
|
||||||
type_: &variable.type_.clone(),
|
type_: apply_substitution(&substitution, &variable.type_),
|
||||||
}), assignment_subs)
|
})
|
||||||
},
|
},
|
||||||
ast::AssignmentTarget::StructAttr(struct_attr) => {
|
ast::AssignmentTarget::StructAttr(struct_attr) => {
|
||||||
// let assignment_subs = compose_substitutions(subs, unify(&expr.type_, &struct_attr.type_));
|
let (source, subst) = self.with_expression(ctx, &substitution, &struct_attr.source);
|
||||||
let (expr, subst) = self.with_expression(ctx, &struct_attr.source);
|
// TODO: match source attr with type
|
||||||
|
let substitution = compose_substitutions(&compose_substitutions(&substitution, &subst), &unify(&struct_attr.type_, &expr.type_));
|
||||||
(ast::AssignmentTarget::StructAttr(ast::StructGetter{
|
ast::AssignmentTarget::StructAttr(ast::StructGetter{
|
||||||
source: expr,
|
source: source,
|
||||||
attribute: struct_attr.attribute.clone(),
|
attribute: struct_attr.attribute.clone(),
|
||||||
type_: &struct_attr.type_.clone(),
|
type_: apply_substitution(&substitution, &struct_attr.type_),
|
||||||
}), subst)
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expression: expr,
|
expression: expr,
|
||||||
}
|
};
|
||||||
|
return (result_as, substitution);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> (ast::Expression, SubstitutionMap) {
|
fn with_expression(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, expression: &ast::Expression) -> (ast::Expression, SubstitutionMap) {
|
||||||
let mut substitution = SubstitutionMap::new();
|
let mut substitution = incoming_substitutions.clone();
|
||||||
let expr = ast::Expression{
|
let subexpression = Box::new(match &*expression.subexpression {
|
||||||
subexpression: Box::new(match &*expression.subexpression {
|
ast::Subexpression::LiteralInt(literal_int) => {
|
||||||
ast::Subexpression::LiteralInt(literal_int) => {
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_int.type_));
|
||||||
ast::Subexpression::LiteralInt(ast::LiteralInt{
|
ast::Subexpression::LiteralInt(ast::LiteralInt{
|
||||||
value: literal_int.value.clone(),
|
value: literal_int.value.clone(),
|
||||||
type_: literal_int.type_.clone(),
|
type_: apply_substitution(&substitution, &literal_int.type_),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::Subexpression::LiteralFloat(literal_float) => {
|
ast::Subexpression::LiteralFloat(literal_float) => {
|
||||||
ast::Subexpression::LiteralFloat(ast::LiteralFloat{
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_float.type_));
|
||||||
value: literal_float.value.clone(),
|
ast::Subexpression::LiteralFloat(ast::LiteralFloat{
|
||||||
type_: literal_float.type_.clone(),
|
value: literal_float.value.clone(),
|
||||||
})
|
type_: apply_substitution(&substitution, &literal_float.type_),
|
||||||
},
|
})
|
||||||
ast::Subexpression::LiteralStruct(literal_struct) => {
|
},
|
||||||
ast::Subexpression::LiteralStruct(ast::LiteralStruct{
|
ast::Subexpression::LiteralStruct(literal_struct) => {
|
||||||
name: literal_struct.name.clone(),
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_struct.type_));
|
||||||
fields: literal_struct.fields.iter().map(|field|{
|
let type_declaration = match &ctx.environment[&literal_struct.name.name.value] {
|
||||||
|
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => {
|
||||||
|
type_declaration
|
||||||
|
},
|
||||||
|
_ => {panic!("literal struct used with non struct name")}
|
||||||
|
};
|
||||||
|
ast::Subexpression::LiteralStruct(ast::LiteralStruct{
|
||||||
|
name: literal_struct.name.clone(),
|
||||||
|
fields: literal_struct.fields.iter().map(|field|{
|
||||||
|
let (result, subst) = self.with_expression(ctx, &substitution, &field.1);
|
||||||
|
|
||||||
// substitution = compose_substitutions(substitution, );
|
for type_field in type_declaration.fields.iter() {
|
||||||
(field.0.clone(), self.with_expression(ctx, &field.1))
|
if type_field.name.name.value == field.0.name.value {
|
||||||
}).collect(),
|
substitution = compose_substitutions(&substitution, &unify(&type_field.type_, &result.type_));
|
||||||
type_: literal_struct.type_.clone(),
|
}
|
||||||
})
|
}
|
||||||
},
|
|
||||||
ast::Subexpression::FunctionCall(function_call) => {
|
substitution = compose_substitutions(&substitution, &subst);
|
||||||
ast::Subexpression::FunctionCall(ast::FunctionCall{
|
(field.0.clone(), result)
|
||||||
source: self.with_expression(ctx, &function_call.source),
|
}).collect(),
|
||||||
arguments: function_call.arguments.iter().map(|arg| {self.with_expression(ctx, arg)}).collect(),
|
type_: apply_substitution(&substitution, &literal_struct.type_),
|
||||||
type_: function_call.type_.clone(),
|
})
|
||||||
})
|
},
|
||||||
},
|
ast::Subexpression::FunctionCall(function_call) => {
|
||||||
ast::Subexpression::VariableUsage(variable_usage) => {
|
let (source, subst) = self.with_expression(ctx, &substitution, &function_call.source);
|
||||||
match ctx.environment[variable_usage.name] {
|
substitution = compose_substitutions(&substitution, &subst);
|
||||||
NamedEntity::TypeDeclaration(_) => {
|
match &source.type_ {
|
||||||
panic!("Using types not yet supported");
|
ast::TypeUsage::Function(fn_type) => {
|
||||||
},
|
substitution = compose_substitutions(&substitution, &unify(&function_call.type_, &*fn_type.return_type));
|
||||||
NamedEntity::Variable(variable) => {
|
if function_call.arguments.len() != fn_type.arguments.len() {
|
||||||
substitution = compose_substitutions(substitution, unify(variable, expression.type_));
|
panic!("mismatched function argument count");
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
ast::Subexpression::VariableUsage(ast::VariableUsage{
|
ast::TypeUsage::Named(_) => panic!("FunctionCall doesn't have function type."),
|
||||||
name: variable_usage.name.clone(),
|
_ => {},
|
||||||
type_: variable_usage.type_.clone(),
|
}
|
||||||
})
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &function_call.type_));
|
||||||
},
|
ast::Subexpression::FunctionCall(ast::FunctionCall{
|
||||||
ast::Subexpression::StructGetter(struct_getter) => {
|
source: source.clone(),
|
||||||
ast::Subexpression::StructGetter(ast::StructGetter{
|
arguments: function_call.arguments.iter().enumerate().map(|(i, arg)| {
|
||||||
source: self.with_expression(ctx, &struct_getter.source),
|
let (result, subst) = self.with_expression(ctx, &substitution, arg);
|
||||||
attribute: struct_getter.attribute.clone(),
|
substitution = compose_substitutions(&substitution, &subst);
|
||||||
type_: struct_getter.type_.clone(),
|
|
||||||
})
|
match &source.type_ {
|
||||||
},
|
ast::TypeUsage::Function(fn_type) => {
|
||||||
ast::Subexpression::Block(block) => {
|
substitution = compose_substitutions(&substitution, &unify(&fn_type.arguments[i], &result.type_));
|
||||||
let (result, substitution) = self.with_block(ctx, &block);
|
},
|
||||||
substitution = compose_substitutions(substitution, unify(expression.type_, block.type_));
|
ast::TypeUsage::Named(_) => panic!("FunctionCall doesn't have function type."),
|
||||||
ast::Subexpression::Block(result)
|
_ => {},
|
||||||
},
|
}
|
||||||
ast::Subexpression::Op(op) => {
|
result
|
||||||
let expr_left, subst_left = self.with_expression(ctx, &op.left);
|
}).collect(),
|
||||||
let expr_right, subst_right = self.with_expression(ctx, &op.right);
|
type_: apply_substitution(&substitution, &function_call.type_),
|
||||||
substitution = compose_substitutions(substitution, subst_left);
|
})
|
||||||
substitution = compose_substitutions(substitution, subst_right);
|
},
|
||||||
substitution = compose_substitutions(substitution, unify(expression.type_, expr_left.type_));
|
ast::Subexpression::VariableUsage(variable_usage) => {
|
||||||
substitution = compose_substitutions(substitution, unify(expression.type_, expr_right.type_));
|
match &ctx.environment[&variable_usage.name.name.value] {
|
||||||
ast::Subexpression::Op(ast::Operation{
|
NamedEntity::TypeDeclaration(_) => {
|
||||||
left: expr_left,
|
panic!("Using types not yet supported");
|
||||||
op: op.op.clone(),
|
},
|
||||||
right: expr_right,
|
NamedEntity::Variable(variable) => {
|
||||||
})
|
substitution = compose_substitutions(&substitution, &unify(&variable_usage.type_, &variable));
|
||||||
},
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &variable_usage.type_));
|
||||||
}),
|
},
|
||||||
type_: expression.type_.clone(),
|
}
|
||||||
|
ast::Subexpression::VariableUsage(ast::VariableUsage{
|
||||||
|
name: variable_usage.name.clone(),
|
||||||
|
type_: apply_substitution(&substitution, &variable_usage.type_),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
ast::Subexpression::StructGetter(struct_getter) => {
|
||||||
|
let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source);
|
||||||
|
substitution = compose_substitutions(&substitution, &subst);
|
||||||
|
|
||||||
|
match &source.type_ {
|
||||||
|
ast::TypeUsage::Named(named) => {
|
||||||
|
match &ctx.environment[&named.name.name.value] {
|
||||||
|
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => {
|
||||||
|
let mut found = false;
|
||||||
|
for field in type_declaration.fields.iter() {
|
||||||
|
if field.name.name.value == struct_getter.attribute.name.value {
|
||||||
|
found = true;
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&struct_getter.type_, &field.type_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
println!("foo: {:?} {:?}", &type_declaration.name.name.value, struct_getter.attribute.name.value);
|
||||||
|
for method in ctx.impls[&type_declaration.name.name.value].functions.iter() {
|
||||||
|
println!("foo: {:?} {:?}", &method.declaration.name.name.value, struct_getter.attribute.name.value);
|
||||||
|
if method.declaration.name.name.value == struct_getter.attribute.name.value {
|
||||||
|
let mut function_type = ast::FunctionTypeUsage{
|
||||||
|
arguments: method.declaration.arguments.iter().map(|arg|{arg.type_.clone()}).collect(),
|
||||||
|
return_type: Box::new(method.declaration.return_type.clone()),
|
||||||
|
};
|
||||||
|
// if the name of the type of the first argument == the class, remove the first arg
|
||||||
|
if function_type.arguments.len() > 0 {
|
||||||
|
match &function_type.arguments[0] {
|
||||||
|
ast::TypeUsage::Named(named) => {
|
||||||
|
if named.name.name.value == type_declaration.name.name.value {
|
||||||
|
function_type = ast::FunctionTypeUsage{
|
||||||
|
arguments: method.declaration.arguments[1..method.declaration.arguments.len()].iter().map(|arg|{arg.type_.clone()}).collect(),
|
||||||
|
return_type: Box::new(method.declaration.return_type.clone()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("found: {:?}", &function_type);
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&struct_getter.type_, &ast::TypeUsage::Function(function_type)));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
panic!("unknown field name")
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
_ => panic!("struct getter being used on non-struct")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::TypeUsage::Function(_) => {
|
||||||
|
panic!("function used with attr")
|
||||||
|
},
|
||||||
|
_ => {} // skip unifying if struct type is unknown1
|
||||||
|
}
|
||||||
|
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &struct_getter.type_));
|
||||||
|
|
||||||
|
ast::Subexpression::StructGetter(ast::StructGetter{
|
||||||
|
source: source,
|
||||||
|
attribute: struct_getter.attribute.clone(),
|
||||||
|
type_: apply_substitution(&substitution, &struct_getter.type_),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
ast::Subexpression::Block(block) => {
|
||||||
|
let (result, subst) = self.with_block(ctx, &substitution, &block);
|
||||||
|
substitution = compose_substitutions(&substitution, &subst);
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &result.type_));
|
||||||
|
println!("foo {:?} {:?}", &expression.type_, &block.type_);
|
||||||
|
ast::Subexpression::Block(result)
|
||||||
|
},
|
||||||
|
ast::Subexpression::Op(op) => {
|
||||||
|
let (expr_left, subst_left) = self.with_expression(ctx, &substitution, &op.left);
|
||||||
|
let (expr_right, subst_right) = self.with_expression(ctx, &substitution, &op.right);
|
||||||
|
substitution = compose_substitutions(&substitution, &subst_left);
|
||||||
|
substitution = compose_substitutions(&substitution, &subst_right);
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &expr_left.type_));
|
||||||
|
substitution = compose_substitutions(&substitution, &unify(&expression.type_, &expr_right.type_));
|
||||||
|
ast::Subexpression::Op(ast::Operation{
|
||||||
|
left: expr_left,
|
||||||
|
op: op.op.clone(),
|
||||||
|
right: expr_right,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let expr = ast::Expression{
|
||||||
|
subexpression: subexpression,
|
||||||
|
type_: apply_substitution(&substitution, &expression.type_),
|
||||||
};
|
};
|
||||||
return (expr, substitution);
|
return (expr, substitution);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user