diff --git a/Cargo.lock b/Cargo.lock index 7fdb4d4b50..30bfdff722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,16 @@ dependencies = [ "block-padding", "byte-tools", "byteorder", - "generic-array", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", ] [[package]] @@ -577,6 +586,15 @@ dependencies = [ "objc", ] +[[package]] +name = "cpufeatures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec1028182c380cc45a2e2c5ec841134f2dfd0f8f5f0a5bcd68004f81b5efdf4" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -840,7 +858,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array", + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", ] [[package]] @@ -1134,6 +1161,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1875,6 +1912,14 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "morphic_lib" +version = "0.1.0" +dependencies = [ + "sha2", + "thiserror", +] + [[package]] name = "naga" version = "0.4.1" @@ -2083,22 +2128,21 @@ dependencies = [ [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + +[[package]] +name = "object" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" dependencies = [ "crc32fast", "flate2", "indexmap", - "wasmparser", ] -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - [[package]] name = "once_cell" version = "1.7.2" @@ -2117,6 +2161,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "ordered-float" version = "2.1.1" @@ -3134,7 +3184,7 @@ dependencies = [ "libc", "libloading 0.6.7", "maplit", - "object 0.22.0", + "object 0.24.0", "pretty_assertions 0.5.1", "quickcheck 0.8.5", "quickcheck_macros 0.8.0", @@ -3213,6 +3263,7 @@ dependencies = [ "indoc 0.3.6", "linked-hash-map", "maplit", + "morphic_lib", "pretty_assertions 0.5.1", "quickcheck 0.8.5", "quickcheck_macros 0.8.0", @@ -3558,10 +3609,23 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.7.3", + "digest 0.8.1", "fake-simd", - "opaque-debug", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f6b75b17576b792bef0db1bcc4b8b8bcdf9506744cf34b974195487af6cff2" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -4134,12 +4198,6 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" -[[package]] -name = "wasmparser" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6" - [[package]] name = "wayland-client" version = "0.28.5" diff --git a/LEGAL_DETAILS b/LEGAL_DETAILS index 851d42efd5..3cb8fd3034 100644 --- a/LEGAL_DETAILS +++ b/LEGAL_DETAILS @@ -496,3 +496,22 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. =========================================================== + +* morphic_lib - https://github.com/morphic-lang/morphic_lib + +This source code can be found in vendor/morphic_lib and is licensed under the following terms: + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================== diff --git a/compiler/build/src/lib.rs b/compiler/build/src/lib.rs index e2abf50874..7ee0e97721 100644 --- a/compiler/build/src/lib.rs +++ b/compiler/build/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod link; diff --git a/compiler/builtins/src/lib.rs b/compiler/builtins/src/lib.rs index a8e7c6cf08..a7bd2f3fe8 100644 --- a/compiler/builtins/src/lib.rs +++ b/compiler/builtins/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod bitcode; diff --git a/compiler/can/src/lib.rs b/compiler/can/src/lib.rs index 158426477e..7230ab1a36 100644 --- a/compiler/can/src/lib.rs +++ b/compiler/can/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod annotation; diff --git a/compiler/collections/src/lib.rs b/compiler/collections/src/lib.rs index 5f54b3f3a1..885d50b458 100644 --- a/compiler/collections/src/lib.rs +++ b/compiler/collections/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/compiler/constrain/src/lib.rs b/compiler/constrain/src/lib.rs index 2304b8a738..94067ab076 100644 --- a/compiler/constrain/src/lib.rs +++ b/compiler/constrain/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod builtins; diff --git a/compiler/fmt/src/lib.rs b/compiler/fmt/src/lib.rs index db3530b0aa..23b40379d5 100644 --- a/compiler/fmt/src/lib.rs +++ b/compiler/fmt/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod annotation; diff --git a/compiler/gen/src/lib.rs b/compiler/gen/src/lib.rs index a10c76ef63..092cf0b8a8 100644 --- a/compiler/gen/src/lib.rs +++ b/compiler/gen/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index cf0271db60..4b20416264 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -844,7 +844,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>( .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) } - CallType::LowLevel { op } => { + CallType::LowLevel { op, update_mode: _ } => { run_low_level(env, layout_ids, scope, parent, layout, *op, arguments) } diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 92534238f3..b960c7468e 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -23,7 +23,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] } inlinable_string = "0.1" target-lexicon = "0.10" libloading = "0.6" -object = { version = "0.22", features = ["write"] } +object = { version = "0.24", features = ["write"] } [dev-dependencies] roc_can = { path = "../can" } diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index c6381a455b..6f805efcca 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] @@ -214,7 +214,7 @@ where } } - CallType::LowLevel { op: lowlevel } => { + CallType::LowLevel { op: lowlevel, .. } => { self.build_run_low_level(sym, lowlevel, arguments, layout) } x => Err(format!("the call type, {:?}, is not yet implemented", x)), diff --git a/compiler/gen_dev/src/object_builder.rs b/compiler/gen_dev/src/object_builder.rs index 2c3398c2da..be16da281a 100644 --- a/compiler/gen_dev/src/object_builder.rs +++ b/compiler/gen_dev/src/object_builder.rs @@ -13,7 +13,9 @@ use roc_mono::ir::Proc; use roc_mono::layout::Layout; use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple}; -const VERSION: &str = env!("CARGO_PKG_VERSION"); +// This is used by some code below which is currently commented out. +// See that code for more details! +// const VERSION: &str = env!("CARGO_PKG_VERSION"); /// build_module is the high level builder/delegator. /// It takes the request to build a module and output the object file for the module. @@ -41,6 +43,28 @@ pub fn build_module<'a>( Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little), ) } + Triple { + architecture: TargetArch::X86_64, + binary_format: TargetBF::Macho, + .. + } => { + let backend: Backend64Bit< + x86_64::X86_64GeneralReg, + x86_64::X86_64FloatReg, + x86_64::X86_64Assembler, + x86_64::X86_64SystemV, + > = Backend::new(env, target)?; + build_object( + env, + procedures, + backend, + Object::new( + BinaryFormat::MachO, + Architecture::X86_64, + Endianness::Little, + ), + ) + } Triple { architecture: TargetArch::Aarch64(_), binary_format: TargetBF::Elf, @@ -72,12 +96,16 @@ fn build_object<'a, B: Backend<'a>>( mut output: Object, ) -> Result { let data_section = output.section_id(StandardSection::Data); - let comment = output.add_section(vec![], b"comment".to_vec(), SectionKind::OtherString); + + /* + // Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/rtfeldman/roc/pull/1323 + let comment = output.add_section(vec![], b".comment".to_vec(), SectionKind::OtherString); output.append_section_data( comment, format!("\0roc dev backend version {} \0", VERSION).as_bytes(), 1, ); + */ // Setup layout_ids for procedure calls. let mut layout_ids = roc_mono::layout::LayoutIds::default(); @@ -89,7 +117,7 @@ fn build_object<'a, B: Backend<'a>>( let section_id = output.add_section( output.segment_name(StandardSegment::Text).to_vec(), - format!(".text.{}", fn_name).as_bytes().to_vec(), + format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(), SectionKind::Text, ); @@ -182,7 +210,7 @@ fn build_object<'a, B: Backend<'a>>( offset: offset + proc_offset, size: 32, kind: RelocationKind::PltRelative, - encoding: RelocationEncoding::Generic, + encoding: RelocationEncoding::X86Branch, symbol: sym_id, addend: -4, } diff --git a/compiler/gen_dev/tests/gen_num.rs b/compiler/gen_dev/tests/gen_num.rs index 68b2dca31c..6110845818 100644 --- a/compiler/gen_dev/tests/gen_num.rs +++ b/compiler/gen_dev/tests/gen_num.rs @@ -10,7 +10,7 @@ extern crate libc; #[macro_use] mod helpers; -#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))] +#[cfg(all(test, any(target_os = "linux", target_os = "macos"), any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))] mod gen_num { #[test] fn i64_values() { diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 9d16782c42..aea50c1edb 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2046,6 +2046,14 @@ fn update<'a>( && state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations { + if false { + let it = state.procedures.iter().map(|x| x.1); + + if let Err(e) = roc_mono::alias_analysis::spec_program(it) { + println!("Error in alias analysis: {:?}", e) + } + } + Proc::insert_refcount_operations(arena, &mut state.procedures); Proc::optimize_refcount_operations( @@ -3799,6 +3807,8 @@ fn make_specializations<'a>( home, ident_ids: &mut ident_ids, ptr_bytes, + update_mode_counter: 0, + call_specialization_counter: 0, }; // TODO: for now this final specialization pass is sequential, @@ -3860,6 +3870,8 @@ fn build_pending_specializations<'a>( home, ident_ids: &mut ident_ids, ptr_bytes, + update_mode_counter: 0, + call_specialization_counter: 0, }; // Add modules' decls to Procs diff --git a/compiler/load/src/lib.rs b/compiler/load/src/lib.rs index 977b57afef..7b73ccefd5 100644 --- a/compiler/load/src/lib.rs +++ b/compiler/load/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod docs; diff --git a/compiler/module/src/lib.rs b/compiler/module/src/lib.rs index 2f88540f2d..0bd93ef98f 100644 --- a/compiler/module/src/lib.rs +++ b/compiler/module/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 095fa24529..0999d013ef 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -86,6 +86,10 @@ impl Symbol { }) } + pub fn as_u64(self) -> u64 { + self.0 + } + pub fn fully_qualified(self, interns: &Interns, home: ModuleId) -> InlinableString { let module_id = self.module_id(); @@ -101,6 +105,10 @@ impl Symbol { .into() } } + + pub const fn to_ne_bytes(self) -> [u8; 8] { + self.0.to_ne_bytes() + } } /// Rather than displaying as this: diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index f0f880ea31..cd0e0e6d77 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -15,6 +15,7 @@ roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_problem = { path = "../problem" } ven_pretty = { path = "../../vendor/pretty" } +morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.6.1", features = ["collections"] } hashbrown = { version = "0.11.2", features = [ "bumpalo" ] } ven_ena = { path = "../../vendor/ena" } diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs new file mode 100644 index 0000000000..dac161ef04 --- /dev/null +++ b/compiler/mono/src/alias_analysis.rs @@ -0,0 +1,620 @@ +use morphic_lib::TypeContext; +use morphic_lib::{ + BlockExpr, BlockId, CalleeSpecVar, EntryPointName, ExprContext, FuncDef, FuncDefBuilder, + FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, TypeName, UpdateModeVar, + ValueId, +}; +use roc_collections::all::MutMap; +use roc_module::low_level::LowLevel; +use roc_module::symbol::Symbol; +use std::convert::TryFrom; + +use crate::ir::{Call, CallType, Expr, Literal, ModifyRc, Proc, Stmt}; +use crate::layout::{Builtin, Layout, ListLayout, UnionLayout}; + +// just using one module for now +const MOD_LIST: ModName = ModName(b"UserApp"); +const MOD_APP: ModName = ModName(b"UserApp"); + +pub fn spec_program<'a, I>(procs: I) -> Result +where + I: Iterator>, +{ + let mut main_function = None; + let main_module = { + let mut m = ModDefBuilder::new(); + + for proc in procs { + let spec = proc_spec(proc)?; + + m.add_func(FuncName(&proc.name.to_ne_bytes()), spec)?; + + if format!("{:?}", proc.name).contains("mainForHost") { + main_function = Some(proc.name); + } + } + + m.build()? + }; + + let program = { + let mut p = ProgramBuilder::new(); + p.add_mod(MOD_APP, main_module)?; + p.add_entry_point( + EntryPointName(b"mainForHost"), + MOD_APP, + FuncName(&main_function.unwrap().to_ne_bytes()), + )?; + + p.build()? + }; + + morphic_lib::solve(program) +} + +fn proc_spec(proc: &Proc) -> Result { + let mut builder = FuncDefBuilder::new(); + let mut env = Env::default(); + + let block = builder.add_block(); + + // introduce the arguments + let mut argument_layouts = Vec::new(); + for (i, (layout, symbol)) in proc.args.iter().enumerate() { + let value_id = builder.add_get_tuple_field(block, builder.get_argument(), i as u32)?; + env.symbols.insert(*symbol, value_id); + + argument_layouts.push(*layout); + } + + let value_id = stmt_spec(&mut builder, &mut env, block, &proc.ret_layout, &proc.body)?; + + let root = BlockExpr(block, value_id); + let arg_type_id = layout_spec(&mut builder, &Layout::Struct(&argument_layouts))?; + let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?; + builder.build(arg_type_id, ret_type_id, root) +} + +#[derive(Default)] +struct Env { + symbols: MutMap, + join_points: MutMap, +} + +fn stmt_spec( + builder: &mut FuncDefBuilder, + env: &mut Env, + block: BlockId, + layout: &Layout, + stmt: &Stmt, +) -> Result { + use Stmt::*; + + match stmt { + Let(symbol, expr, layout, continuation) => { + let value_id = expr_spec(builder, env, block, layout, expr)?; + env.symbols.insert(*symbol, value_id); + let result = stmt_spec(builder, env, block, layout, continuation)?; + env.symbols.remove(symbol); + + Ok(result) + } + Invoke { + symbol, + call, + layout: call_layout, + pass, + fail, + } => { + // a call that might throw an exception + + let value_id = call_spec(builder, env, block, call_layout, call)?; + + let pass_block = builder.add_block(); + env.symbols.insert(*symbol, value_id); + let pass_value_id = stmt_spec(builder, env, pass_block, layout, pass)?; + env.symbols.remove(symbol); + let pass_block_expr = BlockExpr(pass_block, pass_value_id); + + let fail_block = builder.add_block(); + let fail_value_id = stmt_spec(builder, env, fail_block, layout, fail)?; + let fail_block_expr = BlockExpr(fail_block, fail_value_id); + + builder.add_choice(block, &[pass_block_expr, fail_block_expr]) + } + Switch { + cond_symbol: _, + cond_layout: _, + branches, + default_branch, + ret_layout, + } => { + let mut cases = Vec::with_capacity(branches.len() + 1); + + let it = branches + .iter() + .map(|(_, _, body)| body) + .chain(std::iter::once(default_branch.1)); + + for branch in it { + let block = builder.add_block(); + let value_id = stmt_spec(builder, env, block, ret_layout, branch)?; + cases.push(BlockExpr(block, value_id)); + } + + builder.add_choice(block, &cases) + } + Ret(symbol) => Ok(env.symbols[symbol]), + Refcounting(modify_rc, continuation) => match modify_rc { + ModifyRc::Inc(symbol, _) | ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => { + let result_type = builder.add_tuple_type(&[])?; + let argument = env.symbols[symbol]; + + // this is how RC is modelled; it recursively touches all heap cells + builder.add_unknown_with(block, &[argument], result_type)?; + + stmt_spec(builder, env, block, layout, continuation) + } + }, + Join { + id, + parameters, + continuation, + remainder, + } => { + let mut type_ids = Vec::new(); + + for p in parameters.iter() { + type_ids.push(layout_spec(builder, &p.layout)?); + } + + let ret_type_id = layout_spec(builder, layout)?; + + let jp_arg_type_id = builder.add_tuple_type(&type_ids)?; + + let (jpid, jp_argument) = + builder.declare_join_point(block, jp_arg_type_id, ret_type_id)?; + + let join_body_sub_block = { + env.join_points.insert(*id, jpid); + let jp_body_block = builder.add_block(); + + // unpack the argument + for (i, p) in parameters.iter().enumerate() { + let value_id = + builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?; + env.symbols.insert(p.symbol, value_id); + } + + let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?; + BlockExpr(jp_body_block, jp_body_value_id) + }; + + // NOTE the symbols bound by the join point can shadow the argument symbols of the + // surrounding function, so we don't remove them from the env here + + let cont_block = builder.add_block(); + let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?; + + env.join_points.remove(id); + builder.define_join_point(jpid, join_body_sub_block)?; + + builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id)) + } + Jump(id, symbols) => { + let ret_type_id = layout_spec(builder, layout)?; + let argument = build_tuple_value(builder, env, block, symbols)?; + + let jpid = env.join_points[id]; + builder.add_jump(block, jpid, argument, ret_type_id) + } + Rethrow | RuntimeError(_) => { + let type_id = layout_spec(builder, layout)?; + + builder.add_terminate(block, type_id) + } + } +} + +fn build_tuple_value( + builder: &mut FuncDefBuilder, + env: &Env, + block: BlockId, + symbols: &[Symbol], +) -> Result { + let mut value_ids = Vec::new(); + + for field in symbols.iter() { + let value_id = match env.symbols.get(field) { + None => panic!( + "Symbol {:?} is not defined in environment {:?}", + field, &env.symbols + ), + Some(x) => *x, + }; + value_ids.push(value_id); + } + + builder.add_make_tuple(block, &value_ids) +} + +fn build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result { + let mut field_types = Vec::new(); + + for field in layouts.iter() { + field_types.push(layout_spec(builder, field)?); + } + + builder.add_tuple_type(&field_types) +} + +fn call_spec( + builder: &mut FuncDefBuilder, + env: &Env, + block: BlockId, + layout: &Layout, + call: &Call, +) -> Result { + use CallType::*; + + match &call.call_type { + ByName { + name: symbol, + full_layout: _, + ret_layout: _, + arg_layouts: _, + specialization_id, + } => { + let array = specialization_id.to_bytes(); + let spec_var = CalleeSpecVar(&array); + + let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?; + let slice = &symbol.to_ne_bytes(); + let name = FuncName(slice); + let module = MOD_APP; + builder.add_call(block, spec_var, module, name, arg_value_id) + } + ByPointer { + name: _, + full_layout: _, + ret_layout: _, + arg_layouts: _, + } => todo!(), + Foreign { + foreign_symbol: _, + ret_layout, + } => { + let arguments: Vec<_> = call + .arguments + .iter() + .map(|symbol| env.symbols[symbol]) + .collect(); + + let result_type = layout_spec(builder, ret_layout)?; + + builder.add_unknown_with(block, &arguments, result_type) + } + LowLevel { op, update_mode } => lowlevel_spec( + builder, + env, + block, + layout, + op, + *update_mode, + call.arguments, + ), + } +} + +fn lowlevel_spec( + builder: &mut FuncDefBuilder, + env: &Env, + block: BlockId, + layout: &Layout, + op: &LowLevel, + update_mode: crate::ir::UpdateModeId, + arguments: &[Symbol], +) -> Result { + use LowLevel::*; + + let type_id = layout_spec(builder, layout)?; + let mode = update_mode.to_bytes(); + let update_mode_var = UpdateModeVar(&mode); + + match op { + NumAdd | NumSub => { + // NOTE these numeric operations panic (e.g. on overflow) + + let pass_block = { + let block = builder.add_block(); + let value = new_num(builder, block)?; + BlockExpr(block, value) + }; + + let fail_block = { + let block = builder.add_block(); + let value = builder.add_terminate(block, type_id)?; + BlockExpr(block, value) + }; + + let sub_block = { + let block = builder.add_block(); + let choice = builder.add_choice(block, &[pass_block, fail_block])?; + + BlockExpr(block, choice) + }; + + builder.add_sub_block(block, sub_block) + } + Eq | NotEq => new_bool(builder, block), + NumLte | NumLt | NumGt | NumGte => new_order(builder, block), + ListLen => { + let list = env.symbols[&arguments[0]]; + + builder.add_get_tuple_field(block, list, LIST_LEN_INDEX) + } + ListGetUnsafe => { + // NOTE the ListGet lowlevel op is only evaluated if the index is in-bounds + let list = env.symbols[&arguments[0]]; + + let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; + + let _unit = builder.add_touch(block, cell)?; + + builder.add_bag_get(block, bag) + } + ListSet => { + let list = env.symbols[&arguments[0]]; + let to_insert = env.symbols[&arguments[2]]; + + let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; + + let _unit = builder.add_update(block, update_mode_var, cell)?; + + builder.add_bag_insert(block, bag, to_insert)?; + + Ok(list) + } + other => todo!("lowlevel op not implemented: {:?}", other), + } +} + +fn build_variant_types( + builder: &mut FuncDefBuilder, + layout: &Layout, +) -> Option>> { + match layout { + Layout::Union(union_layout) => Some(build_variant_types_help(builder, union_layout)), + _ => None, + } +} + +fn build_variant_types_help( + builder: &mut FuncDefBuilder, + union_layout: &UnionLayout, +) -> Result> { + use UnionLayout::*; + + let mut result = Vec::new(); + + match union_layout { + NonRecursive(tags) => { + for tag in tags.iter() { + result.push(build_tuple_type(builder, tag)?); + } + } + Recursive(_) => todo!(), + NonNullableUnwrapped(_) => todo!(), + NullableWrapped { + nullable_id: _, + other_tags: _, + } => todo!(), + NullableUnwrapped { + nullable_id: _, + other_fields: _, + } => todo!(), + } + + Ok(result) +} + +fn expr_spec( + builder: &mut FuncDefBuilder, + env: &Env, + block: BlockId, + layout: &Layout, + expr: &Expr, +) -> Result { + use Expr::*; + + match expr { + Literal(literal) => literal_spec(builder, block, literal), + FunctionPointer(_, _) => todo!(), + Call(call) => call_spec(builder, env, block, layout, call), + Tag { + tag_layout, + tag_name: _, + tag_id, + union_size: _, + arguments, + } => { + let value_id = build_tuple_value(builder, env, block, arguments)?; + let variant_types = build_variant_types(builder, tag_layout).unwrap()?; + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) + } + Struct(fields) => build_tuple_value(builder, env, block, fields), + AccessAtIndex { + index, + field_layouts: _, + structure, + wrapped, + } => { + use crate::ir::Wrapped; + + let value_id = env.symbols[structure]; + + match wrapped { + Wrapped::EmptyRecord => { + // this is a unit value + builder.add_make_tuple(block, &[]) + } + Wrapped::SingleElementRecord => { + todo!("do we unwrap single-element records still?") + } + Wrapped::RecordOrSingleTagUnion => { + builder.add_get_tuple_field(block, value_id, *index as u32) + } + Wrapped::MultiTagUnion => { + builder.add_get_tuple_field(block, value_id, *index as u32) + } + } + } + Array { elem_layout, elems } => { + let type_id = layout_spec(builder, elem_layout)?; + + let list = new_list(builder, block, type_id)?; + + let mut bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + + for symbol in elems.iter() { + let value_id = env.symbols[symbol]; + + bag = builder.add_bag_insert(block, bag, value_id)?; + } + + Ok(bag) + } + + EmptyArray => { + use ListLayout::*; + + match ListLayout::try_from(layout) { + Ok(EmptyList) => { + // just make up an element type + let type_id = builder.add_tuple_type(&[])?; + new_list(builder, block, type_id) + } + Ok(List(element_layout)) => { + let type_id = layout_spec(builder, element_layout)?; + new_list(builder, block, type_id) + } + Err(()) => unreachable!("empty array does not have a list layout"), + } + } + Reuse { .. } => todo!("currently unused"), + Reset(_) => todo!("currently unused"), + RuntimeErrorFunction(_) => { + let type_id = layout_spec(builder, layout)?; + + builder.add_terminate(block, type_id) + } + } +} + +fn literal_spec( + builder: &mut FuncDefBuilder, + block: BlockId, + literal: &Literal, +) -> Result { + use Literal::*; + + match literal { + Str(_) => new_static_string(builder, block), + Int(_) | Float(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]), + } +} + +fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result { + use Layout::*; + + match layout { + Builtin(builtin) => builtin_spec(builder, builtin), + PhantomEmptyStruct => todo!(), + Struct(fields) => build_tuple_type(builder, fields), + Union(union_layout) => { + let variant_types = build_variant_types_help(builder, union_layout)?; + builder.add_union_type(&variant_types) + } + RecursivePointer => todo!(), + FunctionPointer(_, _) => todo!(), + Closure(_, _, _) => todo!(), + Pointer(_) => todo!(), + } +} + +fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result { + use Builtin::*; + + match builtin { + Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize => builder.add_tuple_type(&[]), + Float128 => todo!(), + Float64 => todo!(), + Float32 => todo!(), + Float16 => todo!(), + Str => todo!(), + Dict(_, _) => todo!(), + Set(_) => todo!(), + List(_, _) => { + // TODO should incorporate the element type into the name + Ok(builder.add_named_type(MOD_LIST, TypeName(b"List"))) + } + EmptyStr => todo!(), + EmptyList => todo!(), + EmptyDict => todo!(), + EmptySet => todo!(), + } +} + +// const OK_TAG_ID: u8 = 1u8; +// const ERR_TAG_ID: u8 = 0u8; + +const LIST_CELL_INDEX: u32 = 0; +const LIST_BAG_INDEX: u32 = 1; +const LIST_LEN_INDEX: u32 = 2; + +fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result { + let cell = builder.add_new_heap_cell(block)?; + let bag = builder.add_empty_bag(block, element_type)?; + let length = new_usize(builder, block)?; + builder.add_make_tuple(block, &[cell, bag, length]) +} + +fn new_usize(builder: &mut FuncDefBuilder, block: BlockId) -> Result { + new_num(builder, block) +} + +fn new_static_string(builder: &mut FuncDefBuilder, block: BlockId) -> Result { + let cell = builder.add_new_heap_cell(block)?; + + // immediately mutate the cell, so any future updates on this value are invalid + // updating a static string would cause a crash at runtime + let _ = builder.add_update(block, UpdateModeVar(&[]), cell)?; + + let length = new_usize(builder, block)?; + builder.add_make_tuple(block, &[cell, length]) +} + +fn new_order(builder: &mut FuncDefBuilder, block: BlockId) -> Result { + // always generats EQ + let tag_id = 0; + + let unit = builder.add_tuple_type(&[])?; + let unit_value = builder.add_make_tuple(block, &[])?; + builder.add_make_union(block, &[unit, unit, unit], tag_id, unit_value) +} + +fn new_bool(builder: &mut FuncDefBuilder, block: BlockId) -> Result { + // always generats False + let tag_id = 0; + + let unit = builder.add_tuple_type(&[])?; + let unit_value = builder.add_make_tuple(block, &[])?; + builder.add_make_union(block, &[unit, unit], tag_id, unit_value) +} + +fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result { + // we model all our numbers as unit values + builder.add_make_tuple(block, &[]) +} diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index de98da21dc..de3a7ca7f5 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -402,7 +402,7 @@ impl<'a> BorrowInfState<'a> { self.own_args(arguments); } - LowLevel { op } => { + LowLevel { op, .. } => { // very unsure what demand RunLowLevel should place upon its arguments self.own_var(z); diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index d3bc2753d8..3aa4a7f22d 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -1404,8 +1404,12 @@ fn compile_test_help<'a>( default_branch, }; + let op = LowLevel::Eq; let test = Expr::Call(crate::ir::Call { - call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq }, + call_type: crate::ir::CallType::LowLevel { + op, + update_mode: env.next_update_mode_id(), + }, arguments: arena.alloc([lhs, rhs]), }); diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 89405474a1..6aa8727974 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -442,7 +442,7 @@ impl<'a> Context<'a> { use crate::ir::CallType::*; match &call_type { - LowLevel { op } => { + LowLevel { op, .. } => { let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars); @@ -768,7 +768,7 @@ impl<'a> Context<'a> { use crate::ir::CallType; let stmt = match &call.call_type { - CallType::LowLevel { op } => { + CallType::LowLevel { op, .. } => { let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); self.add_dec_after_lowlevel(call.arguments, ps, cont, &case_live_vars) } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 470fbabc9c..1d416b7865 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -735,6 +735,8 @@ pub struct Env<'a, 'i> { pub home: ModuleId, pub ident_ids: &'i mut IdentIds, pub ptr_bytes: u32, + pub update_mode_counter: u64, + pub call_specialization_counter: u64, } impl<'a, 'i> Env<'a, 'i> { @@ -746,6 +748,26 @@ impl<'a, 'i> Env<'a, 'i> { Symbol::new(self.home, ident_id) } + pub fn next_update_mode_id(&mut self) -> UpdateModeId { + let id = UpdateModeId { + id: self.update_mode_counter, + }; + + self.update_mode_counter += 1; + + id + } + + pub fn next_call_specialization_id(&mut self) -> CallSpecId { + let id = CallSpecId { + id: self.call_specialization_counter, + }; + + self.call_specialization_counter += 1; + + id + } + pub fn is_imported_symbol(&self, symbol: Symbol) -> bool { symbol.module_id() != self.home && !symbol.is_builtin() } @@ -1004,7 +1026,7 @@ impl<'a> Call<'a> { .text("CallByPointer ") .append(alloc.intersperse(it, " ")) } - LowLevel { op: lowlevel } => { + LowLevel { op: lowlevel, .. } => { let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); alloc @@ -1024,18 +1046,39 @@ impl<'a> Call<'a> { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct CallSpecId { + id: u64, +} + +impl CallSpecId { + pub fn to_bytes(self) -> [u8; 8] { + self.id.to_ne_bytes() + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UpdateModeId { + id: u64, +} + +impl UpdateModeId { + pub fn to_bytes(self) -> [u8; 8] { + self.id.to_ne_bytes() + } +} + #[derive(Clone, Debug, PartialEq)] pub enum CallType<'a> { ByName { name: Symbol, - full_layout: Layout<'a>, ret_layout: Layout<'a>, arg_layouts: &'a [Layout<'a>], + specialization_id: CallSpecId, }, ByPointer { name: Symbol, - full_layout: Layout<'a>, ret_layout: Layout<'a>, arg_layouts: &'a [Layout<'a>], @@ -1046,6 +1089,7 @@ pub enum CallType<'a> { }, LowLevel { op: LowLevel, + update_mode: UpdateModeId, }, } @@ -3885,7 +3929,10 @@ pub fn with_hole<'a>( return_on_layout_error!(env, layout_cache.from_var(env.arena, ret_var, env.subs)); let call = self::Call { - call_type: CallType::LowLevel { op }, + call_type: CallType::LowLevel { + op, + update_mode: env.next_update_mode_id(), + }, arguments: arg_symbols, }; @@ -4374,8 +4421,10 @@ pub fn from_can<'a>( let bool_layout = Layout::Builtin(Builtin::Int1); let cond_symbol = env.unique_symbol(); + let op = LowLevel::ExpectTrue; let call_type = CallType::LowLevel { - op: LowLevel::ExpectTrue, + op, + update_mode: env.next_update_mode_id(), }; let arguments = env.arena.alloc([cond_symbol]); let call = self::Call { @@ -5112,11 +5161,13 @@ fn substitute_in_call<'a>( arg_layouts, ret_layout, full_layout, + specialization_id, } => substitute(subs, *name).map(|new| CallType::ByName { name: new, arg_layouts, ret_layout: *ret_layout, full_layout: *full_layout, + specialization_id: *specialization_id, }), CallType::ByPointer { name, @@ -5898,6 +5949,7 @@ fn call_by_pointer<'a>( full_layout: layout, ret_layout: *ret_layout, arg_layouts, + specialization_id: env.next_call_specialization_id(), }; let call = Call { call_type, @@ -6156,6 +6208,7 @@ fn call_by_name<'a>( ret_layout: *ret_layout, full_layout, arg_layouts, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, }; @@ -6200,6 +6253,7 @@ fn call_by_name<'a>( ret_layout: *ret_layout, full_layout, arg_layouts, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, }; @@ -6306,6 +6360,7 @@ fn call_by_name<'a>( ret_layout: *ret_layout, full_layout, arg_layouts, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, } @@ -6369,6 +6424,7 @@ fn call_specialized_proc<'a>( ret_layout: function_layout.result, full_layout: function_layout.full, arg_layouts: function_layout.arguments, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, }; @@ -6390,6 +6446,7 @@ fn call_specialized_proc<'a>( ret_layout: function_layout.result, full_layout: function_layout.full, arg_layouts: function_layout.arguments, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, }; @@ -6409,6 +6466,7 @@ fn call_specialized_proc<'a>( ret_layout: function_layout.result, full_layout: function_layout.full, arg_layouts: function_layout.arguments, + specialization_id: env.next_call_specialization_id(), }, arguments: field_symbols, }; diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 620f700a90..dabe9a3b57 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1996,3 +1996,21 @@ impl<'a> LayoutIds<'a> { LayoutId(answer) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum ListLayout<'a> { + EmptyList, + List(&'a Layout<'a>), +} + +impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> { + type Error = (); + + fn try_from(value: &Layout<'a>) -> Result { + match value { + Layout::Builtin(Builtin::EmptyList) => Ok(ListLayout::EmptyList), + Layout::Builtin(Builtin::List(_, element)) => Ok(ListLayout::List(element)), + _ => Err(()), + } + } +} diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index 98c16ab593..831ff3d8c1 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -1,7 +1,8 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] +pub mod alias_analysis; pub mod borrow; pub mod expand_rc; pub mod inc_dec; diff --git a/compiler/parse/src/lib.rs b/compiler/parse/src/lib.rs index adb36adf78..4ebf534dd3 100644 --- a/compiler/parse/src/lib.rs +++ b/compiler/parse/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/compiler/problem/src/lib.rs b/compiler/problem/src/lib.rs index 333c9990a8..9da9d7b105 100644 --- a/compiler/problem/src/lib.rs +++ b/compiler/problem/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod can; diff --git a/compiler/region/src/lib.rs b/compiler/region/src/lib.rs index 5f54b3f3a1..885d50b458 100644 --- a/compiler/region/src/lib.rs +++ b/compiler/region/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/compiler/reporting/src/lib.rs b/compiler/reporting/src/lib.rs index c8550d9966..ff2791a5f8 100644 --- a/compiler/reporting/src/lib.rs +++ b/compiler/reporting/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index bcf235f2a1..6f1ba3f687 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -100,6 +100,8 @@ mod test_reporting { home, ident_ids: &mut ident_ids, ptr_bytes: 8, + update_mode_counter: 0, + call_specialization_counter: 0, }; let _mono_expr = Stmt::new( &mut mono_env, diff --git a/compiler/solve/src/lib.rs b/compiler/solve/src/lib.rs index 3d093e2c1e..3706c9d5dd 100644 --- a/compiler/solve/src/lib.rs +++ b/compiler/solve/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/compiler/test_gen/src/lib.rs b/compiler/test_gen/src/lib.rs index 723c3986dd..bcbe385cb3 100644 --- a/compiler/test_gen/src/lib.rs +++ b/compiler/test_gen/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits diff --git a/compiler/types/src/lib.rs b/compiler/types/src/lib.rs index 7c85065bd6..780bed8621 100644 --- a/compiler/types/src/lib.rs +++ b/compiler/types/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod builtin_aliases; diff --git a/compiler/unify/src/lib.rs b/compiler/unify/src/lib.rs index 39437895b0..06addd9f5b 100644 --- a/compiler/unify/src/lib.rs +++ b/compiler/unify/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] diff --git a/editor/src/lang/constrain.rs b/editor/src/lang/constrain.rs index 6a940c50f4..2832cef8ac 100644 --- a/editor/src/lang/constrain.rs +++ b/editor/src/lang/constrain.rs @@ -286,9 +286,9 @@ pub fn constrain_expr<'a>( } => { // The expression that evaluates to the function being called, e.g. `foo` in // (foo) bar baz - let expr = env.pool.get(*expr_node_id); + let call_expr = env.pool.get(*expr_node_id); - let opt_symbol = if let Expr2::Var(symbol) = expr { + let opt_symbol = if let Expr2::Var(symbol) = call_expr { Some(*symbol) } else { None @@ -303,7 +303,7 @@ pub fn constrain_expr<'a>( arity: args.len() as u8, }; - let fn_con = constrain_expr(arena, env, expr, fn_expected, region); + let fn_con = constrain_expr(arena, env, call_expr, fn_expected, region); // The function's return type // TODO: don't use expr_var? @@ -431,6 +431,54 @@ pub fn constrain_expr<'a>( exists(arena, flex_vars, And(and_constraints)) } + Expr2::Access { + expr: expr_id, + field, + field_var, + record_var, + ext_var, + } => { + let ext_type = Type2::Variable(*ext_var); + + let field_type = Type2::Variable(*field_var); + + let record_field = + types::RecordField::Demanded(env.pool.add(field_type.shallow_clone())); + + let record_type = Type2::Record( + PoolVec::new(vec![(*field, record_field)].into_iter(), env.pool), + env.pool.add(ext_type), + ); + + let record_expected = Expected::NoExpectation(record_type); + + let category = Category::Access(field.as_str(env.pool).into()); + + let record_con = Eq( + Type2::Variable(*record_var), + record_expected.shallow_clone(), + category.clone(), + region, + ); + + let access_expr = env.pool.get(*expr_id); + + let constraint = constrain_expr(arena, env, access_expr, record_expected, region); + + let mut flex_vars = BumpVec::with_capacity_in(3, arena); + + flex_vars.push(*record_var); + flex_vars.push(*field_var); + flex_vars.push(*ext_var); + + let mut and_constraints = BumpVec::with_capacity_in(3, arena); + + and_constraints.push(constraint); + and_constraints.push(Eq(field_type, expected, category, region)); + and_constraints.push(record_con); + + exists(arena, flex_vars, And(and_constraints)) + } _ => todo!("implement constaints for {:?}", expr), } } diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index 5e0b583bf7..cd227fcf3f 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -780,6 +780,10 @@ pub fn to_expr2<'a>( (expr, output) } + Defs(loc_defs, loc_ret) => { + todo!("{:?} {:?}", loc_defs, loc_ret) + } + PrecedenceConflict { .. } => { // use roc_problem::can::RuntimeError::*; // diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 45c2fbd68a..f94ac4ccc8 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::dbg_macro)] +#![warn(clippy::dbg_macro)] // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index 2c3a7d1299..4685f3e2bb 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -262,3 +262,15 @@ fn constrain_call_and_accessor() { "Str", ) } + +#[test] +fn constrain_access() { + infer_eq( + indoc!( + r#" + { foo: "bar" }.foo + "# + ), + "Str", + ) +} diff --git a/examples/quicksort/Quicksort.roc b/examples/quicksort/Quicksort.roc index 590e32813f..ca445f1cb2 100644 --- a/examples/quicksort/Quicksort.roc +++ b/examples/quicksort/Quicksort.roc @@ -4,56 +4,56 @@ app "quicksort" provides [ quicksort ] to base quicksort = \originalList -> - - quicksortHelp : List (Num a), Nat, Nat -> List (Num a) - quicksortHelp = \list, low, high -> - if low < high then - when partition low high list is - Pair partitionIndex partitioned -> - partitioned - |> quicksortHelp low (partitionIndex - 1) - |> quicksortHelp (partitionIndex + 1) high - else - list - - - partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ] - partition = \low, high, initialList -> - when List.get initialList high is - Ok pivot -> - when partitionHelp (low - 1) low initialList high pivot is - Pair newI newList -> - Pair (newI + 1) (swap (newI + 1) high newList) - - Err _ -> - Pair (low - 1) initialList - - partitionHelp : Nat, Nat, List (Num c), Nat, (Num c) -> [ Pair Nat (List (Num c)) ] - partitionHelp = \i, j, list, high, pivot -> - if j < high then - when List.get list j is - Ok value -> - if value <= pivot then - partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot - else - partitionHelp i (j + 1) list high pivot - - Err _ -> - Pair i list - else - Pair i list - - - swap : Nat, Nat, List a -> List a - swap = \i, j, list -> - when Pair (List.get list i) (List.get list j) is - Pair (Ok atI) (Ok atJ) -> - list - |> List.set i atJ - |> List.set j atI - - _ -> - [] - n = List.len originalList quicksortHelp originalList 0 (n - 1) + +quicksortHelp : List (Num a), Nat, Nat -> List (Num a) +quicksortHelp = \list, low, high -> + if low < high then + when partition low high list is + Pair partitionIndex partitioned -> + partitioned + |> quicksortHelp low (partitionIndex - 1) + |> quicksortHelp (partitionIndex + 1) high + else + list + + +partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ] +partition = \low, high, initialList -> + when List.get initialList high is + Ok pivot -> + when partitionHelp (low - 1) low initialList high pivot is + Pair newI newList -> + Pair (newI + 1) (swap (newI + 1) high newList) + + Err _ -> + Pair (low - 1) initialList + +partitionHelp : Nat, Nat, List (Num c), Nat, (Num c) -> [ Pair Nat (List (Num c)) ] +partitionHelp = \i, j, list, high, pivot -> + if j < high then + when List.get list j is + Ok value -> + if value <= pivot then + partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot + else + partitionHelp i (j + 1) list high pivot + + Err _ -> + Pair i list + else + Pair i list + + +swap : Nat, Nat, List a -> List a +swap = \i, j, list -> + when Pair (List.get list i) (List.get list j) is + Pair (Ok atI) (Ok atJ) -> + list + |> List.set i atJ + |> List.set j atI + + _ -> + [] + diff --git a/vendor/morphic_lib/.gitignore b/vendor/morphic_lib/.gitignore new file mode 100644 index 0000000000..96ef6c0b94 --- /dev/null +++ b/vendor/morphic_lib/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/vendor/morphic_lib/Cargo.toml b/vendor/morphic_lib/Cargo.toml new file mode 100644 index 0000000000..bf93c8b39e --- /dev/null +++ b/vendor/morphic_lib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "morphic_lib" +version = "0.1.0" +authors = ["William Brandon", "Wilson Berkow", "Frank Dai", "Benjamin Driscoll"] +edition = "2018" + +[dependencies] +thiserror = "1.0.24" +sha2 = "0.9.4" diff --git a/vendor/morphic_lib/LICENSE-APACHE b/vendor/morphic_lib/LICENSE-APACHE new file mode 100644 index 0000000000..1b5ec8b78e --- /dev/null +++ b/vendor/morphic_lib/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/vendor/morphic_lib/LICENSE-MIT b/vendor/morphic_lib/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/vendor/morphic_lib/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/morphic_lib/src/api.rs b/vendor/morphic_lib/src/api.rs new file mode 100644 index 0000000000..604b02f896 --- /dev/null +++ b/vendor/morphic_lib/src/api.rs @@ -0,0 +1,1325 @@ +use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; + +use sha2::{digest::Digest, Sha256}; + +#[derive(Clone, thiserror::Error, Debug)] +#[non_exhaustive] +enum ErrorKind { + #[error("no type found for {0:?}")] + TypeIdNotFound(TypeId), + #[error("no block found for {0:?}")] + BlockIdNotFound(BlockId), + #[error("no value found for {0:?}")] + ValueIdNotFound(ValueId), + #[error("no join point found for {0:?}")] + JoinPointIdNotFound(JoinPointId), + #[error("body of join point {0:?} is not defined")] + JoinPointNotDefined(JoinPointId), + #[error("body of join point {0:?} has already been defined")] + JoinPointAlreadyDefined(JoinPointId), + #[error("block {0:?} has no parent")] + BlockDetached(BlockId), + #[error("block {0:?} already has a parent")] + BlockAlreadyAttached(BlockId), + #[error("callee specialization variable {0:?} already used elsewhere in the same function")] + DuplicateCalleeSpecVar(CalleeSpecBuf), + #[error("update mode variable {0:?} already used elsewhere in the same function")] + DuplicateUpdateModeVar(UpdateModeBuf), + #[error("duplicate type name {0:?} in module")] + DuplicateTypeName(TypeNameBuf), + #[error("duplicate function name {0:?} in module")] + DuplicateFuncName(FuncNameBuf), + #[error("duplicate constant name {0:?} in module")] + DuplicateConstName(ConstNameBuf), + #[error("duplicate module name {0:?} in program")] + DuplicateModName(ModNameBuf), + #[error("duplicate entry point name {0:?} in program")] + DuplicateEntryPointName(EntryPointNameBuf), + #[error("callee specialization variable {0:?} not found")] + CalleeSpecVarNotFound(CalleeSpecBuf), + #[error("update mode variable {0:?} not found")] + UpdateModeVarNotFound(UpdateModeBuf), + #[error("function specialization {0:?} not found")] + FuncSpecNotFound(FuncSpec), + #[error("function {0:?} not found in module")] + FuncNotFound(FuncNameBuf), + #[error("constant {0:?} not found in module")] + ConstNotFound(ConstNameBuf), + #[error("module {0:?} not found in program")] + ModNotFound(ModNameBuf), + #[error("entry point {0:?} not found in program")] + EntryPointNotFound(EntryPointNameBuf), +} + +#[derive(Clone, thiserror::Error, Debug)] +#[error("{kind}")] +pub struct Error { + #[from] + kind: ErrorKind, +} + +pub type Result = std::result::Result; + +// Global identifiers: + +bytes_id! { + pub ModName; + ModNameBuf; +} + +bytes_id! { + pub EntryPointName; + EntryPointNameBuf; +} + +// Module-level identifiers (unique within a module): + +bytes_id! { + pub TypeName; + TypeNameBuf; +} + +bytes_id! { + pub FuncName; + FuncNameBuf; +} + +bytes_id! { + pub ConstName; + ConstNameBuf; +} + +// Local identifiers (unique within a function body): + +/// A reference to an arena-allocated expression object in a function body. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ValueId(pub u32); + +/// A reference to an arena-allocated join point in a function body. +/// +/// A join point is a point where multiple paths of execution "join" up again. In other words, a +/// block with multiple entry points. Join points can also be thought of as continuations that do +/// not escape. See [Compiling without Continuations](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/join-points-pldi17.pdf). +/// Join points are common in functional language IRs, but might also be useful (in the context of +/// this library) for encoding constructs such as for loops. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct JoinPointId(pub u32); + +/// A reference to an arena-allocated block in a function body. +/// +/// A block is a sequence of zero or more expressions. Each expression has an associated +/// `ValueId`, which is unique across all blocks within its function. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BlockId(pub u32); + +/// A reference to an arena-allocated type node in a type definition or function body. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TypeId(pub u32); + +bytes_id! { + /// A client-provided name used as a key to look up callee specializations in the solution + /// tables generated by the analysis engine. + pub CalleeSpecVar; + pub(self) CalleeSpecBuf; +} + +bytes_id! { + /// A client-provided name used as a key to look up concrete update mode flags in the solution + /// tables generated by the analysis engine. + pub UpdateModeVar; + pub(self) UpdateModeBuf; +} + +// Input API: + +forward_trait! { + /// A trait for constructing types in both typedefs and expressions. + /// + /// Types are conceptually represented via trees of arena-allocated nodes. References to these + /// nodes are exposed in the API as integer identifiers. + pub trait TypeContext { + /// Create a local type node referring to a named type defined in a module. + /// + /// A named type is essentially identical to a `newtype` in e.g. Haskell. Named types are + /// useful for defining recursive types. + fn add_named_type(&mut self, mod_: ModName, type_: TypeName) -> TypeId; + + /// Create a local type node representing an anonymous tuple type of the given types. + fn add_tuple_type(&mut self, field_types: &[TypeId]) -> Result; + + /// Create a local type node representing an anonymous tagged union of the given types. + fn add_union_type(&mut self, variant_types: &[TypeId]) -> Result; + + /// Create a local type node representing a heap cell. + /// + /// A heap cell is an abstract object used to model data types which are candidates for + /// in-place mutation. A heap cell has reference semantics, and is always in one of two + /// states: either "fresh" or "mutated". Accessing a mutated heap cell is considered unsafe, + /// and in-place mutations mark heap cells as mutated. The analysis engine tries to perform + /// as many heap cell updates as possible in-place, subject to the constraint that every + /// heap cell access must be safe. + fn add_heap_cell_type(&mut self) -> TypeId; + + /// Create a local type node representing a bag of a given type. + /// + /// A bag is an immutable unordered collection of values of a given type, permitting + /// duplicates. + fn add_bag_type(&mut self, item_type: TypeId) -> Result; + } + + impl TypeDefBuilder => .inner; + impl ExprBuilder => .type_builder; + impl FuncDefBuilder => .expr_builder; + impl ConstDefBuilder => .expr_builder; +} + +#[derive(Debug)] +struct SeqGen { + next: u32, +} + +impl SeqGen { + fn new() -> Self { + Self { next: 0 } + } + + fn next(&mut self) -> u32 { + let result = self.next; + self.next += 1; + result + } + + fn check_in_range(&self, id: u32) -> std::result::Result<(), ()> { + if id < self.next { + Ok(()) + } else { + Err(()) + } + } +} + +/// A `TypeDef` defines the content type of a named type. +pub struct TypeDef; + +struct TypeBuilder { + tid_gen: SeqGen, +} + +impl Default for TypeBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TypeBuilder { + fn new() -> Self { + Self { + tid_gen: SeqGen::new(), + } + } + + fn check(&self, id: TypeId) -> Result<()> { + self.tid_gen + .check_in_range(id.0) + .map_err(|()| ErrorKind::TypeIdNotFound(id).into()) + } +} + +impl TypeContext for TypeBuilder { + fn add_named_type(&mut self, _mod: ModName, _type: TypeName) -> TypeId { + TypeId(self.tid_gen.next()) + } + + fn add_tuple_type(&mut self, field_types: &[TypeId]) -> Result { + for &field_type in field_types { + self.check(field_type)?; + } + Ok(TypeId(self.tid_gen.next())) + } + + fn add_union_type(&mut self, variant_types: &[TypeId]) -> Result { + for &variant_type in variant_types { + self.check(variant_type)?; + } + Ok(TypeId(self.tid_gen.next())) + } + + fn add_heap_cell_type(&mut self) -> TypeId { + TypeId(self.tid_gen.next()) + } + + fn add_bag_type(&mut self, item_type: TypeId) -> Result { + self.check(item_type)?; + Ok(TypeId(self.tid_gen.next())) + } +} + +pub struct TypeDefBuilder { + inner: TypeBuilder, +} + +impl Default for TypeDefBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TypeDefBuilder { + pub fn new() -> Self { + Self { + inner: TypeBuilder::new(), + } + } + + /// Create a `TypeDef` using the given type node as the root. + pub fn build(self, root: TypeId) -> Result { + self.inner.check(root)?; + Ok(TypeDef) + } +} + +/// A block, together with a value to return from the block. +/// +/// The returned value must be in scope at the end of the block. +/// +/// This is conceptually similar to a `let ... in ...` expression. The block corresponds to the +/// sequence of `let` bindings, and the returned value id corresponds to the final `in ...` +/// expression. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct BlockExpr(pub BlockId, pub ValueId); + +forward_trait! { + /// A trait for constructing expressions in both function bodies and constant initializers. + pub trait ExprContext { + /// Declare a join point in a `block`. + /// + /// Returning from the resulting join point after jumping to it will yield control to the + /// parent of the block in which the join point was defined. + /// + /// This function allows you to forward-declare a join point without specifying its body, + /// which makes it possible to define recursive and mutually recursive joint points. + /// However, you must eventually populate the body of each joint point via a call to + /// `define_join_point`. + /// + /// The returned ValueId is the ValueId of the *argument* to the join point, which is in + /// scope within the body of the join point. + fn declare_join_point( + &mut self, + block: BlockId, + arg_type: TypeId, + ret_type: TypeId, + ) -> Result<(JoinPointId, ValueId)>; + + /// Define a join point. + /// + /// Before a join point can be defined, it must be declared via a call to + /// `declare_join_point`. + fn define_join_point(&mut self, join_point: JoinPointId, body: BlockExpr) -> Result<()>; + + /// Add a jump to a join point. + /// + /// The rest of the API assumes that every code path produces a `ValueId`. Join points do + /// not fit this model neatly because jumping to a join point yields control to outside of + /// the current expression. Hence, this function returns a dummy unconstrained `ValueId`, + /// which can be used as if the jump were a regular expression producing a value of type + /// `unreachable_result_type`. + fn add_jump( + &mut self, + block: BlockId, + join_point: JoinPointId, + arg: ValueId, + unreachable_result_type: TypeId, + ) -> Result; + + /// Add a block to the current function body. + /// + /// A block is a sequence of zero or more expressions. Each expression has an associated + /// `ValueId`, which is unique across all blocks within the current function. + /// + /// Blocks form a tree. In particular, blocks may contain other blocks via 'choice' + /// expressions, and building a function with `build` requires providing a 'root' block + /// which acts as the entry point of the function. + /// + /// When a function body is finalized with `build`, every non-root block should have exactly + /// one parent, and the root block should have no parents. + fn add_block(&mut self) -> BlockId; + + /// Add an expression with unknown semantics to a block, operating on a given set of values. + /// + /// The analysis engine will conservatively treat this expression as if it could perform any + /// operation expressible in the modeling language mentioning `args`, except in-place + /// mutations. + /// + /// The analysis engine will not consider any possible behaviors which would require access + /// to values in scope not appearing in `args`, or which would require access to any join + /// point in scope. + /// + /// This will significantly limit optimizations, but could be useful for prototyping. + fn add_unknown_with( + &mut self, + block: BlockId, + args: &[ValueId], + result_type: TypeId, + ) -> Result; + + /// Add a function call expression to a block. + /// + /// The call must be annotated with a `CalleeSpecVar`, to be used in the solution table as a + /// key associated with the appropriate specialization of the callee selected by the + /// analysis engine. + fn add_call( + &mut self, + block: BlockId, + callee_spec_var: CalleeSpecVar, + callee_mod: ModName, + callee: FuncName, + arg: ValueId, + ) -> Result; + + /// Add a const ref expression to a block. + /// + /// This conceptually represents a fetch of a global constant value with static lifetime, + /// initialiazed at the beginning of the program. It is considered unsafe to perform an + /// in-place update on any heap cell reachable from a value returned from a const ref. + fn add_const_ref( + &mut self, + block: BlockId, + const_mod: ModName, + const_: ConstName, + ) -> Result; + + /// Add a 'choice' expression to a block. + /// + /// A 'choice' expression consists of one or more 'case' blocks, each of which returns a + /// value. Semantically, executing a choice expression always executes exactly one of its + /// 'case' blocks. The analysis engine assumes that any 'case' block may be executed + /// whenever the 'choice' expression is encountered; it is as if a nondeterministic process + /// selects the 'case' block to be executed. + fn add_choice(&mut self, block: BlockId, cases: &[BlockExpr]) -> Result; + + /// Add a sub-block expression to a block. + /// + /// The sub-block expression evaluates to the value returned by the sub-block. + /// + /// This is useful in combination with join points, because returning from a join point + /// always yields control to the parent of the enclosing block in which the join point was + /// defined. Without sub-blocks, there would be no way to reduce the scope of a join point + /// within a block. + fn add_sub_block(&mut self, block: BlockId, sub_block: BlockExpr) -> Result; + + /// Add a 'terminate' expression to a block. + /// + /// Semantically, a 'terminate' expression is modeled as immediately terminating the entire + /// program. Terminating the program is considered to be safe behavior. + /// + /// The type of the resulting value can be chosen freely, because a 'terminate' expression + /// never actually returns (this is analogous to `panic!()` in Rust, which may be used where + /// a value of any type is expected). + fn add_terminate(&mut self, block: BlockId, result_type: TypeId) -> Result; + + /// Add an expresion which creates a fresh heap cell to a block. + fn add_new_heap_cell(&mut self, block: BlockId) -> Result; + + /// Add a 'touch' expression to a block. + /// + /// A 'touch' expression represents "reading" the contents of a heap cell. Semantically, + /// 'touch'ing a heap cell which has been mutated is considered *unsafe*. The analysis + /// engine will choose update mode flags to ensure that this never happens. + /// + /// A 'touch' expression returns the empty tuple type `()`. + fn add_touch(&mut self, block: BlockId, heap_cell: ValueId) -> Result; + + /// Add an 'update' expression to a block. + /// + /// An 'update' expression represents an attempt to "write" the contents of the heap cell. + /// The analysis engine will assign each 'update' operation a concrete "update mode" flag, + /// which is either `UpdateMode::Immutable` or `UpdateMode::InPlace`. When the mode is + /// `Immutable`, an 'update' operation is semantically a no-op. When the mode is `InPlace`, + /// an 'update' operation *mutates* its heap cell, making further 'touch' and 'update' + /// operations on that heap cell unsafe. The analysis engine will always choose update modes + /// such that all possible executions of the program are guaranteed to be safe. + /// + /// Note that performing an 'update' operation on a mutated heap cell is considered + /// *unsafe*. This is a conservative assumption, and accurately models most real-world + /// update operations. If you want a version of 'update' which the analysis engine considers + /// safe to perform on mutated heap cells, see `add_update_write_only`. A call to + /// `add_update` is semantically equivalent to `add_touch` followed by + /// `add_update_write_only`. + /// + /// The 'update' expression must be annotated with an `UpdateModeVar`, to be used in the + /// solution table as a key associated with the concrete update mode selected for this + /// expression by the analysis engine. + /// + /// An 'update' expression returns the empty tuple type `()`. If you want to model an + /// operation which returns an updated version of its input data structure (with the update + /// possibly performed in-place), you can return a fresh heap cell with `add_new_heap_cell`. + /// Returning the original heap cell is almost certainly not what you want, because if the + /// 'update' is performed in-place then any subsequent accesses to the original heap cell + /// will be considered unsafe. + fn add_update( + &mut self, + block: BlockId, + update_mode_var: UpdateModeVar, + heap_cell: ValueId, + ) -> Result; + + /// Add a write-only 'update' expression to a block. + /// + /// This is identical to `add_update`, except that write-only 'update's of mutated heap + /// cells are considered safe. This is probably not what you want for most real-world update + /// operations. + fn add_update_write_only( + &mut self, + block: BlockId, + update_mode_var: UpdateModeVar, + heap_cell: ValueId, + ) -> Result; + + /// Add an expression to a block which creates an empty bag. + /// + /// A bag is an immutable unordered collection of values of a given type, permitting + /// duplicates. + fn add_empty_bag(&mut self, block: BlockId, item_type: TypeId) -> Result; + + /// Add an expression to a block which inserts a value into a bag, producing a new bag. + fn add_bag_insert( + &mut self, + block: BlockId, + bag: ValueId, + to_insert: ValueId, + ) -> Result; + + /// Add an expression to a block which gets an item from a bag, returning the item. + /// + /// The analysis engine assumes that any item in the bag may potentially be obtained from + /// this operation; it is as if the item is chosen by a nondeterministic process. + /// + /// Semantically, if the bag is empty the program is considered to terminate immediately, as + /// if a 'terminate' expression had appeared. Terminating the program in this way is + /// considered safe behavior. + fn add_bag_get(&mut self, block: BlockId, bag: ValueId) -> Result; + + /// Add an expression to a block which removes an item from a bag, producing a two-element + /// tuple containing (1) the bag with the item removed, and (2) the removed item. + /// + /// The analysis engine assumes that any item in the bag may potentially be removed by this + /// operation; it is as if the item to remove is chosen by a nondeterministic process. + // + /// Semantically, if the original bag is empty the program is considered to terminate + /// immediately, as if a 'terminate' expression had appeared. Terminating the program in + /// this way is considered safe behavior. + fn add_bag_remove(&mut self, block: BlockId, bag: ValueId) -> Result; + + /// Add an expression to a block which constructs a tuple value. + fn add_make_tuple(&mut self, block: BlockId, field_vals: &[ValueId]) -> Result; + + /// Add an expression to a block which gets a single field of a tuple value. + fn add_get_tuple_field( + &mut self, + block: BlockId, + tuple: ValueId, + field_idx: u32, + ) -> Result; + + /// Add an expression to a block which constructs a specific variant of an anonymous tagged + /// union. + fn add_make_union( + &mut self, + block: BlockId, + variant_types: &[TypeId], + variant_idx: u32, + to_wrap: ValueId, + ) -> Result; + + /// Add an expression to a block which unwraps a specific variant of an anonymous tagged + /// union. + /// + /// Semantically, if the union value is not actually tagged with the requested variant + /// index, the program is considered to terminate immediately, as if a 'terminate' + /// expression had appeared. Terminating the program as a result of this kind of variant tag + /// mismatch is considered safe behavior. + fn add_unwrap_union( + &mut self, + block: BlockId, + to_unwrap: ValueId, + variant_idx: u32, + ) -> Result; + + /// Add an expression to a block which wraps a value in a named type. + /// + /// The type of the wrapped value must match the content type of the named type. + fn add_make_named( + &mut self, + block: BlockId, + named_mod: ModName, + named: TypeName, + to_wrap: ValueId, + ) -> Result; + + /// Add an expression to a block which unwraps a value wrapped in a named type. + /// + /// The type of the resulting value will match the content type of the named type. + fn add_unwrap_named( + &mut self, + block: BlockId, + named_mod: ModName, + named: TypeName, + to_unwrap: ValueId, + ) -> Result; + } + + impl FuncDefBuilder => .expr_builder; + impl ConstDefBuilder => .expr_builder; +} + +#[derive(Clone, Copy, Debug)] +enum JoinPointState { + Declared, + Defined, +} + +#[derive(Clone, Copy, Debug)] +enum BlockState { + Detached, + Attached, +} + +struct ExprBuilder { + type_builder: TypeBuilder, + bid_gen: SeqGen, + vid_gen: SeqGen, + jid_gen: SeqGen, + blocks: BTreeMap, + join_points: BTreeMap, + callee_spec_vars: BTreeMap, + update_mode_vars: BTreeSet, +} + +impl Default for ExprBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ExprBuilder { + fn new() -> Self { + Self { + type_builder: TypeBuilder::new(), + bid_gen: SeqGen::new(), + vid_gen: SeqGen::new(), + jid_gen: SeqGen::new(), + blocks: BTreeMap::new(), + join_points: BTreeMap::new(), + callee_spec_vars: BTreeMap::new(), + update_mode_vars: BTreeSet::new(), + } + } + + fn check_tid(&self, id: TypeId) -> Result<()> { + self.type_builder.check(id) + } + + fn check_bid(&self, id: BlockId) -> Result<()> { + self.bid_gen + .check_in_range(id.0) + .map_err(|()| ErrorKind::BlockIdNotFound(id).into()) + } + + fn check_vid(&self, id: ValueId) -> Result<()> { + self.vid_gen + .check_in_range(id.0) + .map_err(|()| ErrorKind::ValueIdNotFound(id).into()) + } + + fn check_jid(&self, id: JoinPointId) -> Result<()> { + self.jid_gen + .check_in_range(id.0) + .map_err(|()| ErrorKind::JoinPointIdNotFound(id).into()) + } + + fn finalize_with_root(&mut self, root: BlockExpr) -> Result<()> { + self.attach_block(root.0)?; + self.check_vid(root.1)?; + for (&join_point, state) in &self.join_points { + match state { + JoinPointState::Declared => { + return Err(ErrorKind::JoinPointNotDefined(join_point).into()); + } + JoinPointState::Defined => {} + } + } + for (&block, state) in &self.blocks { + match state { + BlockState::Detached => { + return Err(ErrorKind::BlockDetached(block).into()); + } + BlockState::Attached => {} + } + } + Ok(()) + } + + fn add_argument(&mut self) -> ValueId { + ValueId(self.vid_gen.next()) + } + + fn attach_block(&mut self, block: BlockId) -> Result<()> { + match self.blocks.insert(block, BlockState::Attached) { + Some(BlockState::Detached) => Ok(()), + Some(BlockState::Attached) => Err(ErrorKind::BlockAlreadyAttached(block).into()), + None => Err(ErrorKind::BlockIdNotFound(block).into()), + } + } +} + +impl ExprContext for ExprBuilder { + fn declare_join_point( + &mut self, + block: BlockId, + arg_type: TypeId, + ret_type: TypeId, + ) -> Result<(JoinPointId, ValueId)> { + self.check_bid(block)?; + self.check_tid(arg_type)?; + self.check_tid(ret_type)?; + let join_point = JoinPointId(self.jid_gen.next()); + self.join_points + .insert(join_point, JoinPointState::Declared); + Ok((join_point, ValueId(self.vid_gen.next()))) + } + + fn define_join_point(&mut self, join_point: JoinPointId, body: BlockExpr) -> Result<()> { + self.check_jid(join_point)?; + self.check_bid(body.0)?; + self.check_vid(body.1)?; + self.attach_block(body.0)?; + match self.join_points.insert(join_point, JoinPointState::Defined) { + Some(JoinPointState::Declared) => Ok(()), + Some(JoinPointState::Defined) => { + Err(ErrorKind::JoinPointAlreadyDefined(join_point).into()) + } + None => Err(ErrorKind::JoinPointIdNotFound(join_point).into()), + } + } + + fn add_jump( + &mut self, + block: BlockId, + join_point: JoinPointId, + arg: ValueId, + unreachable_result_type: TypeId, + ) -> Result { + self.check_bid(block)?; + self.check_jid(join_point)?; + self.check_vid(arg)?; + self.check_tid(unreachable_result_type)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_block(&mut self) -> BlockId { + let block = BlockId(self.bid_gen.next()); + self.blocks.insert(block, BlockState::Detached); + block + } + + fn add_unknown_with( + &mut self, + block: BlockId, + args: &[ValueId], + result_type: TypeId, + ) -> Result { + self.check_bid(block)?; + self.check_tid(result_type)?; + for &arg in args { + self.check_vid(arg)?; + } + Ok(ValueId(self.vid_gen.next())) + } + + fn add_call( + &mut self, + block: BlockId, + callee_spec_var: CalleeSpecVar, + callee_mod: ModName, + callee: FuncName, + arg: ValueId, + ) -> Result { + self.check_bid(block)?; + self.check_vid(arg)?; + match self.callee_spec_vars.entry(callee_spec_var.into()) { + Entry::Occupied(entry) => { + return Err(ErrorKind::DuplicateCalleeSpecVar(entry.key().clone()).into()); + } + Entry::Vacant(entry) => { + entry.insert(hash_func_name(callee_mod, callee)); + } + } + Ok(ValueId(self.vid_gen.next())) + } + + fn add_const_ref( + &mut self, + block: BlockId, + _const_mod: ModName, + _const: ConstName, + ) -> Result { + self.check_bid(block)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_choice(&mut self, block: BlockId, cases: &[BlockExpr]) -> Result { + self.check_bid(block)?; + for &BlockExpr(case_block, case_ret) in cases { + self.attach_block(case_block)?; + self.check_vid(case_ret)?; + } + Ok(ValueId(self.vid_gen.next())) + } + + fn add_sub_block(&mut self, block: BlockId, sub_block: BlockExpr) -> Result { + self.check_bid(block)?; + self.attach_block(sub_block.0)?; + self.check_vid(sub_block.1)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_terminate(&mut self, block: BlockId, result_type: TypeId) -> Result { + self.check_bid(block)?; + self.check_tid(result_type)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_new_heap_cell(&mut self, block: BlockId) -> Result { + self.check_bid(block)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_touch(&mut self, block: BlockId, heap_cell: ValueId) -> Result { + self.check_bid(block)?; + self.check_vid(heap_cell)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_update( + &mut self, + block: BlockId, + update_mode_var: UpdateModeVar, + heap_cell: ValueId, + ) -> Result { + self.add_touch(block, heap_cell)?; + self.add_update_write_only(block, update_mode_var, heap_cell) + } + + fn add_update_write_only( + &mut self, + block: BlockId, + update_mode_var: UpdateModeVar, + heap_cell: ValueId, + ) -> Result { + self.check_bid(block)?; + self.check_vid(heap_cell)?; + if !self.update_mode_vars.insert(update_mode_var.into()) { + return Err(ErrorKind::DuplicateUpdateModeVar(update_mode_var.into()).into()); + } + Ok(ValueId(self.vid_gen.next())) + } + + fn add_empty_bag(&mut self, block: BlockId, item_type: TypeId) -> Result { + self.check_bid(block)?; + self.check_tid(item_type)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_bag_insert( + &mut self, + block: BlockId, + bag: ValueId, + to_insert: ValueId, + ) -> Result { + self.check_bid(block)?; + self.check_vid(bag)?; + self.check_vid(to_insert)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_bag_get(&mut self, block: BlockId, bag: ValueId) -> Result { + self.check_bid(block)?; + self.check_vid(bag)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_bag_remove(&mut self, block: BlockId, bag: ValueId) -> Result { + self.check_bid(block)?; + self.check_vid(bag)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_make_tuple(&mut self, block: BlockId, field_vals: &[ValueId]) -> Result { + self.check_bid(block)?; + for &field_val in field_vals { + self.check_vid(field_val)?; + } + Ok(ValueId(self.vid_gen.next())) + } + + fn add_get_tuple_field( + &mut self, + block: BlockId, + tuple: ValueId, + _field_idx: u32, + ) -> Result { + self.check_bid(block)?; + self.check_vid(tuple)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_make_union( + &mut self, + block: BlockId, + variant_types: &[TypeId], + _variant_idx: u32, + to_wrap: ValueId, + ) -> Result { + self.check_bid(block)?; + for &variant_type in variant_types { + self.check_tid(variant_type)?; + } + // TODO: Check variant_idx in range + self.check_vid(to_wrap)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_unwrap_union( + &mut self, + block: BlockId, + to_unwrap: ValueId, + _variant_idx: u32, + ) -> Result { + self.check_bid(block)?; + self.check_vid(to_unwrap)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_make_named( + &mut self, + block: BlockId, + _named_mod: ModName, + _named: TypeName, + to_wrap: ValueId, + ) -> Result { + self.check_bid(block)?; + self.check_vid(to_wrap)?; + Ok(ValueId(self.vid_gen.next())) + } + + fn add_unwrap_named( + &mut self, + block: BlockId, + _named_mod: ModName, + _named: TypeName, + to_unwrap: ValueId, + ) -> Result { + self.check_bid(block)?; + self.check_vid(to_unwrap)?; + Ok(ValueId(self.vid_gen.next())) + } +} + +/// A `FuncDef` defines the signature and body of a function. +pub struct FuncDef { + // We can precompute hashes for all calls because monomorphization isn't implemented yet. + callee_spec_vars: BTreeMap, + update_mode_vars: BTreeSet, +} + +pub struct FuncDefBuilder { + expr_builder: ExprBuilder, + argument: ValueId, +} + +impl Default for FuncDefBuilder { + fn default() -> Self { + Self::new() + } +} + +impl FuncDefBuilder { + pub fn new() -> Self { + let mut expr_builder = ExprBuilder::new(); + let argument = expr_builder.add_argument(); + Self { + expr_builder, + argument, + } + } + + /// Get the value id of the argument to the current function. + /// + /// Every function takes exactly one argument. Multiple arguments can be represented via + /// tuples. + pub fn get_argument(&self) -> ValueId { + self.argument + } + + /// Construct a function from this builder with a given argument type, return type, root block, + /// and value to return from that root block. + pub fn build(mut self, arg_type: TypeId, ret_type: TypeId, root: BlockExpr) -> Result { + // TODO: Perform scope checking and typechecking + self.expr_builder.check_tid(arg_type)?; + self.expr_builder.check_tid(ret_type)?; + self.expr_builder.finalize_with_root(root)?; + Ok(FuncDef { + callee_spec_vars: self.expr_builder.callee_spec_vars, + update_mode_vars: self.expr_builder.update_mode_vars, + }) + } +} + +/// A `ConstDef` defines the type and initializer expression for a global constant. +pub struct ConstDef { + // We can precompute hashes for all calls because monomorphization isn't implemented yet. + callee_spec_vars: BTreeMap, + update_mode_vars: BTreeSet, +} + +pub struct ConstDefBuilder { + expr_builder: ExprBuilder, +} + +impl Default for ConstDefBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ConstDefBuilder { + pub fn new() -> Self { + Self { + expr_builder: ExprBuilder::new(), + } + } + + /// Construct a const from this builder with a given type, root block, and value to return from + /// that root block. + pub fn build(mut self, type_: TypeId, root: BlockExpr) -> Result { + // TODO: Perform scope checking and typechecking + self.expr_builder.check_tid(type_)?; + self.expr_builder.finalize_with_root(root)?; + Ok(ConstDef { + callee_spec_vars: self.expr_builder.callee_spec_vars, + update_mode_vars: self.expr_builder.update_mode_vars, + }) + } +} + +/// A `ModDef` defines a module, which is a collection of named types, functions, and constants. +pub struct ModDef { + func_defs: BTreeMap, + const_defs: BTreeMap, +} + +pub struct ModDefBuilder { + type_names: BTreeSet, + func_defs: BTreeMap, + const_defs: BTreeMap, +} + +impl Default for ModDefBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ModDefBuilder { + pub fn new() -> Self { + Self { + type_names: BTreeSet::new(), + func_defs: BTreeMap::new(), + const_defs: BTreeMap::new(), + } + } + + pub fn build(self) -> Result { + Ok(ModDef { + func_defs: self.func_defs, + const_defs: self.const_defs, + }) + } + + pub fn add_named_type(&mut self, name: TypeName, _type_def: TypeDef) -> Result<()> { + if !self.type_names.insert(name.into()) { + return Err(ErrorKind::DuplicateTypeName(name.into()).into()); + } + Ok(()) + } + + pub fn add_func(&mut self, name: FuncName, func_def: FuncDef) -> Result<()> { + if self.func_defs.insert(name.into(), func_def).is_some() { + return Err(ErrorKind::DuplicateFuncName(name.into()).into()); + } + Ok(()) + } + + pub fn add_const(&mut self, name: ConstName, const_def: ConstDef) -> Result<()> { + if self.const_defs.insert(name.into(), const_def).is_some() { + return Err(ErrorKind::DuplicateConstName(name.into()).into()); + } + Ok(()) + } +} + +/// A `Program` is the input to the analysis engine, consisting of a collection of modules +/// and entry points. +/// +/// Each entry point has an associated main function, which must have signature `() -> ()`. +pub struct Program { + mods: BTreeMap, + entry_points: BTreeMap, +} + +pub struct ProgramBuilder { + mods: BTreeMap, + entry_points: BTreeMap, +} + +impl Default for ProgramBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ProgramBuilder { + pub fn new() -> Self { + Self { + mods: BTreeMap::new(), + entry_points: BTreeMap::new(), + } + } + + pub fn build(self) -> Result { + Ok(Program { + mods: self.mods, + entry_points: self.entry_points, + }) + } + + pub fn add_mod(&mut self, name: ModName, mod_def: ModDef) -> Result<()> { + match self.mods.entry(name.into()) { + Entry::Occupied(entry) => Err(ErrorKind::DuplicateModName(entry.key().clone()).into()), + Entry::Vacant(entry) => { + entry.insert(mod_def); + Ok(()) + } + } + } + + pub fn add_entry_point( + &mut self, + name: EntryPointName, + func_mod: ModName, + func: FuncName, + ) -> Result<()> { + match self.entry_points.entry(name.into()) { + Entry::Occupied(entry) => { + Err(ErrorKind::DuplicateEntryPointName(entry.key().clone()).into()) + } + Entry::Vacant(entry) => { + entry.insert((func_mod.into(), func.into())); + Ok(()) + } + } + } +} + +// Output API: + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum UpdateMode { + Immutable, + InPlace, +} + +pub const SPEC_HASH_BYTES: usize = 32; + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct FuncSpec(pub [u8; SPEC_HASH_BYTES]); + +/// The solution table for an individual specialization. +pub struct FuncSpecSolutions { + func_def: FuncDef, +} + +impl FuncSpecSolutions { + pub fn callee_spec(&self, var: CalleeSpecVar) -> Result { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + match self.func_def.callee_spec_vars.get(&var.into()) { + Some(spec) => Ok(*spec), + None => Err(ErrorKind::CalleeSpecVarNotFound(var.into()).into()), + } + } + + pub fn update_mode(&self, var: UpdateModeVar) -> Result { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + if !self.func_def.update_mode_vars.contains(&var.into()) { + return Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()); + } + Ok(UpdateMode::Immutable) + } +} + +/// Zero or more specializations for a single function, and the solution table for each +/// specialization. +pub struct FuncSolutions { + spec: FuncSpec, + spec_solutions: FuncSpecSolutions, +} + +impl FuncSolutions { + pub fn specs(&self) -> impl Iterator { + std::iter::once(&self.spec) + } + + pub fn spec(&self, spec: &FuncSpec) -> Result<&FuncSpecSolutions> { + if &self.spec != spec { + return Err(ErrorKind::FuncSpecNotFound(*spec).into()); + } + Ok(&self.spec_solutions) + } +} + +/// The solution table for a constant definition. +/// +/// Note that, unlike functions, constant definitions cannot have multiple specializations. +pub struct ConstSolutions { + const_def: ConstDef, +} + +impl ConstSolutions { + pub fn callee_spec(&self, var: CalleeSpecVar) -> Result { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + match self.const_def.callee_spec_vars.get(&var.into()) { + Some(spec) => Ok(*spec), + None => Err(ErrorKind::CalleeSpecVarNotFound(var.into()).into()), + } + } + + pub fn update_mode(&self, var: UpdateModeVar) -> Result { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + if !self.const_def.update_mode_vars.contains(&var.into()) { + return Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()); + } + Ok(UpdateMode::Immutable) + } +} + +/// Specializations and solution tables for a single module. +pub struct ModSolutions { + funcs: BTreeMap, + consts: BTreeMap, +} + +impl ModSolutions { + pub fn func_solutions(&self, func: FuncName) -> Result<&FuncSolutions> { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + match self.funcs.get(&func.into()) { + Some(func_solutions) => Ok(func_solutions), + None => Err(ErrorKind::FuncNotFound(func.into()).into()), + } + } + + pub fn const_solutions(&self, const_: ConstName) -> Result<&ConstSolutions> { + // TODO: The clone here is unnecessary -- avoid it! + match self.consts.get(&const_.into()) { + Some(const_solutions) => Ok(const_solutions), + None => Err(ErrorKind::ConstNotFound(const_.into()).into()), + } + } +} + +/// Specializations and solution tables generated for the entire program. +pub struct Solutions { + mods: BTreeMap, + entry_points: BTreeMap, +} + +impl Solutions { + pub fn mod_solutions(&self, mod_: ModName) -> Result<&ModSolutions> { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + match self.mods.get(&mod_.into()) { + Some(mod_solutions) => Ok(mod_solutions), + None => Err(ErrorKind::ModNotFound(mod_.into()).into()), + } + } + + pub fn entry_point_solution( + &self, + entry_point: EntryPointName, + ) -> Result<(ModName, FuncName, FuncSpec)> { + // TODO: The clone here is unnecessary -- avoid it! + // (might require something like a transmute) + match self.entry_points.get(&entry_point.into()) { + Some((mod_name, func_name)) => { + let spec = hash_func_name(mod_name.borrowed(), func_name.borrowed()); + Ok((mod_name.borrowed(), func_name.borrowed(), spec)) + } + None => Err(ErrorKind::EntryPointNotFound(entry_point.into()).into()), + } + } +} + +pub fn solve(program: Program) -> Result { + Ok(Solutions { + mods: program + .mods + .into_iter() + .map(|(mod_name, mod_def)| { + let mod_sols = ModSolutions { + funcs: mod_def + .func_defs + .into_iter() + .map(|(func_name, func_def)| { + let func_sols = FuncSolutions { + spec: hash_func_name(mod_name.borrowed(), func_name.borrowed()), + spec_solutions: FuncSpecSolutions { func_def }, + }; + (func_name, func_sols) + }) + .collect(), + consts: mod_def + .const_defs + .into_iter() + .map(|(const_name, const_def)| (const_name, ConstSolutions { const_def })) + .collect(), + }; + (mod_name, mod_sols) + }) + .collect(), + entry_points: program.entry_points, + }) +} + +fn hash_bstr(hasher: &mut Sha256, bstr: &[u8]) { + let header = (bstr.len() as u64).to_le_bytes(); + hasher.update(&header); + hasher.update(bstr); +} + +fn hash_func_name(mod_: ModName, func: FuncName) -> FuncSpec { + let mut hasher = Sha256::new(); + hash_bstr(&mut hasher, &mod_.0); + hash_bstr(&mut hasher, &func.0); + FuncSpec(hasher.finalize().into()) +} diff --git a/vendor/morphic_lib/src/bindings.rs b/vendor/morphic_lib/src/bindings.rs new file mode 100644 index 0000000000..c8864e30d9 --- /dev/null +++ b/vendor/morphic_lib/src/bindings.rs @@ -0,0 +1,706 @@ +// TODO: These bindings are incomplete +// TODO: Add test for compatibility with `include/morphic.h` + +use crate::api::*; +use std::{ffi::CString, os::raw::c_char, ptr, slice}; + +macro_rules! check_err { + ($expr:expr) => { + match $expr { + ::std::result::Result::Ok(val) => val, + ::std::result::Result::Err(err) => { + return ::std::boxed::Box::into_raw(::std::boxed::Box::new(err)); + } + } + }; +} + +#[repr(C)] +pub struct RawModName { + data: *mut u8, + len: usize, +} + +impl RawModName { + unsafe fn slice<'a>(&self) -> ModName<'a> { + ModName(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawEntryPointName { + data: *mut u8, + len: usize, +} + +impl RawEntryPointName { + unsafe fn slice<'a>(&self) -> EntryPointName<'a> { + EntryPointName(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawFuncName { + data: *mut u8, + len: usize, +} + +impl RawFuncName { + unsafe fn slice<'a>(&self) -> FuncName<'a> { + FuncName(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawTypeName { + data: *mut u8, + len: usize, +} + +impl RawTypeName { + unsafe fn slice<'a>(&self) -> TypeName<'a> { + TypeName(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawCalleeSpecVar { + data: *mut u8, + len: usize, +} + +impl RawCalleeSpecVar { + unsafe fn slice<'a>(&self) -> CalleeSpecVar<'a> { + CalleeSpecVar(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawUpdateModeVar { + data: *mut u8, + len: usize, +} + +impl RawUpdateModeVar { + unsafe fn slice<'a>(&self) -> UpdateModeVar<'a> { + UpdateModeVar(slice::from_raw_parts(self.data, self.len)) + } +} + +#[repr(C)] +pub struct RawString { + data: *mut c_char, +} + +impl RawString { + fn new>>(t: T) -> Self { + let c_str = CString::new(t).unwrap(); + Self { + data: c_str.into_raw(), + } + } +} + +fn raw(t: T) -> *mut T { + Box::into_raw(Box::new(t)) +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_String_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Error_Clone(err: &Error) -> Box { + Box::new(err.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Error_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Error_Display(self_: *const Error, out: *mut RawString) { + *out = RawString::new(format!("{}", *self_)); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDef_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_New(out: *mut *mut TypeDefBuilder) { + *out = raw(TypeDefBuilder::new()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_Build( + self_: Box, + root: TypeId, + out: *mut *mut TypeDef, +) -> *mut Error { + *out = raw(check_err!(self_.build(root))); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_AddNamedType( + self_: *mut TypeDefBuilder, + mod_: RawModName, + type_: RawTypeName, + out: *mut TypeId, +) { + *out = (*self_).add_named_type(mod_.slice(), type_.slice()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_AddTupleType( + self_: *mut TypeDefBuilder, + field_types: *const TypeId, + field_types_len: usize, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!((*self_).add_tuple_type(slice::from_raw_parts(field_types, field_types_len))); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_AddUnionType( + self_: *mut TypeDefBuilder, + variant_types: *const TypeId, + variant_types_len: usize, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!( + (*self_).add_union_type(slice::from_raw_parts(variant_types, variant_types_len)) + ); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_AddHeapCellType( + self_: *mut TypeDefBuilder, + out: *mut TypeId, +) { + *out = (*self_).add_heap_cell_type(); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_TypeDefBuilder_AddBagType( + self_: *mut TypeDefBuilder, + item_type: TypeId, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!((*self_).add_bag_type(item_type)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDef_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_New(out: *mut *mut FuncDefBuilder) { + *out = raw(FuncDefBuilder::new()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_Build( + self_: Box, + arg_type: TypeId, + ret_type: TypeId, + root: BlockExpr, + out: *mut *mut FuncDef, +) -> *mut Error { + *out = raw(check_err!(self_.build(arg_type, ret_type, root))); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_DeclareJoinPoint( + self_: *mut FuncDefBuilder, + block: BlockId, + arg_type: TypeId, + ret_type: TypeId, + out0: *mut JoinPointId, + out1: *mut ValueId, +) -> *mut Error { + let (join_point, value) = check_err!((*self_).declare_join_point(block, arg_type, ret_type)); + *out0 = join_point; + *out1 = value; + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_DefineJoinPoint( + self_: *mut FuncDefBuilder, + join_point: JoinPointId, + body: BlockExpr, +) -> *mut Error { + check_err!((*self_).define_join_point(join_point, body)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddJump( + self_: *mut FuncDefBuilder, + block: BlockId, + join_point: JoinPointId, + arg: ValueId, + unreachable_result_type: TypeId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_jump(block, join_point, arg, unreachable_result_type)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddBlock( + self_: *mut FuncDefBuilder, + out: *mut BlockId, +) { + *out = (*self_).add_block(); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddUnknownWith( + self_: *mut FuncDefBuilder, + block: BlockId, + args: *const ValueId, + args_len: usize, + result_type: TypeId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_unknown_with( + block, + slice::from_raw_parts(args, args_len), + result_type + )); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddCall( + self_: *mut FuncDefBuilder, + block: BlockId, + callee_spec_var: RawCalleeSpecVar, + callee_mod: RawModName, + callee: RawFuncName, + arg: ValueId, +) -> *mut Error { + check_err!((*self_).add_call( + block, + callee_spec_var.slice(), + callee_mod.slice(), + callee.slice(), + arg + )); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddChoice( + self_: *mut FuncDefBuilder, + block: BlockId, + cases: *const BlockExpr, + cases_len: usize, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_choice(block, slice::from_raw_parts(cases, cases_len))); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddTerminate( + self_: *mut FuncDefBuilder, + block: BlockId, + result_type: TypeId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_terminate(block, result_type)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddNewHeapCell( + self_: *mut FuncDefBuilder, + block: BlockId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_new_heap_cell(block)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddTouch( + self_: *mut FuncDefBuilder, + block: BlockId, + heap_cell: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_touch(block, heap_cell)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddUpdate( + self_: *mut FuncDefBuilder, + block: BlockId, + update_mode_var: RawUpdateModeVar, + heap_cell: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_update(block, update_mode_var.slice(), heap_cell)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddUpdateWriteOnly( + self_: *mut FuncDefBuilder, + block: BlockId, + update_mode_var: RawUpdateModeVar, + heap_cell: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_update_write_only(block, update_mode_var.slice(), heap_cell)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddEmptyBag( + self_: *mut FuncDefBuilder, + block: BlockId, + item_type: TypeId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_empty_bag(block, item_type)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddBagInsert( + self_: *mut FuncDefBuilder, + block: BlockId, + bag: ValueId, + to_insert: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_bag_insert(block, bag, to_insert)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddBagGet( + self_: *mut FuncDefBuilder, + block: BlockId, + bag: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_bag_get(block, bag)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddBagRemove( + self_: *mut FuncDefBuilder, + block: BlockId, + bag: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_bag_remove(block, bag)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddMakeTuple( + self_: *mut FuncDefBuilder, + block: BlockId, + field_vals: *const ValueId, + field_vals_len: usize, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!( + (*self_).add_make_tuple(block, slice::from_raw_parts(field_vals, field_vals_len)) + ); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddGetTupleField( + self_: *mut FuncDefBuilder, + block: BlockId, + tuple: ValueId, + field_idx: u32, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_get_tuple_field(block, tuple, field_idx)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddMakeUnion( + self_: *mut FuncDefBuilder, + block: BlockId, + variant_types: *const TypeId, + variant_types_len: usize, + variant_idx: u32, + to_wrap: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_make_union( + block, + slice::from_raw_parts(variant_types, variant_types_len), + variant_idx, + to_wrap + )); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddMakeNamed( + self_: *mut FuncDefBuilder, + block: BlockId, + named_mod: RawModName, + named: RawTypeName, + to_wrap: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = check_err!((*self_).add_make_named(block, named_mod.slice(), named.slice(), to_wrap)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddUnwrapNamed( + self_: *mut FuncDefBuilder, + block: BlockId, + named_mod: RawModName, + named: RawTypeName, + to_unwrap: ValueId, + out: *mut ValueId, +) -> *mut Error { + *out = + check_err!((*self_).add_unwrap_named(block, named_mod.slice(), named.slice(), to_unwrap)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddNamedType( + self_: *mut FuncDefBuilder, + mod_: RawModName, + type_: RawTypeName, + out: *mut TypeId, +) { + *out = (*self_).add_named_type(mod_.slice(), type_.slice()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddTupleType( + self_: *mut FuncDefBuilder, + field_types: *const TypeId, + field_types_len: usize, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!((*self_).add_tuple_type(slice::from_raw_parts(field_types, field_types_len))); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddUnionType( + self_: *mut FuncDefBuilder, + variant_types: *const TypeId, + variant_types_len: usize, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!( + (*self_).add_union_type(slice::from_raw_parts(variant_types, variant_types_len)) + ); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddHeapCellType( + self_: *mut FuncDefBuilder, + out: *mut TypeId, +) { + *out = (*self_).add_heap_cell_type(); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddBagType( + self_: *mut FuncDefBuilder, + item_type: TypeId, + out: *mut TypeId, +) -> *mut Error { + *out = check_err!((*self_).add_bag_type(item_type)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDef_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDefBuilder_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDefBuilder_New(out: *mut *mut ModDefBuilder) { + *out = raw(ModDefBuilder::new()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDefBuilder_Build( + self_: Box, + out: *mut *mut ModDef, +) -> *mut Error { + *out = raw(check_err!(self_.build())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDefBuilder_AddNamedType( + self_: *mut ModDefBuilder, + name: RawTypeName, + type_def: Box, +) -> *mut Error { + check_err!((*self_).add_named_type(name.slice(), *type_def)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModDefBuilder_AddFunc( + self_: *mut ModDefBuilder, + name: RawFuncName, + func_def: Box, +) -> *mut Error { + check_err!((*self_).add_func(name.slice(), *func_def)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Program_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ProgramBuilder_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ProgramBuilder_New(out: *mut *mut ProgramBuilder) { + *out = raw(ProgramBuilder::new()); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ProgramBuilder_Build( + self_: Box, + out: *mut *mut Program, +) -> *mut Error { + *out = raw(check_err!(self_.build())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ProgramBuilder_AddMod( + self_: *mut ProgramBuilder, + name: RawModName, + mod_def: Box, +) -> *mut Error { + check_err!((*self_).add_mod(name.slice(), *mod_def)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ProgramBuilder_AddEntryPoint( + self_: *mut ProgramBuilder, + name: RawEntryPointName, + func_mod: RawModName, + func: RawFuncName, +) -> *mut Error { + check_err!((*self_).add_entry_point(name.slice(), func_mod.slice(), func.slice())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncSpecSolutions_CalleeSpec( + self_: *const FuncSpecSolutions, + var: RawCalleeSpecVar, + out: *mut FuncSpec, +) -> *mut Error { + *out = check_err!((*self_).callee_spec(var.slice())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncSpecSolutions_UpdateMode( + self_: *const FuncSpecSolutions, + var: RawUpdateModeVar, + out: *mut UpdateMode, +) -> *mut Error { + *out = check_err!((*self_).update_mode(var.slice())); + ptr::null_mut() +} + +pub struct FuncSpecIter<'a> { + iter: Box>, +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncSpecIter_Next( + self_: *mut FuncSpecIter, + item: *mut *const FuncSpec, +) -> bool { + match (*self_).iter.next() { + Some(val) => { + *item = val; + true + } + None => false, + } +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncSolutions_Specs( + _self: *const FuncSolutions, + _out: *mut *mut FuncSpecIter, +) { + todo!(); +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_FuncSolutions_Spec( + self_: *mut FuncSolutions, + spec: *const FuncSpec, + out: *mut *const FuncSpecSolutions, +) -> *mut Error { + *out = check_err!((*self_).spec(&*spec)); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_ModSolutions_FuncSolutions( + self_: *mut ModSolutions, + func: RawFuncName, + out: *mut *const FuncSolutions, +) -> *mut Error { + *out = check_err!((*self_).func_solutions(func.slice())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Solutions_Drop(_: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Solutions_ModSolutions( + self_: *mut Solutions, + mod_: RawModName, + out: *mut *const ModSolutions, +) -> *mut Error { + *out = check_err!((*self_).mod_solutions(mod_.slice())); + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn Morphic_Solve( + program: Box, + out: *mut *mut Solutions, +) -> *mut Error { + *out = raw(check_err!(solve(*program))); + ptr::null_mut() +} diff --git a/vendor/morphic_lib/src/lib.rs b/vendor/morphic_lib/src/lib.rs new file mode 100644 index 0000000000..3a49195dbe --- /dev/null +++ b/vendor/morphic_lib/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] + +#[macro_use] +mod util; + +mod api; +mod bindings; + +pub use api::*; diff --git a/vendor/morphic_lib/src/util/bytes_id.rs b/vendor/morphic_lib/src/util/bytes_id.rs new file mode 100644 index 0000000000..a5168c4c71 --- /dev/null +++ b/vendor/morphic_lib/src/util/bytes_id.rs @@ -0,0 +1,27 @@ +macro_rules! bytes_id { + ( + // Capturing attributes allows us to capture doc comments + $(#[$annot_borrowed:meta])* $borrowed_vis:vis $borrowed:ident; + $(#[$annot_owned:meta])* $owned_vis:vis $owned:ident; + ) => { + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] + $(#[$annot_borrowed])* + $borrowed_vis struct $borrowed<'a>($borrowed_vis &'a [u8]); + + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + $(#[$annot_owned])* + $owned_vis struct $owned($owned_vis ::std::vec::Vec); + + impl $owned { + fn borrowed<'a>(&'a self) -> $borrowed<'a> { + $borrowed(&self.0) + } + } + + impl<'a> ::std::convert::From<$borrowed<'a>> for $owned { + fn from(borrowed: $borrowed<'a>) -> Self { + $owned(<[u8]>::to_vec(&borrowed.0)) + } + } + } +} diff --git a/vendor/morphic_lib/src/util/forward_trait.rs b/vendor/morphic_lib/src/util/forward_trait.rs new file mode 100644 index 0000000000..c634ec506b --- /dev/null +++ b/vendor/morphic_lib/src/util/forward_trait.rs @@ -0,0 +1,91 @@ +macro_rules! forward_trait { + ( + $(#[$annot:meta])* + $t_vis:vis trait $t_name:ident { + $($methods:tt)* + } + + $($impls:tt)* + ) => { + $(#[$annot])* + $t_vis trait $t_name { $($methods)* } + + forward_trait_impls!(trait $t_name { $($methods)* } $($impls)*); + }; +} + +macro_rules! forward_trait_impls { + ( + trait $t_name:ident { $($methods:tt)* } + ) => { + // Base case: no impls left + }; + + ( + trait $t_name:ident { $($methods:tt)* } + + impl $wrapper:ident => .$field:ident; + + $($impls:tt)* + ) => { + impl $t_name for $wrapper { + forward_trait_impl_body!( { $($methods)* } .$field ); + } + + forward_trait_impls!(trait $t_name { $($methods)* } $($impls)*); + } +} + +macro_rules! forward_trait_impl_body { + ( + {} + .$field:ident + ) => { + // Base case: no methods left + }; + + ( + { + $(#[$annot:meta])* + fn $fn_name:ident(self $(, $arg_name:ident : $arg_ty:ty)* $(,)? ) -> $ret_ty:ty ; + $($methods:tt)* + } + .$field:ident + ) => { + fn $fn_name(self, $($arg_name: $arg_ty),*) -> $ret_ty { + self.$field.$fn_name($($arg_name),*) + } + + forward_trait_impl_body!({ $($methods)* } .$field); + }; + + ( + { + $(#[$annot:meta])* + fn $fn_name:ident(&self $(, $arg_name:ident : $arg_ty:ty)* $(,)? ) -> $ret_ty:ty ; + $($methods:tt)* + } + .$field:ident + ) => { + fn $fn_name(&self, $($arg_name: $arg_ty),*) -> $ret_ty { + self.$field.$fn_name($($arg_name),*) + } + + forward_trait_impl_body!({ $($methods)* } .$field); + }; + + ( + { + $(#[$annot:meta])* + fn $fn_name:ident(&mut self $(, $arg_name:ident : $arg_ty:ty)* $(,)? ) -> $ret_ty:ty ; + $($methods:tt)* + } + .$field:ident + ) => { + fn $fn_name(&mut self, $($arg_name: $arg_ty),*) -> $ret_ty { + self.$field.$fn_name($($arg_name),*) + } + + forward_trait_impl_body!({ $($methods)* } .$field); + }; +} diff --git a/vendor/morphic_lib/src/util/mod.rs b/vendor/morphic_lib/src/util/mod.rs new file mode 100644 index 0000000000..a58e87cdac --- /dev/null +++ b/vendor/morphic_lib/src/util/mod.rs @@ -0,0 +1,5 @@ +#[macro_use] +pub mod bytes_id; + +#[macro_use] +pub mod forward_trait;