Files
leaf/compiler/src/scope.rs
T

1127 lines
32 KiB
Rust
Executable File

use crate::{CompilationContext, FuncQueue, diagnostics::*, events::Event, metadata::CodePosition};
use arcstr::{Substr, literal_substr};
use leaf_assembly::{
assembly::Assembly,
functions::{
Function,
ir::{Cmp, FunctionBodyBuilder, PhiValue},
},
types::{
IntT, Type,
compound::{Field, FieldMap, StructT},
derivations::{PtrT, RefT},
},
values::{AnyConst, AnyValue, Int, Value, ValueFlags},
};
use leaf_parser::{
SourceCode,
ast::{self, *},
};
use std::{
borrow::Cow,
collections::HashMap,
ops::Range,
sync::{Arc, OnceLock},
};
struct ExpressionContext<'l, 'r> {
decl_names: Option<&'r NamePattern>,
type_hint: Option<Type<'l>>,
builder: Option<&'r mut FunctionBodyBuilder<'l>>,
fn_queue: &'r mut FuncQueue<'l>,
}
impl<'l, 'r, 'a> ExpressionContext<'l, 'r> {
pub fn with_type_hit(&'a mut self, type_hint: Option<Type<'l>>) -> ExpressionContext<'l, 'a> {
ExpressionContext {
decl_names: self.decl_names,
type_hint,
builder: self.builder.as_mut().map(|b| &mut **b),
fn_queue: self.fn_queue,
}
}
}
#[derive(Clone)]
struct Variable<'l> {
value: Arc<OnceLock<AnyValue<'l>>>,
}
#[derive(Clone)]
pub struct Scope<'l> {
ctx: &'l CompilationContext<'l>,
assembly: &'l Assembly<'l>,
source: Arc<SourceCode>,
values: HashMap<Substr, Variable<'l>>,
}
impl<'l> Scope<'l> {
pub fn new(
ctx: &'l CompilationContext<'l>,
assembly: &'l Assembly<'l>,
source: Arc<SourceCode>,
) -> Self {
Self {
ctx,
assembly,
source,
values: HashMap::default(),
}
}
pub fn insert(&mut self, name: Substr, value: AnyValue<'l>) {
self.values.insert(
name,
Variable {
value: Arc::new(OnceLock::from(value)),
},
);
}
pub fn declare_constants(&mut self, decl: &[AstNode<ConstDecl>]) {
for val in decl {
for range in val.names.as_slice() {
let name = self.get_text_arc(range);
self.values.insert(
name,
Variable {
value: Arc::default(),
},
);
}
}
}
pub fn define_constants(
&mut self,
decl: &[AstNode<ConstDecl>],
fn_queue: &mut FuncQueue<'l>,
) -> Result<(), Diagnostic> {
for val in decl {
let expr = self.compile_expression(
&val.value,
&mut ExpressionContext {
decl_names: Some(&val.names),
builder: None,
type_hint: None,
fn_queue,
},
)?;
match &*val.names {
NamePattern::Single(ident) => {
let name = self.get_text(ident).to_string();
self.values
.get_mut(&*name)
.unwrap()
.value
.set(expr)
.unwrap();
self.ctx.emit_event(Event::Definition {
value: expr,
position: CodePosition::new(self.source.clone(), ident.clone()),
});
}
NamePattern::Tuple(_) => todo!(),
NamePattern::List(_) => todo!(),
}
}
Ok(())
}
pub fn compile_function(
&mut self,
func: &'l Function<'l>,
block: &Arc<AstNode<ast::Block>>,
fn_queue: &mut FuncQueue<'l>,
) -> Result<(), Diagnostic> {
let mut builder = func.create_body().unwrap();
let mut ret = self.compile_block(
block,
&mut ExpressionContext {
builder: Some(&mut builder),
decl_names: None,
type_hint: Some(func.ty.ret_t),
fn_queue: fn_queue,
},
)?;
if !builder.current_block().has_termination() {
match func.ty.ret_t {
Type::Void => {
builder.ret(None).unwrap();
}
_ => {
if ret.is_lvalue() {
ret = builder.load(ret).unwrap();
}
self.assert_ty_eq(
&ret,
&AstNode {
range: block.range.clone(),
node: Expr::Block(block.clone()),
},
&func.ty.ret_t,
)?;
builder.ret(Some(ret)).unwrap();
}
};
}
builder.build().unwrap();
Ok(())
}
fn compile_expression(
&mut self,
expr: &AstNode<Expr>,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, Diagnostic> {
match &**expr {
Expr::Ident(range) => {
let name = self.get_text(range);
match self.values.get(&*name) {
None => Err(Diagnostic {
kind: Kind::Error,
code: Code::SymbolNotFound,
message: format!("Symbol `{name}` does not exist in the current scope."),
position: CodePosition::new(self.source.clone(), range.clone()),
cause: None,
}),
Some(Variable { value, .. }) => match value.get() {
None => Err(Diagnostic {
kind: Kind::Error,
code: Code::UninitializedSymbol,
message: format!("Symbol `{name}` is not initialized at this time."),
position: CodePosition::new(self.source.clone(), range.clone()),
cause: None,
}),
Some(value) => {
self.ctx.emit_event(Event::Symbol {
value: *value,
position: CodePosition::new(self.source.clone(), range.clone()),
});
Ok(*value)
}
},
}
}
Expr::Func(func) => self
.make_function(func, ctx)
.map(|f| AnyValue::Constant(AnyConst::Function(f)))
.map_err(|err| Diagnostic {
kind: Kind::Error,
code: Code::FunctionCompilationFailed,
message: "Could not compile function.".to_string(),
position: CodePosition::new(self.source.clone(), expr.range.clone()),
cause: Some(Box::new(err)),
}),
Expr::ConstDecl(decl) => self.compile_decl(&decl.names, &decl.value, false, ctx),
Expr::VarDecl(decl) => self.compile_decl(&decl.names, &decl.value, true, ctx),
Expr::Number(n) => {
macro_rules! parse_number {
($ty: ty, $id: ident) => {{
let text = self.get_text(&n.number);
match text.split_at_checked(2) {
Some(("0b", value)) => <$ty>::from_str_radix(value, 2),
Some(("0x", value)) => <$ty>::from_str_radix(value, 16),
_ => text.parse::<$ty>(),
}
.map(|v| AnyValue::Constant(AnyConst::Int(Int::$id(v))))
.map_err(|_| Diagnostic {
kind: Kind::Error,
code: Code::InvalidInteger,
message: format!("`{}` is not a valid integer.", text),
position: CodePosition::new(self.source.clone(), n.number.clone()),
cause: None,
})
}};
}
let ty = n.ty.as_ref().map(|v| self.get_text(v));
let value = match ty.as_ref().map(|ty| &**ty) {
None if ctx.type_hint == Some(Type::I8) => parse_number!(i8, I8),
None if ctx.type_hint == Some(Type::I16) => parse_number!(i16, I16),
None if ctx.type_hint == Some(Type::I32) => parse_number!(i32, I32),
None if ctx.type_hint == Some(Type::I64) => parse_number!(i64, I64),
None if ctx.type_hint == Some(Type::I128) => parse_number!(i128, I128),
None if ctx.type_hint == Some(Type::ISIZE) => parse_number!(i64, ISize),
None if ctx.type_hint == Some(Type::U8) => parse_number!(u8, U8),
None if ctx.type_hint == Some(Type::U16) => parse_number!(u16, U16),
None if ctx.type_hint == Some(Type::U32) => parse_number!(u32, U32),
None if ctx.type_hint == Some(Type::U64) => parse_number!(u64, U64),
None if ctx.type_hint == Some(Type::U128) => parse_number!(u128, U128),
None if ctx.type_hint == Some(Type::USIZE) => parse_number!(u64, USize),
None => parse_number!(i64, ISize),
Some("i8") => parse_number!(i8, I8),
Some("i16") => parse_number!(i16, I16),
Some("i32") => parse_number!(i32, I32),
Some("i64") => parse_number!(i64, I64),
Some("i128") => parse_number!(i128, I128),
Some("isize") => parse_number!(i64, ISize),
Some("u8") => parse_number!(u8, U8),
Some("u16") => parse_number!(u16, U16),
Some("u32") => parse_number!(u32, U32),
Some("u64") => parse_number!(u64, U64),
Some("u128") => parse_number!(u128, U128),
Some("usize") => parse_number!(u64, USize),
Some(ty) => Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidIntegerType,
message: format!("`{ty}` is not a valid integer type."),
position: CodePosition::new(self.source.clone(), n.ty.clone().unwrap()),
cause: None,
}),
}?;
self.ctx.emit_event(Event::Symbol {
value: value,
position: CodePosition::new(self.source.clone(), n.number.clone()),
});
Ok(value)
}
Expr::Access {
value: value_expr,
operator,
field,
} => {
let value = self.compile_expression(value_expr, ctx)?;
let name = self.get_text(field);
if let Some(value) = value.get_associated_value(&*name) {
return Ok(value);
}
let value = 'value: {
match value.ty() {
Type::Struct(StructT { fields, .. }) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(&*name) {
let builder = ctx.builder.as_mut().unwrap();
break 'value Some(
builder
.get_element_value(value, field.name.as_any_value())
.unwrap()
.as_any_value(),
);
}
}
None
}
Type::Ptr(PtrT {
base: Type::Struct(StructT { fields, .. }),
..
}) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(&*name) {
let builder = ctx.builder.as_mut().unwrap();
let inst = builder
.get_element_ptr(value, field.name.as_any_value())
.unwrap()
.as_any_value();
unsafe {
if let AnyValue::Instruction(inst) = inst {
inst.edit_flags(|f| f | ValueFlags::LValue);
}
}
break 'value Some(inst);
}
}
None
}
_ => None,
}
};
if let Some(value) = value {
self.ctx.emit_event(Event::Symbol {
value: value,
position: CodePosition::new(self.source.clone(), field.clone()),
});
return Ok(value);
}
return Err(Diagnostic {
kind: Kind::Error,
code: Code::FieldNotFound,
message: format!("Value does not contain field `{name}`."),
position: CodePosition::new(self.source.clone(), field.clone()),
cause: None,
});
}
Expr::Binary {
lhs: lhs_expr,
rhs: rhs_expr,
operator,
} => {
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
let type_hint = if lhs.is_lvalue() {
let Type::Ptr(PtrT { base, .. }) = lhs.ty() else {
unreachable!();
};
if !matches!(operator.node, BinaryOperator::Assign) {
lhs = ctx.builder.as_mut().unwrap().load(lhs).unwrap();
}
*base
} else {
lhs.ty()
};
let mut rhs =
self.compile_expression(rhs_expr, &mut ctx.with_type_hit(Some(type_hint)))?;
if rhs.is_lvalue() {
rhs = ctx.builder.as_mut().unwrap().load(rhs).unwrap();
}
let builder = ctx.builder.as_mut().unwrap();
macro_rules! int_bin_ops {
(
const exact
$([$($ty:ident),*] $op:pat => |$a:ident, $b:ident| $expr:expr,)*
) => {
match operator.node {
$(
$op => match (lhs, rhs) {
$(
(
AnyValue::Constant(AnyConst::Int(Int::$ty($a))),
AnyValue::Constant(AnyConst::Int(Int::$ty($b))),
) => return Ok(AnyValue::Constant(AnyConst::Int(Int::$ty($expr)))),
)*
_ => {}
}
)*
_ => {}
}
};
(
const auto
$([$($ty:ident),*] $op:pat => |$a:ident, $b:ident| $expr:expr,)*
) => {
match operator.node {
$(
$op => match (lhs, rhs) {
$(
(
AnyValue::Constant(AnyConst::Int(Int::$ty($a))),
AnyValue::Constant(AnyConst::Int(Int::$ty($b))),
) => return Ok($expr.as_any_value()),
)*
_ => {}
}
)*
_ => {}
}
};
}
if lhs.is_const() && rhs.is_const() {
int_bin_ops! {
const exact
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Add => |a, b| a + b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Sub => |a, b| a - b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Mul => |a, b| a - b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Div => |a, b| a - b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Mod => |a, b| a - b,
}
int_bin_ops! {
const auto
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Eq => |a, b| a == b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Ne => |a, b| a == b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Lt => |a, b| a == b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Gt => |a, b| a == b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Le => |a, b| a == b,
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Ge => |a, b| a == b,
}
}
let (lhs_ty, rhs_ty) = (lhs.ty(), rhs.ty());
if match (lhs_ty, rhs_ty) {
(Type::Int(a_ty), Type::Int(b_ty)) => a_ty == b_ty,
_ => false,
} {
return Ok(match operator.node {
BinaryOperator::Add => builder.add(lhs, rhs).unwrap(),
BinaryOperator::Sub => builder.sub(lhs, rhs).unwrap(),
BinaryOperator::Mul => builder.mul(lhs, rhs).unwrap(),
BinaryOperator::Div => builder.div(lhs, rhs).unwrap(),
BinaryOperator::Mod => builder.modulo(lhs, rhs).unwrap(),
BinaryOperator::Eq => builder.cmp(lhs, rhs, Cmp::Eq).unwrap(),
BinaryOperator::Ne => builder.cmp(lhs, rhs, Cmp::Ne).unwrap(),
BinaryOperator::Lt => builder.cmp(lhs, rhs, Cmp::Lt).unwrap(),
BinaryOperator::Gt => builder.cmp(lhs, rhs, Cmp::Gt).unwrap(),
BinaryOperator::Le => builder.cmp(lhs, rhs, Cmp::Le).unwrap(),
BinaryOperator::Ge => builder.cmp(lhs, rhs, Cmp::Ge).unwrap(),
_ => todo!("{lhs:?} {:?} {rhs:?}", operator.node),
});
}
if match (lhs_ty, rhs_ty) {
(Type::Ptr(a_ty), Type::Ptr(b_ty)) => a_ty == b_ty,
_ => false,
} {
let lhs = builder.ptr_to_int(lhs, IntT::USIZE).unwrap();
let rhs = builder.ptr_to_int(rhs, IntT::USIZE).unwrap();
return Ok(match operator.node {
BinaryOperator::Eq => builder.cmp(lhs, rhs, Cmp::Eq).unwrap(),
BinaryOperator::Ne => builder.cmp(lhs, rhs, Cmp::Ne).unwrap(),
BinaryOperator::Lt => builder.cmp(lhs, rhs, Cmp::Lt).unwrap(),
BinaryOperator::Gt => builder.cmp(lhs, rhs, Cmp::Gt).unwrap(),
BinaryOperator::Le => builder.cmp(lhs, rhs, Cmp::Le).unwrap(),
BinaryOperator::Ge => builder.cmp(lhs, rhs, Cmp::Ge).unwrap(),
_ => todo!("{lhs:?} {:?} {rhs:?}", operator.node),
});
}
match (lhs_ty, rhs_ty, operator.node) {
(Type::Ptr(ptr @ PtrT { base, .. }), Type::USIZE, BinaryOperator::Add) => {
let mut value = builder.ptr_to_int(lhs, IntT::USIZE).unwrap();
let add = builder.mul(rhs, AnyConst::SizeOf(*base).into()).unwrap();
value = builder.add(value, add).unwrap();
value = builder.int_to_ptr(value, ptr).unwrap();
Ok(value)
}
(Type::Ptr(PtrT { base, .. }), ty, BinaryOperator::Assign) => match *base == ty
{
true => Ok(builder.store(lhs, rhs).unwrap()),
false => Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: format!(
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
),
position: CodePosition::new(self.source.clone(), expr.range.clone()),
cause: None,
}),
},
(src_ty, Type::Type, BinaryOperator::Cast) => {
let dst_ty = self.assert_ty(rhs, rhs_expr).map_err(|err| Diagnostic {
kind: Kind::Error,
code: Code::InvalidCast,
message: "Cannot perform cast.".to_string(),
position: CodePosition::new(self.source.clone(), expr.range.clone()),
cause: Some(Box::new(err)),
})?;
if src_ty == dst_ty {
return Ok(lhs);
}
match (src_ty, dst_ty) {
(Type::Int(src_ty), Type::Int(dst_ty)) => {
if dst_ty.precision < src_ty.precision {
return Ok(builder.trunc(lhs, dst_ty).unwrap());
}
todo!("{src_ty} as {dst_ty}");
}
(Type::Int(_), Type::Ptr(dst_ty)) => {
return Ok(builder.int_to_ptr(lhs, dst_ty).unwrap());
}
(Type::Ptr(_), dst_ty @ Type::Ptr(_)) => unsafe {
return Ok(builder.reinterpret(lhs, dst_ty, lhs.flags()).unwrap());
},
_ => todo!("{src_ty} as {dst_ty}"),
}
}
(a, b, _) => todo!(
"{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}",
op = operator.node,
),
}
}
Expr::If(expr) => self.compile_if(expr, ctx),
Expr::While(expr) => {
let While { cond, block, .. } = &**expr;
let mut builder = ctx.builder.as_mut().unwrap();
let cond_block = builder.create_block();
let exec_block = builder.create_block();
let exit_block = builder.create_block();
builder.jump(cond_block).unwrap();
builder.set_current_block(cond_block);
let condition = self.compile_expression(cond, ctx)?;
builder = ctx.builder.as_mut().unwrap();
builder.branch(condition, exec_block, exit_block).unwrap();
builder.set_current_block(exec_block);
let ret = self.compile_block(block, ctx)?;
builder = ctx.builder.as_mut().unwrap();
builder.jump(cond_block).unwrap();
builder.set_current_block(exit_block);
Ok(ret)
}
Expr::Call {
func,
args: args_exprs,
} => {
let func = match self.compile_expression(func, ctx)? {
AnyValue::Constant(AnyConst::Function(func)) => func,
_ => {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAFunction,
message: "Value is not a function".into(),
position: CodePosition::new(self.source.clone(), func.range.clone()),
cause: None,
});
}
};
let mut arg_ty = func.ty.par_t.iter().cloned();
let mut args = Vec::with_capacity(args_exprs.len());
for expr in args_exprs {
let mut arg =
self.compile_expression(expr, &mut ctx.with_type_hit(arg_ty.next()))?;
if arg.is_lvalue() {
arg = ctx.builder.as_mut().unwrap().load(arg).unwrap();
}
args.push(arg);
}
let builder = ctx.builder.as_mut().unwrap();
Ok(builder.call(func, args).unwrap())
}
Expr::Ptr { mutable, base, .. } => match self.compile_expression(base, ctx)? {
AnyValue::Constant(AnyConst::Type(ty)) => {
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(mutable.is_some()))).into())
}
AnyValue::Instruction(inst) if inst.is_lvalue() => {
let Type::Ptr(PtrT {
base,
mutable: is_mut,
..
}) = inst.ty()
else {
unreachable!()
};
if mutable.is_some() && !*is_mut {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAFunction,
message: "Cannot obtain a mutable pointer to an immutable value."
.into(),
position: CodePosition::new(self.source.clone(), expr.range.clone()),
cause: None,
});
}
let mut flags = inst.flags();
let builder = ctx.builder.as_mut().unwrap();
flags.remove(ValueFlags::Mutable | ValueFlags::Volatile | ValueFlags::LValue);
let ptr = Type::Ptr(base.make_ptr(mutable.is_some()));
unsafe {
Ok(builder
.reinterpret(AnyValue::Instruction(inst), ptr, flags)
.unwrap())
}
}
v => todo!("{v:?}"),
},
Expr::Struct { fields, .. } => {
let name = match &ctx.decl_names {
Some(NamePattern::Single(func_name)) => &*self.get_text(func_name),
_ => "",
};
let struct_ty = self.assembly.create_struct(name);
let mut scope = self.clone();
let mut expr_ctx = ExpressionContext {
builder: None,
decl_names: None,
type_hint: Some(Type::Type),
fn_queue: ctx.fn_queue,
};
let ctx = self.assembly.ctx();
scope.insert(literal_substr!("Self"), struct_ty.as_any_value());
let mut field_map = FieldMap::default();
for AstNode {
range,
node:
ast::Field {
name,
ty: ty_expr,
public,
mutable,
},
} in fields
{
let ty = scope.compile_expression(ty_expr, &mut expr_ctx)?;
let name = ctx.intern_str(&self.get_text(name));
field_map.insert(
name,
Field {
name,
ty: self.assert_ty(ty, ty_expr)?,
public: public.is_some(),
mutable: mutable.is_some(),
},
);
}
struct_ty.fields.set(field_map).unwrap();
Ok(struct_ty.as_any_value())
}
Expr::List(expr) => {
let mut expr = expr.iter();
let mut values = Vec::with_capacity(expr.len());
match expr.next() {
None => return Ok(AnyValue::Constant(AnyConst::Array(&[]))),
Some(expr) => {
let value = self.compile_expression(expr, ctx)?;
// TODO Check if it matches the ctx type hint
values.push(value);
}
};
let element_ty = values[0].ty();
for expr in expr {
let value = self.compile_expression(expr, ctx)?;
self.assert_ty_eq(&value, expr, &element_ty)?;
values.push(value);
}
if values.iter().all(|v| matches!(v, AnyValue::Constant(_))) {
let alloc = self.assembly.ctx().alloc();
return Ok(AnyValue::Constant(AnyConst::Array(alloc.alloc_slice(
values.into_iter().map(|v| match v {
AnyValue::Constant(c) => c,
_ => unreachable!(),
}),
))));
}
todo!()
}
Expr::Deref { value: expr, .. } => {
let value = self.compile_expression(expr, ctx)?;
let builder = ctx.builder.as_mut().unwrap();
let ty = value.ty();
match value.is_lvalue() {
false => unsafe {
if !matches!(ty, Type::Ptr(_)) {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotDereference,
message: format!("Cannot dereference a value of type `{ty}`."),
position: CodePosition::new(
self.source.clone(),
expr.range.clone(),
),
cause: None,
});
}
Ok(builder
.reinterpret(value, value.ty(), value.flags() | ValueFlags::LValue)
.unwrap())
},
true => unsafe {
if !matches!(
ty,
Type::Ptr(PtrT {
base: Type::Ptr(_),
..
})
) {
let Type::Ptr(PtrT { base, .. }) = ty else {
unreachable!()
};
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotDereference,
message: format!("Cannot dereference a value of type `{base}`."),
position: CodePosition::new(
self.source.clone(),
expr.range.clone(),
),
cause: None,
});
}
let AnyValue::Instruction(value) = builder.load(value).unwrap() else {
unreachable!()
};
value.edit_flags(|v| v | ValueFlags::LValue);
Ok(AnyValue::Instruction(value))
},
_ => unimplemented!("{}", value.is_lvalue()),
}
}
Expr::Index {
value,
index: index_expr,
} if index_expr.len() == 1 => {
let mut value = self.compile_expression(value, ctx)?;
let mut index = self.compile_expression(&index_expr[0], ctx)?;
let builder = ctx.builder.as_mut().unwrap();
if index.is_lvalue() {
index = builder.load(index).unwrap();
}
// TODO Add support for custom indexing operations
if index.ty() != Type::USIZE {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: "Value is not of type `usize`".into(),
position: CodePosition::new(
self.source.clone(),
index_expr[0].range.clone(),
),
cause: None,
});
}
// TODO This is probably wrong, make it better.
while value.is_lvalue()
&& !matches!(
value.ty(),
Type::Ptr(PtrT {
base: Type::Array(_),
..
}) | Type::Ref(RefT {
base: Type::Array(_),
..
})
) {
value = builder.load(value).unwrap();
}
if value.is_lvalue() {
let gep = builder.get_element_ptr(value, index).unwrap();
return Ok(gep);
}
todo!("{:#?}", value.ty());
}
Expr::StructCtor {
ty: ctor_ty,
values: ctor_values,
} => {
let ty = match ctor_ty {
Some(ty) => self.compile_expression(ty, ctx)?,
None => match ctx.type_hint {
Some(ty) => AnyValue::Constant(AnyConst::Type(ty)),
None => {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotInferType,
message: "Type cannot be inferred.".into(),
position: CodePosition::new(
self.source.clone(),
expr.range.clone(),
),
cause: None,
});
}
},
};
let AnyValue::Constant(AnyConst::Type(Type::Struct(
struct_ty @ StructT { fields, .. },
))) = ty
else {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAStruct,
message: format!("Expected struct type, got value of type `{}`.", ty.ty()),
position: CodePosition::new(
self.source.clone(),
match ctor_ty {
None => expr.range.clone(),
Some(ty) => ty.range.clone(),
},
),
cause: None,
});
};
let mut non_const = false;
let fields = fields.get().unwrap();
let mut values = Vec::with_capacity(fields.len());
for Field {
name: fld_name, ty, ..
} in fields.values()
{
let Some(name_value_pair) = ctor_values.iter().find_map(|v| {
let name = self.get_text(&v.name);
match name == *fld_name {
true => Some(&v.node),
false => None,
}
}) else {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::UninitializedField,
message: format!("Uninitialized field `{fld_name}`."),
position: CodePosition::new(self.source.clone(), expr.range.clone()),
cause: None,
});
};
let mut ctx = ctx.with_type_hit(Some(*ty));
let value = self.compile_expression(&name_value_pair.value, &mut ctx)?;
self.assert_ty_eq(&value, &name_value_pair.value, ty)?;
non_const |= !value.flags().contains(ValueFlags::Const);
values.push(value);
}
Ok(match non_const {
true => {
let builder = ctx.builder.as_mut().unwrap();
let values = self.assembly.ctx().alloc().alloc_slice(values.into_iter());
builder.make_struct(struct_ty, values).unwrap()
}
false => AnyValue::Constant(AnyConst::Struct(
struct_ty,
self.assembly
.ctx()
.alloc()
.alloc_slice(values.into_iter().map(|v| {
let AnyValue::Constant(c) = v else {
unreachable!()
};
c
})),
)),
})
}
_ => todo!("{expr:#?}"),
}
}
fn make_function(
&mut self,
ast: &Arc<AstNode<ast::Function>>,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<&'l Function<'l>, Diagnostic> {
let ret_ty = match ast.ret.as_ref() {
None => AnyValue::Constant(AnyConst::Type(Type::Void)),
Some(ty) => self.compile_expression(ty, ctx)?,
};
let ast_as_expr = AstNode {
range: ast.range.clone(),
node: Expr::Func(ast.clone()),
};
let ret_ty = self.assert_ty(ret_ty, ast.ret.as_ref().unwrap_or(&ast_as_expr))?;
let mut par_ty = Vec::with_capacity(ast.args.len());
for arg in &ast.args {
let ty = self.compile_expression(&arg.value, ctx)?;
let ty = self.assert_ty(ty, &arg.value)?;
par_ty.push(ty);
}
let fn_ty = ret_ty.make_fn(par_ty);
let name = match &ctx.decl_names {
Some(NamePattern::Single(func_name)) => &*self.get_text(func_name),
_ => "",
};
let func = self.assembly.create_function(fn_ty, name);
let Some(block) = &ast.block else {
return Ok(func);
};
let mut scope = self.clone();
for (i, arg) in ast.args.iter().enumerate() {
let name = self.get_text_arc(&arg.name);
scope.insert(name, AnyValue::Parameter(i, func));
}
ctx.fn_queue.push_back((func, block.clone(), scope));
Ok(func)
}
fn compile_decl(
&mut self,
names: &NamePattern,
value: &AstNode<Expr>,
mutable: bool,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, Diagnostic> {
let mut sub_ctx = ExpressionContext {
decl_names: Some(names),
builder: ctx.builder.as_deref_mut(),
type_hint: None, //TODO
fn_queue: ctx.fn_queue,
};
let mut value = self.compile_expression(value, &mut sub_ctx)?;
let builder = sub_ctx.builder.unwrap();
if value.is_lvalue() {
value = builder.load(value).unwrap();
}
if mutable {
let variable = builder.stack_alloc(value.ty()).unwrap();
builder.store(variable, value).unwrap();
value = variable;
}
match names {
NamePattern::Single(ident) => {
let name = self.get_text_arc(ident);
self.values.insert(
name,
Variable {
value: Arc::new(OnceLock::from(value)),
},
);
self.ctx.emit_event(Event::Definition {
value: value,
position: CodePosition::new(self.source.clone(), ident.clone()),
});
}
NamePattern::Tuple(_) => todo!(),
NamePattern::List(_) => todo!(),
}
Ok(AnyConst::Void.into())
}
fn compile_block(
&mut self,
block: &Block,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, Diagnostic> {
let mut scope = self.clone();
let builder = ctx.builder.as_mut().unwrap();
let mut last_expr = None;
for (i, expr) in block.0.iter().enumerate() {
let mut ctx = ExpressionContext {
builder: Some(builder),
decl_names: None,
type_hint: match i == block.0.len() - 1 {
true => ctx.type_hint,
false => None,
},
fn_queue: ctx.fn_queue,
};
last_expr = Some(scope.compile_expression(expr, &mut ctx)?);
}
Ok(last_expr.unwrap_or(AnyValue::Constant(AnyConst::Void)))
}
fn compile_if(
&mut self,
expr: &If,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, Diagnostic> {
let If {
cond, block, else_, ..
} = expr;
let condition = self.compile_expression(cond, ctx)?;
self.assert_ty_eq(&condition, cond, &Type::Bool)?;
let builder = ctx.builder.as_mut().unwrap();
let then_block = builder.create_block();
let else_block = builder.create_block();
builder.branch(condition, then_block, else_block).unwrap();
builder.set_current_block(then_block);
let then_val = self.compile_block(block, ctx)?;
let builder = ctx.builder.as_mut().unwrap();
let else_ = match else_ {
None => {
builder.jump(else_block).unwrap();
builder.set_current_block(else_block);
return Ok(then_val);
}
Some(else_) => else_,
};
let continue_block = builder.create_block();
builder.jump(continue_block).unwrap();
builder.set_current_block(else_block);
let else_val = match &**else_ {
Else::Block { expr, .. } => self.compile_block(expr, ctx)?,
Else::If { expr, .. } => self.compile_if(expr, ctx)?,
};
let builder = ctx.builder.as_mut().unwrap();
builder.jump(continue_block).unwrap();
builder.set_current_block(continue_block);
if then_val.ty() != else_val.ty() {
todo!()
}
Ok(builder
.phi(
[
PhiValue {
value: then_val,
block: then_block.id,
},
PhiValue {
value: else_val,
block: else_block.id,
},
]
.into_iter(),
)
.unwrap()
.into())
}
fn assert_ty(
&self,
val: AnyValue<'l>,
value_expr: &AstNode<Expr>,
) -> Result<Type<'l>, Diagnostic> {
match val {
AnyValue::Constant(AnyConst::Type(ty)) => Ok(ty),
_ => Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAType,
message: "Value is not a type.".to_string(),
position: CodePosition::new(self.source.clone(), value_expr.range.clone()),
cause: None,
}),
}
}
pub fn assert_ty_eq(
&self,
value: &AnyValue<'l>,
value_expr: &AstNode<Expr>,
expected: &Type<'l>,
) -> Result<Type<'l>, Diagnostic> {
let value_ty = value.ty();
match value_ty == *expected {
true => Ok(value_ty),
false => {
return Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: format!("Expected value of type `{expected}`, found `{value_ty}`."),
position: CodePosition::new(self.source.clone(), value_expr.range.clone()),
cause: None,
});
}
}
}
fn get_text(&self, range: &Range<usize>) -> Cow<'_, str> {
match &self.source.text {
leaf_parser::Text::ArcStr(arc_str, _) => {
Cow::Borrowed(&arc_str.as_str()[range.clone()])
}
}
}
fn get_text_arc(&self, range: &Range<usize>) -> Substr {
match &self.source.text {
leaf_parser::Text::ArcStr(arc_str, _) => arc_str.substr(range.clone()),
}
}
}