# HG changeset patch # User Simon Heath # Date 1689676623 14400 # Tue Jul 18 06:37:03 2023 -0400 # Branch devel # Node ID 24722db3fc92738e8277c74248036d36ab75adc5 # Parent a9c9a157dcc75a2523e11dcdb33058585bb28c52 play around with benchmarking and Rc in types vs. Box. The results are... not worth it, to say the least. Rc'ing vs Box'ing in types does indeed save a bit of time, about 10%, in various passes since we probably avoid a lot of copying of types. However, what takes the most time apart from executing rustc of course is actually the parser! Given my propensity for saying "parsing isn't slow, don't worry about it" to new PL devs, all I can say is... lol. lmao. But it's still 60k lines a second or whatever, so it's fine even if I expected it to be like 3x that. But attempting to profile garnetfmt (while commenting out anything it does besides parsing) results in `perf` saying that 93% of the time is spent in `__memmove_avx_unaligned_erms`. And if that isn't a sign from on high to shrug and get on with life then idk what is. XD If we wanna optimize types then really they should be interned and/or flattened into a vec instead of a tree, I suspect. Then the AST and HIR should be flattened into a vec as well, though honestly they tend to be allocated all together so I bet data locality is pretty ok anyway. But none of that matters compared to the parser! Well this was a weird Learning Experience, I guess. diff --git a/benches/basic.rs b/benches/basic.rs --- a/benches/basic.rs +++ b/benches/basic.rs @@ -5,7 +5,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; - +use garnet::*; /// Each iteration creates about 32 sLOC fn gen_dumb_test_code(count: usize) -> String { @@ -66,33 +66,22 @@ buf } -fn criterion_benchmark(c: &mut Criterion) { +fn bench_with_rust_backend(c: &mut Criterion) { let code = gen_dumb_test_code(103); let lines = code.lines().count(); - let name = format!("compile {}ish lines", lines); + let name = format!("compile {}ish lines to Rust", lines); c.bench_function(&name, |b| { - b.iter(|| { - garnet::compile( - "criterion.gt", - black_box(&code), - garnet::backend::Backend::Rust, - ) - }) + b.iter(|| compile("criterion.gt", black_box(&code), backend::Backend::Rust)) }); let code = gen_dumb_test_code(103 * 8); let lines = code.lines().count(); - let name = format!("compile {}ish lines", lines); + let name = format!("compile {}ish lines to Rust", lines); c.bench_function(&name, |b| { - b.iter(|| { - garnet::compile( - "criterion.gt", - black_box(&code), - garnet::backend::Backend::Rust, - ) - }) + b.iter(|| compile("criterion.gt", black_box(&code), backend::Backend::Rust)) }); + /* let code = gen_dumb_test_code(103 * 16); let lines = code.lines().count(); let name = format!("compile {}ish lines", lines); @@ -105,7 +94,82 @@ ) }) }); + */ +} + +fn bench_with_null_backend(c: &mut Criterion) { + let code = gen_dumb_test_code(103); + let lines = code.lines().count(); + let name = format!("compile {}ish lines to nothing", lines); + c.bench_function(&name, |b| { + b.iter(|| compile("criterion.gt", black_box(&code), backend::Backend::Null)) + }); + + let code = gen_dumb_test_code(103 * 8); + let lines = code.lines().count(); + let name = format!("compile {}ish lines to nothing", lines); + c.bench_function(&name, |b| { + b.iter(|| compile("criterion.gt", black_box(&code), backend::Backend::Null)) + }); + + let code = gen_dumb_test_code(103 * 16); + let lines = code.lines().count(); + let name = format!("compile {}ish lines to nothing", lines); + c.bench_function(&name, |b| { + b.iter(|| compile("criterion.gt", black_box(&code), backend::Backend::Rust)) + }); } -criterion_group!(benches, criterion_benchmark); +fn bench_stages(c: &mut Criterion) { + let code = gen_dumb_test_code(103 * 8); + let lines = code.lines().count(); + let name = format!("Parse {} lines", lines); + c.bench_function(&name, |b| { + b.iter(|| { + let mut parser = parser::Parser::new("criterion.gt", black_box(&code)); + parser.parse() + }) + }); + + let mut parser = parser::Parser::new("criterion.gt", black_box(&code)); + let ast = parser.parse(); + + c.bench_function("lower and run passes", |b| { + b.iter(|| { + let hir = hir::lower(black_box(&ast)); + passes::run_passes(hir) + }) + }); + + let hir = hir::lower(black_box(&ast)); + let hir = passes::run_passes(hir); + + c.bench_function("typecheck and borrowcheck", |b| { + b.iter(|| { + let tck = &mut typeck::typecheck(black_box(&hir)).unwrap(); + borrowck::borrowck(&hir, tck).unwrap(); + }) + }); + + let tck = &mut typeck::typecheck(black_box(&hir)).unwrap(); + borrowck::borrowck(&hir, tck).unwrap(); + + c.bench_function("typechecked passes", |b| { + b.iter(|| { + passes::run_typechecked_passes(black_box(hir.clone()), black_box(tck)); + }) + }); + + let hir = passes::run_typechecked_passes(hir, tck); + c.bench_function("codegen", |b| { + b.iter(|| backend::output(backend::Backend::Rust, black_box(&hir), black_box(tck))) + }); +} + +criterion_group!( + benches, + //bench_with_rust_backend, + //bench_with_null_backend, + bench_stages +); criterion_main!(benches); diff --git a/src/ast.rs b/src/ast.rs --- a/src/ast.rs +++ b/src/ast.rs @@ -96,11 +96,11 @@ #[derive(Debug, Clone, PartialEq)] pub struct Signature { /// Parameters - pub params: Vec<(Sym, Type)>, + pub params: Arc>, /// Return type pub rettype: Type, /// Type parameters - pub typeparams: Vec, + pub typeparams: Arc>, } impl Signature { @@ -108,8 +108,8 @@ pub fn to_type(&self) -> Type { let paramtypes = self.params.iter().map(|(_nm, ty)| ty.clone()).collect(); Type::Func( - paramtypes, - Box::new(self.rettype.clone()), + Arc::new(paramtypes), + Arc::new(self.rettype.clone()), self.typeparams.clone(), ) } @@ -150,14 +150,14 @@ let new_params = self .params .iter() - .zip(params) + .zip(params.as_ref()) .map(|((nm, _t1), t2)| (*nm, t2.clone())) .collect(); let new_rettype = rettype.clone(); let new_type_params = typeparams.clone(); Self { - params: new_params, - rettype: *new_rettype, + params: Arc::new(new_params), + rettype: new_rettype.as_ref().clone(), typeparams: new_type_params, } } diff --git a/src/backend/rust.rs b/src/backend/rust.rs --- a/src/backend/rust.rs +++ b/src/backend/rust.rs @@ -44,7 +44,7 @@ Named(s, types) if s == &Sym::new("Tuple") => { trace!("Compiling tuple {:?}...", t); let mut accm = String::from("("); - for typ in types { + for typ in &**types { accm += &compile_typename(typ); accm += ", "; } @@ -68,7 +68,7 @@ } */ accm += "("; - for p in params { + for p in &**params { accm += &compile_typename(p); accm += ", "; } @@ -149,7 +149,7 @@ /// Driver that turns a pile of Ir into Rust code. pub(super) fn output(lir: &hir::Ir, tck: &Tck) -> Vec { let mut output = Vec::new(); - for builtin in &*builtins::BUILTINS { + for builtin in &*builtins::all() { output.extend(builtin.code[&backend::Backend::Rust].as_bytes()); } for decl in lir.decls.iter() { @@ -223,7 +223,7 @@ params.iter().map(|sym| (*sym.val()).clone()).collect(); let args = param_strings.join(", "); writeln!(w, "pub enum {}<{}> {{ ", nstr, args)?; - for (nm, ty) in body { + for (nm, ty) in &**body { writeln!(w, " {} ({}),", nm, compile_typename(ty))?; } writeln!(w, "}}")?; diff --git a/src/bin/garnetfmt.rs b/src/bin/garnetfmt.rs --- a/src/bin/garnetfmt.rs +++ b/src/bin/garnetfmt.rs @@ -39,7 +39,6 @@ // anything let formatted_data = &formatted_src.into_inner(); let formatted_str = String::from_utf8_lossy(formatted_data); - println!("{}", formatted_str); let formatted_ast = { let mut parser = parser::Parser::new(filename, &formatted_str); parser.parse() diff --git a/src/builtins.rs b/src/builtins.rs --- a/src/builtins.rs +++ b/src/builtins.rs @@ -6,8 +6,6 @@ use std::collections::BTreeMap; -use once_cell::sync::Lazy; - use crate::backend::Backend; use crate::*; @@ -22,8 +20,6 @@ pub code: BTreeMap, } -pub static BUILTINS: Lazy> = Lazy::new(Builtin::all); - impl Builtin { /// Generate all appropriate methods for the given numeric type. /// Right now we just stick em in the toplevel with constructed names, @@ -108,99 +104,99 @@ vec![ Builtin { name: Sym::new(format!("__println_{name}")), - sig: Type::Func(vec![ty.clone()], Box::new(Type::unit()), vec![]), + sig: Type::function(&vec![ty.clone()], &Type::unit(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, println)]), }, Builtin { name: Sym::new(format!("__band_{name}")), - sig: Type::Func(vec![ty.clone(), ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, band)]), }, Builtin { name: Sym::new(format!("__bor_{name}")), - sig: Type::Func(vec![ty.clone(), ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, bor)]), }, Builtin { name: Sym::new(format!("__bxor_{name}")), - sig: Type::Func(vec![ty.clone(), ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, bxor)]), }, Builtin { name: Sym::new(format!("__bnot_{name}")), - sig: Type::Func(vec![ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, bnot)]), }, Builtin { name: Sym::new(format!("__rshift_{name}")), - sig: Type::Func(vec![ty.clone(), ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, rshift)]), }, Builtin { name: Sym::new(format!("__lshift_{name}")), - sig: Type::Func(vec![ty.clone(), ty.clone()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), ty.clone()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, lshift)]), }, Builtin { name: Sym::new(format!("__rol_{name}")), - sig: Type::Func(vec![ty.clone(), Type::i32()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), Type::i32()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, rol)]), }, Builtin { name: Sym::new(format!("__ror_{name}")), - sig: Type::Func(vec![ty.clone(), Type::i32()], Box::new(ty.clone()), vec![]), + sig: Type::function(&vec![ty.clone(), Type::i32()], &ty.clone(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, ror)]), }, Builtin { name: Sym::new(format!("__{name}_to_i32")), - sig: Type::Func(vec![ty.clone()], Box::new(Type::i32()), vec![]), + sig: Type::function(&vec![ty.clone()], &Type::i32(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, cast_i32)]), }, Builtin { name: Sym::new(format!("__{name}_to_u32")), - sig: Type::Func(vec![ty.clone()], Box::new(Type::u32()), vec![]), + sig: Type::function(&vec![ty.clone()], &Type::u32(), &vec![]), code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, cast_u32)]), }, ] } - /// A function that returns all the compiler builtin info. Just - /// use the `BUILTINS` global instead, this is basically here - /// to initialize it. - fn all() -> Vec { - let rust_println = r#"fn __println(x: i32) { +} +/// A function that returns all the compiler builtin info. Just +/// use the `BUILTINS` global instead, this is basically here +/// to initialize it. +pub fn all() -> Vec { + let rust_println = r#"fn __println(x: i32) { println!("{}", x); }"#; - let rust_println_bool = r#" + let rust_println_bool = r#" fn __println_bool(x: bool) { println!("{}", x); }"#; - let mut funcs = vec![ - Builtin { - name: Sym::new("__println"), - sig: Type::Func(vec![Type::i32()], Box::new(Type::unit()), vec![]), - code: BTreeMap::from([ - (Backend::Null, "".into()), - (Backend::Rust, rust_println.into()), - ]), - }, - Builtin { - name: Sym::new("__println_bool"), - sig: Type::Func(vec![Type::bool()], Box::new(Type::unit()), vec![]), - code: BTreeMap::from([ - (Backend::Null, "".into()), - (Backend::Rust, rust_println_bool.into()), - ]), - }, - ]; - funcs.extend(Self::generate_numerics_for("i8", Type::i8())); - funcs.extend(Self::generate_numerics_for("i16", Type::i16())); - funcs.extend(Self::generate_numerics_for("i32", Type::i32())); - funcs.extend(Self::generate_numerics_for("i64", Type::i64())); - funcs.extend(Self::generate_numerics_for("u8", Type::u8())); - funcs.extend(Self::generate_numerics_for("u16", Type::u16())); - funcs.extend(Self::generate_numerics_for("u32", Type::u32())); - funcs.extend(Self::generate_numerics_for("u64", Type::u64())); - funcs - } + let mut funcs = vec![ + Builtin { + name: Sym::new("__println"), + sig: Type::function(&vec![Type::i32()], &Type::unit(), &vec![]), + code: BTreeMap::from([ + (Backend::Null, "".into()), + (Backend::Rust, rust_println.into()), + ]), + }, + Builtin { + name: Sym::new("__println_bool"), + sig: Type::function(&vec![Type::bool()], &Type::unit(), &vec![]), + code: BTreeMap::from([ + (Backend::Null, "".into()), + (Backend::Rust, rust_println_bool.into()), + ]), + }, + ]; + funcs.extend(Builtin::generate_numerics_for("i8", Type::i8())); + funcs.extend(Builtin::generate_numerics_for("i16", Type::i16())); + funcs.extend(Builtin::generate_numerics_for("i32", Type::i32())); + funcs.extend(Builtin::generate_numerics_for("i64", Type::i64())); + funcs.extend(Builtin::generate_numerics_for("u8", Type::u8())); + funcs.extend(Builtin::generate_numerics_for("u16", Type::u16())); + funcs.extend(Builtin::generate_numerics_for("u32", Type::u32())); + funcs.extend(Builtin::generate_numerics_for("u64", Type::u64())); + funcs } diff --git a/src/hir.rs b/src/hir.rs --- a/src/hir.rs +++ b/src/hir.rs @@ -768,10 +768,10 @@ let struct_signature = ts .iter() //.map(|(enumname, _enumval)| (*enumname, ty.clone())) - .map(|(enumname, _enumval)| (*enumname, Type::Named(name, vec![]))) + .map(|(enumname, _enumval)| (*enumname, Type::Named(name, Arc::new(vec![])))) .collect(); // Enums cannot have type parameters, so this works. - let init_type = Type::Struct(struct_signature, vec![]); + let init_type = Type::Struct(Arc::new(struct_signature), Arc::new(vec![])); let new_constdef = Const { name, typ: init_type, @@ -798,7 +798,7 @@ .map(|(variant_name, variant_type)| { let paramname = Sym::new("x"); let signature = ast::Signature { - params: vec![(paramname, variant_type.clone())], + params: Arc::new(vec![(paramname, variant_type.clone())]), rettype: Type::Named(name, generics.clone()), typeparams: generics.clone(), }; @@ -822,15 +822,15 @@ // `fn(variant_type) name` ( *variant_name, - Type::Func( - vec![variant_type.clone()], - Box::new(Type::Named(name, generics.clone())), - generics.clone(), + Type::function( + &[variant_type.clone()], + &Type::Named(name, generics.clone()), + &generics, ), ) }) .collect(); - let struct_type = Type::Struct(struct_typebody, generics.clone()); + let struct_type = Type::Struct(Arc::new(struct_typebody), generics.clone()); let new_constdef = Const { name: name.to_owned(), typ: struct_type, @@ -843,9 +843,10 @@ other => { let s = Sym::new("x"); trace!("Lowering params {:?}", params); - let type_params: Vec<_> = params.iter().map(|s| Type::Generic(*s)).collect(); + let type_params: Arc> = + Arc::new(params.iter().map(|s| Type::Generic(*s)).collect()); let signature = ast::Signature { - params: vec![(s, other.clone())], + params: Arc::new(vec![(s, other.clone())]), rettype: Type::Named(name.to_owned(), type_params.clone()), typeparams: type_params.clone(), }; @@ -853,7 +854,7 @@ // in a type constructor let body = vec![ExprNode::new(Expr::TypeCtor { name, - type_params, + type_params: type_params.to_vec(), body: ExprNode::new(Expr::Var { name: s }), })]; //println!("{} is {:#?}", variant_name, e); diff --git a/src/lib.rs b/src/lib.rs --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ mod ast; pub mod backend; -mod borrowck; +pub mod borrowck; mod builtins; pub mod format; pub mod hir; @@ -74,26 +74,26 @@ /// Never type, the type of an infinite loop Never, /// A C-like enum, and the integer values it corresponds to - Enum(Vec<(Sym, i32)>), + Enum(Arc>), /// A nominal type of some kind; may be built-in (like Tuple) /// or user-defined. - Named(Sym, Vec), + Named(Sym, Arc>), /// A function pointer. /// /// The contents are arg types, return types, type parameters - Func(Vec, Box, Vec), + Func(Arc>, Arc, Arc>), /// An anonymous struct. The vec is type parameters. - Struct(BTreeMap, Vec), + Struct(Arc>, Arc>), /// Sum type. /// /// Like structs, contains a list of type parameters. - Sum(BTreeMap, Vec), + Sum(Arc>, Arc>), /// Arrays are just a type and a number. - Array(Box, usize), + Array(Arc, usize), /// A generic type parameter that has been given an explicit name. Generic(Sym), /// Unique borrow - Uniq(Box), + Uniq(Arc), } impl Type { @@ -112,32 +112,32 @@ Type::Never => (), Type::Enum(_ts) => (), Type::Named(_, generics) => { - for g in generics { + for g in &**generics { helper(g, accm); } } Type::Func(args, rettype, typeparams) => { - for t in args { + for t in &**args { helper(t, accm); } - for t in typeparams { + for t in &**typeparams { helper(t, accm); } helper(rettype, accm) } Type::Struct(body, generics) => { - for (_, ty) in body { + for (_, ty) in &**body { helper(ty, accm); } - for g in generics { + for g in &**generics { helper(g, accm); } } Type::Sum(body, generics) => { - for (_, ty) in body { + for (_, ty) in &**body { helper(ty, accm); } - for g in generics { + for g in &**generics { helper(g, accm); } } @@ -238,22 +238,30 @@ /// Shortcut for getting the type for Unit pub fn unit() -> Self { - Self::Named(Sym::new("Tuple"), vec![]) + Self::Named(Sym::new("Tuple"), Arc::new(vec![])) } /// Shortcut for getting the type for Never pub fn never() -> Self { - Self::Named(Sym::new("Never"), vec![]) + Self::Named(Sym::new("Never"), Arc::new(vec![])) } /// Create a Tuple with the given values - pub fn tuple(args: Vec) -> Self { + pub fn tuple(args: Arc>) -> Self { Self::Named(Sym::new("Tuple"), args) } /// Used in some tests pub fn array(t: &Type, len: usize) -> Self { - Self::Array(Box::new(t.clone()), len) + Self::Array(Arc::new(t.clone()), len) + } + + fn function(params: &[Type], rettype: &Type, generics: &[Type]) -> Self { + Type::Func( + Arc::new(Vec::from(params)), + Arc::new(rettype.clone()), + Arc::new(Vec::from(generics)), + ) } fn _is_integer(&self) -> bool { @@ -399,7 +407,7 @@ // Types are identical, noop. (s, o) if s == o => (), (Type::Named(n1, args1), Type::Named(n2, args2)) if n1 == n2 => { - for (p1, p2) in args1.iter().zip(args2) { + for (p1, p2) in (&**args1).iter().zip(&**args2) { p1._find_substs(p2, substitutions); } } @@ -416,7 +424,7 @@ if typeparams1.len() > 0 { todo!() } - for (p1, p2) in params1.iter().zip(params2) { + for (p1, p2) in (&**params1).iter().zip(&**params2) { p1._find_substs(p2, substitutions); } rettype1._find_substs(rettype2, substitutions); @@ -438,11 +446,11 @@ if !body1.keys().eq(body2.keys()) { panic!("subst for sum type had non-matching keys") } - for ((_nm1, t1), (_nm2, t2)) in body1.iter().zip(body2) { + for ((_nm1, t1), (_nm2, t2)) in (&**body1).iter().zip(&**body2) { t1._find_substs(t2, substitutions); } - for (p1, p2) in generics1.iter().zip(generics2) { + for (p1, p2) in (&**generics1).iter().zip(&**generics2) { p1._find_substs(p2, substitutions); } } @@ -480,11 +488,15 @@ if typeparams1.len() > 0 { todo!("Hsfjkdslfjs"); } - Type::Func(new_params, Box::new(new_rettype), vec![]) + Type::Func( + Arc::new(new_params), + Arc::new(new_rettype), + Arc::new(vec![]), + ) } Type::Named(n1, args1) => { let new_args = args1.iter().map(|p1| p1._apply_substs(substs)).collect(); - Type::Named(*n1, new_args) + Type::Named(*n1, Arc::new(new_args)) } Type::Struct(_, _) => unreachable!("see other unreachable in substitute()"), Type::Sum(body, generics) => { @@ -493,9 +505,9 @@ .map(|(nm, ty)| (*nm, ty._apply_substs(substs))) .collect(); let new_generics = generics.iter().map(|p1| p1._apply_substs(substs)).collect(); - Type::Sum(new_body, new_generics) + Type::Sum(Arc::new(new_body), Arc::new(new_generics)) } - Type::Array(body, len) => Type::Array(Box::new(body._apply_substs(substs)), *len), + Type::Array(body, len) => Type::Array(Arc::new(body._apply_substs(substs)), *len), Type::Generic(nm) => substs .get(&nm) .unwrap_or_else(|| panic!("No substitution found for generic named {}!", nm)) @@ -625,7 +637,7 @@ #[test] fn check_name_format() { let args = vec![Type::i32(), Type::bool()]; - let def = Type::Func(args, Box::new(Type::i32()), vec![]); + let def = Type::function(&args, &Type::i32(), &vec![]); let gotten_name = def.get_name(); let desired_name = "fn(I32, Bool) I32"; assert_eq!(&gotten_name, desired_name); diff --git a/src/parser.rs b/src/parser.rs --- a/src/parser.rs +++ b/src/parser.rs @@ -706,9 +706,9 @@ let (params, typeparams) = self.parse_fn_args(); let rettype = self.try_parse_type().unwrap_or(Type::unit()); ast::Signature { - params, + params: Arc::new(params), rettype, - typeparams, + typeparams: Arc::new(typeparams), } } @@ -795,7 +795,7 @@ fn parse_fn_type(&mut self) -> Type { let (params, typeparams) = self.parse_type_list_with_typeparams(); let rettype = self.try_parse_type().unwrap_or(Type::unit()); - Type::Func(params, Box::new(rettype), typeparams) + Type::function(¶ms, &rettype, &typeparams) } /// Parse the fields for a struct *type decl* @@ -890,19 +890,19 @@ } }); self.expect(T::RBrace); - Some(Type::tuple(body)) + Some(Type::tuple(Arc::new(body))) } fn try_parse_struct_type(&mut self) -> Option { let (fields, type_params) = self.parse_struct_fields(); self.expect(T::End); - Some(Type::Struct(fields, type_params)) + Some(Type::Struct(Arc::new(fields), Arc::new(type_params))) } fn parse_enum_type(&mut self) -> Type { let variants = self.parse_enum_fields(); self.expect(T::End); - Type::Enum(variants) + Type::Enum(Arc::new(variants)) } /// isomorphic-ish with parse_type_list() @@ -941,7 +941,7 @@ .map(|ty| Type::Generic(ty)) .collect(); */ - Type::Sum(fields, generics) + Type::Sum(Arc::new(fields), Arc::new(generics)) } fn parse_exprs(&mut self) -> Vec { @@ -1384,7 +1384,7 @@ } else { vec![] }; - Type::Named(Sym::new(s), type_params) + Type::Named(Sym::new(s), Arc::new(type_params)) } } T::At => { @@ -1401,11 +1401,11 @@ assert!(len >= 0); self.expect(T::RBracket); let inner = self.parse_type(); - Type::Array(Box::new(inner), len as usize) + Type::Array(Arc::new(inner), len as usize) } T::Ampersand => { let next = self.try_parse_type()?; - Type::Uniq(Box::new(next)) + Type::Uniq(Arc::new(next)) } _ => { // Wind the parse stream back to wherever we started @@ -1552,9 +1552,9 @@ ast::Decl::Function { name: Sym::new("foo"), signature: ast::Signature { - params: vec![(Sym::new("x"), i32_t.clone())], + params: Arc::new(vec![(Sym::new("x"), i32_t.clone())]), rettype: i32_t, - typeparams: vec![], + typeparams: Arc::new(vec![]), }, body: vec![Expr::int(9)], doc_comment: vec![], diff --git a/src/passes.rs b/src/passes.rs --- a/src/passes.rs +++ b/src/passes.rs @@ -319,8 +319,8 @@ } } -fn types_map(typs: Vec, f: &mut dyn FnMut(Type) -> Type) -> Vec { - typs.into_iter().map(|t| type_map(t, f)).collect() +fn types_map(typs: Arc>, f: &mut dyn FnMut(Type) -> Type) -> Arc> { + Arc::new(typs.iter().cloned().map(|t| type_map(t, f)).collect()) } /// Recursion scheme to turn one type into another. @@ -330,15 +330,17 @@ /// it generic over any iterator, if we want to make life even harder for /// ourself. fn types_map_btree( - typs: BTreeMap, + typs: Arc>, f: &mut dyn FnMut(Type) -> Type, - ) -> BTreeMap + ) -> Arc> where - K: Ord, + K: Ord + Clone, { - typs.into_iter() - .map(|(key, ty)| (key, type_map(ty, f))) - .collect() + Arc::new( + typs.iter() + .map(|(key, ty)| (key.clone(), type_map(ty.clone(), f))) + .collect(), + ) } let res = match typ { Type::Struct(fields, generics) => { @@ -349,12 +351,12 @@ let new_fields = types_map_btree(fields, f); Type::Sum(new_fields, generics) } - Type::Array(ty, len) => Type::Array(Box::new(type_map(*ty, f)), len), + Type::Array(ty, len) => Type::array(&type_map(ty.as_ref().clone(), f), len), Type::Func(args, rettype, typeparams) => { let new_args = types_map(args, f); - let new_rettype = type_map(*rettype, f); + let new_rettype = type_map(rettype.as_ref().clone(), f); let new_typeparams = types_map(typeparams, f); - Type::Func(new_args, Box::new(new_rettype), new_typeparams) + Type::function(&new_args, &new_rettype, &new_typeparams) } // Not super sure whether this is necessary, but can't hurt. Type::Named(nm, tys) => Type::Named(nm, types_map(tys, f)), @@ -363,8 +365,8 @@ Type::Enum(_) => typ, Type::Generic(_) => typ, Type::Uniq(t) => { - let new_t = type_map(*t, f); - Type::Uniq(Box::new(new_t)) + let new_t = type_map(t.as_ref().clone(), f); + Type::Uniq(Arc::new(new_t)) } }; f(res) @@ -374,11 +376,12 @@ fn signature_map(sig: hir::Signature, f: &mut dyn FnMut(Type) -> Type) -> hir::Signature { let new_params = sig .params - .into_iter() + .iter() + .cloned() .map(|(sym, ty)| (sym, type_map(ty, f))) .collect(); hir::Signature { - params: new_params, + params: Arc::new(new_params), rettype: type_map(sig.rettype, f), typeparams: types_map(sig.typeparams, f), } diff --git a/src/passes/struct_to_tuple.rs b/src/passes/struct_to_tuple.rs --- a/src/passes/struct_to_tuple.rs +++ b/src/passes/struct_to_tuple.rs @@ -38,9 +38,11 @@ // on the field names. // TODO: What do we do with generics? Anything? - let tuple_fields = fields.values().map(|ty| type_map(ty.clone(), &mut tuplize_type)) + let tuple_fields = fields + .values() + .map(|ty| type_map(ty.clone(), &mut tuplize_type)) .collect(); - Type::tuple(tuple_fields) + Type::tuple(Arc::new(tuple_fields)) } other => other, } @@ -77,10 +79,7 @@ .map(|(ky, vl)| (offset_of_field(type_body, ky), vl)) .collect(); ordered_body.sort_by(|a, b| a.0.cmp(&b.0)); - let new_body = ordered_body - .into_iter() - .map(|(_i, expr)| expr) - .collect(); + let new_body = ordered_body.into_iter().map(|(_i, expr)| expr).collect(); E::TupleCtor { body: new_body } } @@ -271,14 +270,15 @@ let mut body = BTreeMap::new(); body.insert(Sym::new("foo"), Type::i32()); body.insert(Sym::new("bar"), Type::i64()); + let body = Arc::new(body); - let desired = tuplize_type(Type::Struct(body.clone(), vec![])); - let inp = Type::Struct(body, vec![]); + let desired = tuplize_type(Type::Struct(body.clone(), Arc::new(vec![]))); + let inp = Type::Struct(body, Arc::new(vec![])); let out = type_map(inp.clone(), &mut tuplize_type); assert_eq!(out, desired); - let desired2 = Type::Array(Box::new(out), 3); - let inp2 = Type::Array(Box::new(inp), 3); + let desired2 = Type::array(&out, 3); + let inp2 = Type::array(&inp, 3); let out2 = type_map(inp2, &mut tuplize_type); assert_eq!(out2, desired2); } diff --git a/src/typeck.rs b/src/typeck.rs --- a/src/typeck.rs +++ b/src/typeck.rs @@ -409,7 +409,7 @@ let tinfo = match t { Type::Prim(ty) => TypeInfo::Prim(*ty), Type::Never => TypeInfo::Never, - Type::Enum(vals) => TypeInfo::Enum(vals.clone()), + Type::Enum(vals) => TypeInfo::Enum((&**vals).clone()), Type::Named(s, args) => { let new_args = args.iter().map(|t| self.insert_known(t)).collect(); TypeInfo::Named(*s, new_args) @@ -726,11 +726,11 @@ Never => Ok(Type::Never), Prim(ty) => Ok(Type::Prim(*ty)), Ref(id) => self.reconstruct(*id), - Enum(ts) => Ok(Type::Enum(ts.clone())), + Enum(ts) => Ok(Type::Enum(Arc::new(ts.clone()))), Named(s, args) => { let arg_types: Result, _> = args.iter().map(|x| self.reconstruct(*x)).collect(); - Ok(Type::Named(*s, arg_types?)) + Ok(Type::Named(*s, Arc::new(arg_types?))) } Func(args, rettype, typeparams) => { let real_args: Result, TypeError> = @@ -738,10 +738,12 @@ let type_param_types: Result, _> = typeparams.iter().map(|x| self.reconstruct(*x)).collect(); - Ok(Type::Func( - real_args?, - Box::new(self.reconstruct(*rettype)?), - type_param_types?, + let real_args = real_args?; + let type_param_types = type_param_types?; + Ok(Type::function( + &real_args, + &self.reconstruct(*rettype)?, + &type_param_types, )) } TypeParam(name) => Ok(Type::Generic(*name)), @@ -755,11 +757,11 @@ .collect(); // TODO: The empty params here feels suspicious, verify. let params = vec![]; - Ok(Type::Struct(real_body?, params)) + Ok(Type::Struct(Arc::new(real_body?), Arc::new(params))) } Array(ty, len) => { let real_body = self.reconstruct(*ty)?; - Ok(Type::Array(Box::new(real_body), len.unwrap())) + Ok(Type::array(&real_body, len.unwrap())) } Sum(body) => { let real_body: Result, TypeError> = body @@ -770,11 +772,11 @@ }) .collect(); let params = vec![]; - Ok(Type::Sum(real_body?, params)) + Ok(Type::Sum(Arc::new(real_body?), Arc::new(params))) } Uniq(ty) => { let inner_type = self.reconstruct(*ty)?; - Ok(Type::Uniq(Box::new(inner_type))) + Ok(Type::Uniq(Arc::new(inner_type))) } } } @@ -804,7 +806,7 @@ let typeinfo = match t { Type::Prim(val) => TypeInfo::Prim(*val), Type::Never => TypeInfo::Never, - Type::Enum(vals) => TypeInfo::Enum(vals.clone()), + Type::Enum(vals) => TypeInfo::Enum((**vals).clone()), Type::Named(s, args) => { let inst_args: Vec<_> = args.iter().map(|t| helper(tck, named_types, t)).collect(); @@ -914,7 +916,7 @@ impl Symtbl { fn add_builtins(&self, tck: &mut Tck) { - for builtin in &*builtins::BUILTINS { + for builtin in &*builtins::all() { let ty = tck.insert_known(&builtin.sig); self.add_var(builtin.name, ty, false); } @@ -1008,7 +1010,7 @@ */ // Insert info about the function signature let mut params = vec![]; - for (_paramname, paramtype) in &signature.params { + for (_paramname, paramtype) in &*signature.params { let p = tck.insert_known(paramtype); params.push(p); } @@ -1031,7 +1033,7 @@ // Add params to function's scope let _guard = symtbl.push_scope(); - for (paramname, paramtype) in &signature.params { + for (paramname, paramtype) in &*signature.params { let p = tck.insert_known(paramtype); symtbl.add_var(*paramname, p, false); } @@ -1472,7 +1474,8 @@ tck.unify(symtbl, tid, body_type)?; trace!("Done unifying type ctor"); // The type the expression returns - let constructed_type = tck.insert_known(&Type::Named(*name, type_params.clone())); + let constructed_type = + tck.insert_known(&Type::Named(*name, Arc::new(type_params.clone()))); tck.set_expr_type(expr, constructed_type); Ok(constructed_type) } @@ -1623,7 +1626,7 @@ } => { // TODO: Kinda duplicated, not a huge fan. let mut params = vec![]; - for (_paramname, paramtype) in &signature.params { + for (_paramname, paramtype) in &*signature.params { let p = tck.insert_known(paramtype); params.push(p); }