feat(grfn/achilles): Implement tuples, and tuple patterns
Implement tuple expressions, types, and patterns, all the way through
the parser down to the typechecker. In LLVM, these are implemented as
anonymous structs, using an `extract` instruction when they're pattern
matched on to get out the individual fields.
Currently the only limitation here is patterns aren't supported in
function argument position, but you can still do something like
fn xy = let (x, y) = xy in x + y
Change-Id: I357f17e9d4052e741eda8605b6662822f331efde
Reviewed-on: https://cl.tvl.fyi/c/depot/+/3027
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
This commit is contained in:
parent
e1c45be3f5
commit
48098f83c1
12 changed files with 413 additions and 54 deletions
|
|
@ -7,12 +7,13 @@ use inkwell::builder::Builder;
|
|||
pub use inkwell::context::Context;
|
||||
use inkwell::module::Module;
|
||||
use inkwell::support::LLVMString;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType};
|
||||
use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue};
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType, StructType};
|
||||
use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::ast::hir::{Binding, Decl, Expr};
|
||||
use crate::ast::hir::{Binding, Decl, Expr, Pattern};
|
||||
use crate::ast::{BinaryOperator, Ident, Literal, Type, UnaryOperator};
|
||||
use crate::common::env::Env;
|
||||
|
||||
|
|
@ -82,6 +83,25 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
|
|||
.append_basic_block(*self.function_stack.last().unwrap(), name)
|
||||
}
|
||||
|
||||
fn bind_pattern(&mut self, pat: &'ast Pattern<'ast, Type>, val: AnyValueEnum<'ctx>) {
|
||||
match pat {
|
||||
Pattern::Id(id, _) => self.env.set(id, val),
|
||||
Pattern::Tuple(pats) => {
|
||||
for (i, pat) in pats.iter().enumerate() {
|
||||
let member = self
|
||||
.builder
|
||||
.build_extract_value(
|
||||
StructValue::try_from(val).unwrap(),
|
||||
i as _,
|
||||
"pat_bind",
|
||||
)
|
||||
.unwrap();
|
||||
self.bind_pattern(pat, member.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn codegen_expr(
|
||||
&mut self,
|
||||
expr: &'ast Expr<'ast, Type>,
|
||||
|
|
@ -164,9 +184,9 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
|
|||
}
|
||||
Expr::Let { bindings, body, .. } => {
|
||||
self.env.push();
|
||||
for Binding { ident, body, .. } in bindings {
|
||||
for Binding { pat, body, .. } in bindings {
|
||||
if let Some(val) = self.codegen_expr(body)? {
|
||||
self.env.set(ident, val);
|
||||
self.bind_pattern(pat, val);
|
||||
}
|
||||
}
|
||||
let res = self.codegen_expr(body);
|
||||
|
|
@ -244,6 +264,19 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
|
|||
self.env.restore(env);
|
||||
Ok(Some(function.into()))
|
||||
}
|
||||
Expr::Tuple(members, ty) => {
|
||||
let values = members
|
||||
.into_iter()
|
||||
.map(|expr| self.codegen_expr(expr))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.into_iter()
|
||||
.filter_map(|x| x)
|
||||
.map(|x| x.try_into().unwrap())
|
||||
.collect_vec();
|
||||
let field_types = ty.as_tuple().unwrap();
|
||||
let tuple_type = self.codegen_tuple_type(field_types);
|
||||
Ok(Some(tuple_type.const_named_struct(&values).into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,9 +374,20 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
|
|||
Type::Function(_) => todo!(),
|
||||
Type::Var(_) => unreachable!(),
|
||||
Type::Unit => None,
|
||||
Type::Tuple(ts) => Some(self.codegen_tuple_type(ts).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn codegen_tuple_type(&self, ts: &'ast [Type]) -> StructType<'ctx> {
|
||||
self.context.struct_type(
|
||||
ts.iter()
|
||||
.filter_map(|t| self.codegen_type(t))
|
||||
.collect_vec()
|
||||
.as_slice(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn codegen_int_type(&self, type_: &'ast Type) -> IntType<'ctx> {
|
||||
// TODO
|
||||
self.context.i64_type()
|
||||
|
|
@ -433,4 +477,10 @@ mod tests {
|
|||
let res = jit_eval::<i64>("let id = fn x = x in id 1").unwrap();
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_tuple_pattern() {
|
||||
let res = jit_eval::<i64>("let (x, y) = (1, 2) in x + y").unwrap();
|
||||
assert_eq!(res, 3);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue