mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-23 00:18:24 +00:00
progress
This commit is contained in:
parent
8c212d948d
commit
071f5ce0f9
70
src/check.rs
70
src/check.rs
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
codegen::ProgramData,
|
codegen::ProgramData,
|
||||||
lexer::LexicalError,
|
lexer::LexicalError,
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
|
type_analysis::TypeError,
|
||||||
};
|
};
|
||||||
use annotate_snippets::{
|
use annotate_snippets::{
|
||||||
display_list::{DisplayList, FormatOptions},
|
display_list::{DisplayList, FormatOptions},
|
||||||
|
@ -97,7 +98,6 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
line_start: 1,
|
line_start: 1,
|
||||||
fold: true,
|
fold: true,
|
||||||
origin: None,
|
origin: None,
|
||||||
|
|
||||||
annotations: vec![SourceAnnotation {
|
annotations: vec![SourceAnnotation {
|
||||||
label: "invalid token",
|
label: "invalid token",
|
||||||
annotation_type: AnnotationType::Error,
|
annotation_type: AnnotationType::Error,
|
||||||
|
@ -126,14 +126,14 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
}),
|
}),
|
||||||
footer: vec![],
|
footer: vec![],
|
||||||
slices: vec![Slice {
|
slices: vec![Slice {
|
||||||
source: dbg!(source),
|
source: source,
|
||||||
line_start: 1,
|
line_start: 1,
|
||||||
fold: false,
|
fold: false,
|
||||||
origin: None,
|
origin: None,
|
||||||
annotations: vec![SourceAnnotation {
|
annotations: vec![SourceAnnotation {
|
||||||
label: "invalid token (lexical error)",
|
label: "invalid token (lexical error)",
|
||||||
annotation_type: AnnotationType::Error,
|
annotation_type: AnnotationType::Error,
|
||||||
range: dbg!((range.start, range.end)),
|
range: (range.start, range.end),
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
opt: FormatOptions {
|
opt: FormatOptions {
|
||||||
|
@ -147,3 +147,67 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_type_error(source: &str, err: TypeError) {
|
||||||
|
dbg!(&err);
|
||||||
|
match err {
|
||||||
|
TypeError::Mismatch {
|
||||||
|
found,
|
||||||
|
expected,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let snippet = Snippet {
|
||||||
|
title: Some(Annotation {
|
||||||
|
id: None,
|
||||||
|
label: Some("type mismatch"),
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
}),
|
||||||
|
footer: vec![],
|
||||||
|
slices: vec![Slice {
|
||||||
|
source,
|
||||||
|
line_start: 1,
|
||||||
|
fold: false,
|
||||||
|
origin: None,
|
||||||
|
annotations: vec![SourceAnnotation {
|
||||||
|
label: "type mismatch",
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
range: span,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
opt: FormatOptions {
|
||||||
|
color: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let dl = DisplayList::from(snippet);
|
||||||
|
println!("{dl}");
|
||||||
|
}
|
||||||
|
TypeError::UndeclaredVariable { name, span } => {
|
||||||
|
let snippet = Snippet {
|
||||||
|
title: Some(Annotation {
|
||||||
|
id: None,
|
||||||
|
label: Some("undeclared variable"),
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
}),
|
||||||
|
footer: vec![],
|
||||||
|
slices: vec![Slice {
|
||||||
|
source,
|
||||||
|
line_start: 1,
|
||||||
|
fold: false,
|
||||||
|
origin: None,
|
||||||
|
annotations: vec![SourceAnnotation {
|
||||||
|
label: "undeclared variable",
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
range: span,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
opt: FormatOptions {
|
||||||
|
color: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let dl = DisplayList::from(snippet);
|
||||||
|
println!("{dl}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -178,12 +178,12 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
||||||
args_types.into_iter().map(|t| t.into()).collect_vec();
|
args_types.into_iter().map(|t| t.into()).collect_vec();
|
||||||
|
|
||||||
let (fn_type, ret_type) = match &function.return_type {
|
let fn_type = match &function.return_type {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
let return_type = self.get_llvm_type(id)?;
|
let return_type = self.get_llvm_type(id)?;
|
||||||
(return_type.fn_type(&args_types, false), Some(id.clone()))
|
return_type.fn_type(&args_types, false)
|
||||||
}
|
}
|
||||||
None => (self.context.void_type().fn_type(&args_types, false), None),
|
None => self.context.void_type().fn_type(&args_types, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.module.add_function(&function.name, fn_type, None);
|
self.module.add_function(&function.name, fn_type, None);
|
||||||
|
@ -447,6 +447,8 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.compile_expression(rhs, variables, scope_info)?
|
.compile_expression(rhs, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
|
assert_eq!(lhs.get_type(), rhs.get_type(), "type mismatch");
|
||||||
|
|
||||||
let lhs = lhs.into_int_value();
|
let lhs = lhs.into_int_value();
|
||||||
let rhs = rhs.into_int_value();
|
let rhs = rhs.into_int_value();
|
||||||
|
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -3,12 +3,15 @@
|
||||||
use check::print_error;
|
use check::print_error;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use codegen::ProgramData;
|
use codegen::ProgramData;
|
||||||
use color_eyre::Result;
|
|
||||||
use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel};
|
use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel};
|
||||||
use lalrpop_util::lalrpop_mod;
|
use lalrpop_util::lalrpop_mod;
|
||||||
use std::{fs, path::PathBuf, println};
|
use std::{fs, path::PathBuf, println};
|
||||||
|
|
||||||
use crate::{ast::Program, check::Check, lexer::Lexer};
|
use crate::{
|
||||||
|
ast::Program,
|
||||||
|
check::{print_type_error, Check},
|
||||||
|
lexer::Lexer,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod check;
|
pub mod check;
|
||||||
|
@ -91,7 +94,7 @@ fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
|
||||||
error_count == 0
|
error_count == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
@ -102,7 +105,10 @@ fn main() -> Result<()> {
|
||||||
let lexer = Lexer::new(code.as_str());
|
let lexer = Lexer::new(code.as_str());
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
let mut ast = parser.parse(lexer)?;
|
let mut ast = parser.parse(lexer)?;
|
||||||
type_analysis::type_inference(&mut ast);
|
if let Err(e) = type_analysis::type_check(&mut ast) {
|
||||||
|
print_type_error(&code, e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
check_program(&program, &ast);
|
check_program(&program, &ast);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +118,10 @@ fn main() -> Result<()> {
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
match parser.parse(lexer) {
|
match parser.parse(lexer) {
|
||||||
Ok(mut ast) => {
|
Ok(mut ast) => {
|
||||||
type_analysis::type_inference(&mut ast);
|
if let Err(e) = type_analysis::type_check(&mut ast) {
|
||||||
|
print_type_error(&code, e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
println!("{ast:#?}");
|
println!("{ast:#?}");
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -130,7 +139,10 @@ fn main() -> Result<()> {
|
||||||
let lexer = Lexer::new(code.as_str());
|
let lexer = Lexer::new(code.as_str());
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
let mut ast: Program = parser.parse(lexer)?;
|
let mut ast: Program = parser.parse(lexer)?;
|
||||||
type_analysis::type_inference(&mut ast);
|
if let Err(e) = type_analysis::type_check(&mut ast) {
|
||||||
|
print_type_error(&code, e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
|
|
||||||
|
@ -156,7 +168,11 @@ fn main() -> Result<()> {
|
||||||
let code = fs::read_to_string(&input)?;
|
let code = fs::read_to_string(&input)?;
|
||||||
let lexer = Lexer::new(&code[..]);
|
let lexer = Lexer::new(&code[..]);
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
let ast = parser.parse(lexer).unwrap();
|
let mut ast: Program = parser.parse(lexer)?;
|
||||||
|
if let Err(e) = type_analysis::type_check(&mut ast) {
|
||||||
|
print_type_error(&code, e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,30 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::ast::{self, Expression, Function, Statement, TypeExp};
|
use crate::ast::{self, Expression, Function, Statement, TypeExp};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypeError {
|
||||||
|
Mismatch {
|
||||||
|
found: TypeExp,
|
||||||
|
expected: TypeExp,
|
||||||
|
span: (usize, usize),
|
||||||
|
},
|
||||||
|
UndeclaredVariable {
|
||||||
|
name: String,
|
||||||
|
span: (usize, usize),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct Storage {
|
struct Storage {
|
||||||
structs: HashMap<String, HashMap<String, TypeExp>>,
|
structs: HashMap<String, HashMap<String, TypeExp>>,
|
||||||
functions: HashMap<String, Function>,
|
functions: HashMap<String, Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// problem with scopes,
|
|
||||||
// let x = 2;
|
|
||||||
// let x = 2i64;
|
|
||||||
|
|
||||||
type ScopeMap = HashMap<String, Vec<Option<TypeExp>>>;
|
type ScopeMap = HashMap<String, Vec<Option<TypeExp>>>;
|
||||||
|
|
||||||
// this works, but need to find a way to store the found info + handle literal integer types (or not?)
|
// this works, but need to find a way to store the found info + handle literal integer types (or not?)
|
||||||
// maybe use scope ids
|
// maybe use scope ids
|
||||||
pub fn type_inference(ast: &mut ast::Program) {
|
pub fn type_check(ast: &mut ast::Program) -> Result<(), TypeError> {
|
||||||
let mut storage = Storage::default();
|
let mut storage = Storage::default();
|
||||||
|
|
||||||
// gather global constructs first
|
// gather global constructs first
|
||||||
|
@ -40,8 +49,6 @@ pub fn type_inference(ast: &mut ast::Program) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&storage);
|
|
||||||
|
|
||||||
for statement in ast.statements.iter_mut() {
|
for statement in ast.statements.iter_mut() {
|
||||||
if let Statement::Function(function) = statement {
|
if let Statement::Function(function) = statement {
|
||||||
let mut scope_vars: ScopeMap = HashMap::new();
|
let mut scope_vars: ScopeMap = HashMap::new();
|
||||||
|
@ -52,7 +59,7 @@ pub fn type_inference(ast: &mut ast::Program) {
|
||||||
|
|
||||||
let func_info = function.clone();
|
let func_info = function.clone();
|
||||||
let (new_scope_vars, _) =
|
let (new_scope_vars, _) =
|
||||||
type_inference_scope(&mut function.body, &scope_vars, &func_info, &storage);
|
type_inference_scope(&mut function.body, &scope_vars, &func_info, &storage)?;
|
||||||
// todo: check all vars have type info?
|
// todo: check all vars have type info?
|
||||||
function.scope_type_info = new_scope_vars
|
function.scope_type_info = new_scope_vars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -60,6 +67,7 @@ pub fn type_inference(ast: &mut ast::Program) {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds variable types in the scope, returns newly created variables to handle shadowing
|
/// Finds variable types in the scope, returns newly created variables to handle shadowing
|
||||||
|
@ -68,7 +76,7 @@ fn type_inference_scope(
|
||||||
scope_vars: &ScopeMap,
|
scope_vars: &ScopeMap,
|
||||||
func: &Function,
|
func: &Function,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
) -> (ScopeMap, HashSet<String>) {
|
) -> Result<(ScopeMap, HashSet<String>), TypeError> {
|
||||||
let mut scope_vars = scope_vars.clone();
|
let mut scope_vars = scope_vars.clone();
|
||||||
let mut new_vars: HashSet<String> = HashSet::new();
|
let mut new_vars: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
|
@ -78,11 +86,11 @@ fn type_inference_scope(
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
value_type,
|
value_type,
|
||||||
span: _,
|
span,
|
||||||
} => {
|
} => {
|
||||||
new_vars.insert(name.clone());
|
new_vars.insert(name.clone());
|
||||||
|
|
||||||
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None);
|
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
|
||||||
|
|
||||||
if !scope_vars.contains_key(name) {
|
if !scope_vars.contains_key(name) {
|
||||||
scope_vars.insert(name.clone(), vec![]);
|
scope_vars.insert(name.clone(), vec![]);
|
||||||
|
@ -94,27 +102,31 @@ fn type_inference_scope(
|
||||||
var.push(exp_type);
|
var.push(exp_type);
|
||||||
} else {
|
} else {
|
||||||
if exp_type.is_some() && &exp_type != value_type {
|
if exp_type.is_some() && &exp_type != value_type {
|
||||||
panic!("let type mismatch: {:?} != {:?}", value_type, exp_type);
|
Err(TypeError::Mismatch {
|
||||||
|
found: exp_type.clone().unwrap(),
|
||||||
|
expected: value_type.clone().unwrap(),
|
||||||
|
span: *span,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
var.push(value_type.clone());
|
var.push(value_type.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Mutate {
|
Statement::Mutate { name, value, span } => {
|
||||||
name,
|
|
||||||
value,
|
|
||||||
span: _,
|
|
||||||
} => {
|
|
||||||
if !scope_vars.contains_key(name) {
|
if !scope_vars.contains_key(name) {
|
||||||
panic!("undeclared variable");
|
panic!("undeclared variable");
|
||||||
}
|
}
|
||||||
|
|
||||||
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None);
|
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
|
||||||
let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap();
|
let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap();
|
||||||
|
|
||||||
if var.is_none() {
|
if var.is_none() {
|
||||||
*var = exp_type;
|
*var = exp_type;
|
||||||
} else if exp_type.is_some() && &exp_type != var {
|
} else if exp_type.is_some() && &exp_type != var {
|
||||||
panic!("mutate type mismatch: {:?} != {:?}", var, exp_type);
|
Err(TypeError::Mismatch {
|
||||||
|
found: exp_type.clone().unwrap(),
|
||||||
|
expected: var.clone().unwrap(),
|
||||||
|
span: *span,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::If {
|
Statement::If {
|
||||||
|
@ -129,10 +141,10 @@ fn type_inference_scope(
|
||||||
&mut scope_vars,
|
&mut scope_vars,
|
||||||
storage,
|
storage,
|
||||||
Some(TypeExp::Boolean),
|
Some(TypeExp::Boolean),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let (new_scope_vars, new_vars) =
|
let (new_scope_vars, new_vars) =
|
||||||
type_inference_scope(body, &scope_vars, func, storage);
|
type_inference_scope(body, &scope_vars, func, storage)?;
|
||||||
|
|
||||||
for (k, v) in new_scope_vars.iter() {
|
for (k, v) in new_scope_vars.iter() {
|
||||||
// not a new var within the scope (shadowing), so type info is valid
|
// not a new var within the scope (shadowing), so type info is valid
|
||||||
|
@ -148,7 +160,7 @@ fn type_inference_scope(
|
||||||
|
|
||||||
if let Some(body) = else_body {
|
if let Some(body) = else_body {
|
||||||
let (new_scope_vars, new_vars) =
|
let (new_scope_vars, new_vars) =
|
||||||
type_inference_scope(body, &scope_vars, func, storage);
|
type_inference_scope(body, &scope_vars, func, storage)?;
|
||||||
|
|
||||||
for (k, v) in new_scope_vars.iter() {
|
for (k, v) in new_scope_vars.iter() {
|
||||||
// not a new var within the scope (shadowing), so type info is valid
|
// not a new var within the scope (shadowing), so type info is valid
|
||||||
|
@ -170,7 +182,7 @@ fn type_inference_scope(
|
||||||
&mut scope_vars,
|
&mut scope_vars,
|
||||||
storage,
|
storage,
|
||||||
func.return_type.clone(),
|
func.return_type.clone(),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Function(_) => unreachable!(),
|
Statement::Function(_) => unreachable!(),
|
||||||
|
@ -178,7 +190,7 @@ fn type_inference_scope(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(scope_vars, new_vars)
|
Ok((scope_vars, new_vars))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_inference_expression(
|
fn type_inference_expression(
|
||||||
|
@ -186,8 +198,8 @@ fn type_inference_expression(
|
||||||
scope_vars: &mut ScopeMap,
|
scope_vars: &mut ScopeMap,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
expected_type: Option<TypeExp>,
|
expected_type: Option<TypeExp>,
|
||||||
) -> Option<TypeExp> {
|
) -> Result<Option<TypeExp>, TypeError> {
|
||||||
match exp {
|
Ok(match exp {
|
||||||
Expression::Literal(lit) => {
|
Expression::Literal(lit) => {
|
||||||
match lit {
|
match lit {
|
||||||
ast::LiteralValue::String(_) => None, // todo
|
ast::LiteralValue::String(_) => None, // todo
|
||||||
|
@ -214,7 +226,13 @@ fn type_inference_expression(
|
||||||
*var = expected_type.clone();
|
*var = expected_type.clone();
|
||||||
expected_type
|
expected_type
|
||||||
} else if expected_type.is_some() {
|
} else if expected_type.is_some() {
|
||||||
assert_eq!(*var, expected_type, "type mismatch with variables");
|
if *var != expected_type {
|
||||||
|
Err(TypeError::Mismatch {
|
||||||
|
found: expected_type.clone().unwrap(),
|
||||||
|
expected: var.clone().unwrap(),
|
||||||
|
span: name.span,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
expected_type
|
expected_type
|
||||||
} else {
|
} else {
|
||||||
var.clone()
|
var.clone()
|
||||||
|
@ -229,7 +247,7 @@ fn type_inference_expression(
|
||||||
for (i, arg) in args.iter().enumerate() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
let arg_type = func.params[i].type_exp.clone();
|
let arg_type = func.params[i].type_exp.clone();
|
||||||
// result is ignored, but need these to infer call arg types
|
// result is ignored, but need these to infer call arg types
|
||||||
type_inference_expression(arg, scope_vars, storage, Some(arg_type));
|
type_inference_expression(arg, scope_vars, storage, Some(arg_type))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
func.return_type
|
func.return_type
|
||||||
|
@ -238,15 +256,19 @@ fn type_inference_expression(
|
||||||
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
|
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
|
||||||
_ => {
|
_ => {
|
||||||
let lhs_type =
|
let lhs_type =
|
||||||
type_inference_expression(lhs, scope_vars, storage, expected_type.clone());
|
type_inference_expression(lhs, scope_vars, storage, expected_type.clone())?;
|
||||||
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type);
|
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type)?;
|
||||||
|
|
||||||
if lhs_type.is_some() && rhs_type.is_some() {
|
if lhs_type.is_some() && rhs_type.is_some() && lhs_type != rhs_type {
|
||||||
assert_eq!(lhs_type, rhs_type, "types should match");
|
Err(TypeError::Mismatch {
|
||||||
|
found: rhs_type.clone().unwrap(),
|
||||||
|
expected: lhs_type.clone().unwrap(),
|
||||||
|
span: (0, 0), // todo
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs_type.or(rhs_type)
|
lhs_type.or(rhs_type)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue