From e8fb386bdc4aab7e2768409b93199cafbd63383e Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 21 Jan 2023 17:25:09 -0500 Subject: [PATCH 01/19] chore: Switch from hashmap and hashset to indexmap and indexset --- Cargo.lock | 1 + crates/aiken-lang/src/air.rs | 7 +- crates/aiken-lang/src/builder.rs | 62 ++++++++++----- crates/aiken-lang/src/uplc.rs | 77 +++++++++---------- crates/aiken-project/Cargo.toml | 1 + crates/aiken-project/src/lib.rs | 29 ++++--- .../036/assets/spend/spend/mainnet.addr | 2 +- .../assets/spend/spend/payment_script.json | 2 +- .../036/assets/spend/spend/script.cbor | 2 +- .../036/assets/spend/spend/testnet.addr | 2 +- 10 files changed, 109 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14ed619a..1c243db9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,7 @@ dependencies = [ "futures", "hex", "ignore", + "indexmap", "itertools", "miette", "owo-colors", diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 52791762..150c5266 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -1,5 +1,6 @@ -use std::{collections::HashSet, sync::Arc}; +use std::sync::Arc; +use indexmap::IndexSet; use uplc::builtins::DefaultFunction; use crate::{ @@ -127,8 +128,8 @@ pub enum Air { TupleClause { scope: Vec, tipo: Arc, - indices: HashSet<(usize, String)>, - predefined_indices: HashSet<(usize, String)>, + indices: IndexSet<(usize, String)>, + predefined_indices: IndexSet<(usize, String)>, subject_name: String, count: usize, complex_clause: bool, diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 8ed0bd15..4c791610 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -1,10 +1,6 @@ -use std::{ - cell::RefCell, - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::{cell::RefCell, sync::Arc}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ @@ -72,7 +68,7 @@ pub enum ClauseProperties { needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, - defined_tuple_indices: HashSet<(usize, String)>, + defined_tuple_indices: IndexSet<(usize, String)>, }, } @@ -92,7 +88,7 @@ impl ClauseProperties { needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, - defined_tuple_indices: HashSet::new(), + defined_tuple_indices: IndexSet::new(), } } else { ClauseProperties::ConstrClause { @@ -542,6 +538,32 @@ pub fn list_access_to_uplc( .into(), } } else if names.is_empty() { + // Maybe check list is actually empty or should we leave that to when .. is only + // this would replace term.into() if we decide to + // body: choose_list( + // apply_wrap( + // Term::Builtin(DefaultFunction::TailList).force_wrap(), + // Term::Var(Name { + // text: format!( + // "tail_index_{}_{}", + // current_index, id_list[current_index] + // ), + // unique: 0.into(), + // }), + // ), + // term, + // apply_wrap( + // apply_wrap( + // Term::Builtin(DefaultFunction::Trace).force_wrap(), + // Term::Constant(UplcConstant::String( + // "List contains more items".to_string(), + // )), + // ), + // Term::Delay(Term::Error.into()), + // ) + // .force_wrap(), + // ) + // .into(), Term::Lambda { parameter_name: Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), @@ -757,7 +779,7 @@ pub fn match_ir_for_recursion( } } -pub fn find_generics_to_replace(tipo: &mut Arc, generic_types: &HashMap>) { +pub fn find_generics_to_replace(tipo: &mut Arc, generic_types: &IndexMap>) { if let Some(id) = tipo.get_generic() { //If generic does not have a type we know of like a None in option then just use same type *tipo = generic_types.get(&id).unwrap_or(tipo).clone(); @@ -983,7 +1005,7 @@ pub fn wrap_validator_args(term: Term, arguments: Vec) -> Term, - generic_types: HashMap>, + generic_types: IndexMap>, full_type: &Arc, ) -> (String, Vec) { let mut new_air = ir.clone(); @@ -1390,14 +1412,14 @@ pub fn monomorphize( (new_name, new_air) } -pub fn handle_func_deps_ir( - dep_ir: &mut Vec, +pub fn handle_func_dependencies_ir( + dependencies_ir: &mut Vec, funt_comp: &FuncComponents, func_components: &IndexMap, - defined_functions: &mut HashMap, + defined_functions: &mut IndexMap, func_index_map: &IndexMap>, func_scope: &[u64], - to_be_defined: &mut HashMap, + to_be_defined: &mut IndexMap, ) { let mut funt_comp = funt_comp.clone(); @@ -1448,9 +1470,9 @@ pub fn handle_func_deps_ir( temp_ir.append(&mut recursion_ir); - temp_ir.append(dep_ir); + temp_ir.append(dependencies_ir); - *dep_ir = temp_ir; + *dependencies_ir = temp_ir; if get_common_ancestor(dep_scope, func_scope) == func_scope.to_vec() { defined_functions.insert(dependency, ()); } @@ -1501,7 +1523,7 @@ pub fn handle_recursion_ir( } pub fn lookup_data_type_by_tipo( - data_types: HashMap, + data_types: IndexMap, tipo: &Type, ) -> Option>> { match tipo { @@ -1536,7 +1558,7 @@ pub fn lookup_data_type_by_tipo( pub fn check_replaceable_opaque_type( t: &Arc, - data_types: &HashMap, + data_types: &IndexMap, ) -> bool { let data_type = lookup_data_type_by_tipo(data_types.clone(), t); @@ -1548,12 +1570,12 @@ pub fn check_replaceable_opaque_type( } } -pub fn replace_opaque_type(t: &mut Arc, data_types: HashMap) { +pub fn replace_opaque_type(t: &mut Arc, data_types: IndexMap) { if check_replaceable_opaque_type(t, &data_types) && matches!(&**t, Type::App { .. }) { let data_type = lookup_data_type_by_tipo(data_types.clone(), t).unwrap(); let new_type_fields = data_type.typed_parameters.clone(); - let mut generics_type_map: HashMap> = HashMap::new(); + let mut generics_type_map: IndexMap> = IndexMap::new(); for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) { let mut map = generics_type_map.into_iter().collect_vec(); diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 0201d490..7a550f19 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1,10 +1,6 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, - vec, -}; +use std::{sync::Arc, vec}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ @@ -28,9 +24,10 @@ use crate::{ builder::{ check_replaceable_opaque_type, check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, - get_generics_and_type, handle_func_deps_ir, handle_recursion_ir, list_access_to_uplc, - lookup_data_type_by_tipo, monomorphize, rearrange_clauses, replace_opaque_type, - wrap_validator_args, ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey, + get_generics_and_type, handle_func_dependencies_ir, handle_recursion_ir, + list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, + replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, FuncComponents, + FunctionAccessKey, }, expr::TypedExpr, tipo::{ @@ -41,32 +38,32 @@ use crate::{ }; pub struct CodeGenerator<'a> { - defined_functions: HashMap, - functions: &'a HashMap, - // type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, - data_types: &'a HashMap, - module_types: &'a HashMap, + defined_functions: IndexMap, + functions: &'a IndexMap, + // type_aliases: &'a IndexMap<(String, String), &'a TypeAlias>>, + data_types: &'a IndexMap, + module_types: &'a IndexMap, id_gen: IdGenerator, needs_field_access: bool, - zero_arg_functions: HashMap>, + zero_arg_functions: IndexMap>, } impl<'a> CodeGenerator<'a> { pub fn new( - functions: &'a HashMap, - // type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, - data_types: &'a HashMap, - module_types: &'a HashMap, + functions: &'a IndexMap, + // type_aliases: &'a IndexMap<(String, String), &'a TypeAlias>>, + data_types: &'a IndexMap, + module_types: &'a IndexMap, ) -> Self { CodeGenerator { - defined_functions: HashMap::new(), + defined_functions: IndexMap::new(), functions, // type_aliases, data_types, module_types, id_gen: IdGenerator::new(), needs_field_access: false, - zero_arg_functions: HashMap::new(), + zero_arg_functions: IndexMap::new(), } } @@ -1040,7 +1037,7 @@ impl<'a> CodeGenerator<'a> { PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), }; - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let label = constructor_type.arguments[index].label.clone().unwrap(); @@ -1092,7 +1089,7 @@ impl<'a> CodeGenerator<'a> { }); } } else { - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); @@ -1380,7 +1377,7 @@ impl<'a> CodeGenerator<'a> { needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), - defined_tuple_indices: HashSet::new(), + defined_tuple_indices: IndexSet::new(), }; let mut inner_pattern_vec = vec![]; @@ -1406,7 +1403,7 @@ impl<'a> CodeGenerator<'a> { scope, tipo: pattern_type.clone(), indices: defined_indices, - predefined_indices: HashSet::new(), + predefined_indices: IndexSet::new(), subject_name: clause_properties.original_subject_name().to_string(), count: elems.len(), complex_clause: false, @@ -1625,7 +1622,7 @@ impl<'a> CodeGenerator<'a> { } }; - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let label = constructor_type.arguments[index].label.clone().unwrap(); @@ -1694,7 +1691,7 @@ impl<'a> CodeGenerator<'a> { }); } } else { - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); @@ -1834,8 +1831,8 @@ impl<'a> CodeGenerator<'a> { ); let mut final_func_dep_ir = IndexMap::new(); - let mut zero_arg_defined_functions = HashMap::new(); - let mut to_be_defined = HashMap::new(); + let mut zero_arg_defined_functions = IndexMap::new(); + let mut to_be_defined = IndexMap::new(); let mut dependency_map = IndexMap::new(); let mut dependency_vec = vec![]; @@ -1865,7 +1862,7 @@ impl<'a> CodeGenerator<'a> { if !funt_comp.args.is_empty() { // deal with function dependencies - handle_func_deps_ir( + handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, @@ -1877,16 +1874,17 @@ impl<'a> CodeGenerator<'a> { final_func_dep_ir.insert(func, dep_ir); } else { // since zero arg functions are run at compile time we need to pull all deps - let mut defined_functions = HashMap::new(); + // note anon functions are not included in the above. They exist in a function anyway + let mut defined_functions = IndexMap::new(); // deal with function dependencies in zero arg functions - handle_func_deps_ir( + handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, &mut defined_functions, &func_index_map, func_scope, - &mut HashMap::new(), + &mut IndexMap::new(), ); let mut final_zero_arg_ir = dep_ir; @@ -1895,7 +1893,8 @@ impl<'a> CodeGenerator<'a> { self.convert_opaque_type_to_inner_ir(&mut final_zero_arg_ir); self.zero_arg_functions.insert(func, final_zero_arg_ir); - + // zero arg functions don't contain the dependencies since they are pre-evaluated + // As such we add functions to defined only after dependencies for all other functions are calculated for (key, val) in defined_functions.into_iter() { zero_arg_defined_functions.insert(key, val); } @@ -2095,7 +2094,7 @@ impl<'a> CodeGenerator<'a> { let param_types = constructor.tipo.arg_types().unwrap(); - let mut generics_type_map: HashMap> = HashMap::new(); + let mut generics_type_map: IndexMap> = IndexMap::new(); for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { @@ -2133,7 +2132,7 @@ impl<'a> CodeGenerator<'a> { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); } else { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); - let mut func_calls = HashMap::new(); + let mut func_calls = IndexMap::new(); for ir in func_ir.clone().into_iter() { if let Air::Var { @@ -2169,8 +2168,8 @@ impl<'a> CodeGenerator<'a> { } else if let (Some(function), Type::Fn { .. }) = (function, &*tipo) { - let mut generics_type_map: HashMap> = - HashMap::new(); + let mut generics_type_map: IndexMap> = + IndexMap::new(); let param_types = tipo.arg_types().unwrap(); @@ -4267,7 +4266,7 @@ impl<'a> CodeGenerator<'a> { let record = arg_stack.pop().unwrap(); - let mut args = HashMap::new(); + let mut args = IndexMap::new(); let mut unchanged_field_indices = vec![]; let mut prev_index = 0; for (index, tipo) in indices diff --git a/crates/aiken-project/Cargo.toml b/crates/aiken-project/Cargo.toml index e10ead1b..5bc164e9 100644 --- a/crates/aiken-project/Cargo.toml +++ b/crates/aiken-project/Cargo.toml @@ -16,6 +16,7 @@ fslock = "0.2.1" futures = "0.3.25" hex = "0.4.3" ignore = "0.4.18" +indexmap = "1.9.1" itertools = "0.10.1" miette = { version = "5.3.0", features = ["fancy"] } owo-colors = "3.5.0" diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index b6013b9f..86c3ca7c 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -20,6 +20,7 @@ use aiken_lang::{ IdGenerator, MINT, PUBLISH, SPEND, VALIDATOR_NAMES, WITHDRAW, }; use deps::UseManifest; +use indexmap::IndexMap; use miette::NamedSource; use options::{CodeGenMode, Options}; use package_name::PackageName; @@ -482,9 +483,9 @@ where validators: Vec<(PathBuf, String, TypedFunction)>, ) -> Result, Error> { let mut programs = Vec::new(); - let mut functions = HashMap::new(); - let mut type_aliases = HashMap::new(); - let mut data_types = HashMap::new(); + let mut functions = IndexMap::new(); + let mut type_aliases = IndexMap::new(); + let mut data_types = IndexMap::new(); let prelude_functions = builtins::prelude_functions(&self.id_gen); for (access_key, func) in prelude_functions.iter() { @@ -539,11 +540,15 @@ where .. } = func_def; + let mut modules_map = IndexMap::new(); + + modules_map.extend(self.module_types.clone()); + let mut generator = CodeGenerator::new( &functions, // &type_aliases, &data_types, - &self.module_types, + &modules_map, ); self.event_listener.handle_event(Event::GeneratingUPLC { @@ -573,9 +578,9 @@ where should_collect: fn(&TypedDefinition) -> bool, ) -> Result, Error> { let mut programs = Vec::new(); - let mut functions = HashMap::new(); - let mut type_aliases = HashMap::new(); - let mut data_types = HashMap::new(); + let mut functions = IndexMap::new(); + let mut type_aliases = IndexMap::new(); + let mut data_types = IndexMap::new(); let prelude_functions = builtins::prelude_functions(&self.id_gen); for (access_key, func) in prelude_functions.iter() { @@ -648,21 +653,25 @@ where }) } + let mut modules_map = IndexMap::new(); + + modules_map.extend(self.module_types.clone()); + let mut generator = CodeGenerator::new( &functions, // &type_aliases, &data_types, - &self.module_types, + &modules_map, ); let evaluation_hint = if let Some((bin_op, left_src, right_src)) = func_def.test_hint() { - let left = CodeGenerator::new(&functions, &data_types, &self.module_types) + let left = CodeGenerator::new(&functions, &data_types, &modules_map) .generate(*left_src, vec![], false) .try_into() .unwrap(); - let right = CodeGenerator::new(&functions, &data_types, &self.module_types) + let right = CodeGenerator::new(&functions, &data_types, &modules_map) .generate(*right_src, vec![], false) .try_into() .unwrap(); diff --git a/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr b/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr index ec36701e..1f30832e 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr +++ b/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr @@ -1 +1 @@ -addr1w9zyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90s83wahe \ No newline at end of file +addr1w8ehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssvegjnw \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/payment_script.json b/examples/acceptance_tests/036/assets/spend/spend/payment_script.json index 78c4254d..26f7ac73 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/payment_script.json +++ b/examples/acceptance_tests/036/assets/spend/spend/payment_script.json @@ -1,5 +1,5 @@ { "type": "PlutusScriptV2", "description": "Generated by Aiken", - "cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1" + "cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1" } \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/script.cbor b/examples/acceptance_tests/036/assets/spend/spend/script.cbor index 0d8bf202..8663ae49 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/script.cbor +++ b/examples/acceptance_tests/036/assets/spend/spend/script.cbor @@ -1 +1 @@ -59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1 \ No newline at end of file +59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1 \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/testnet.addr b/examples/acceptance_tests/036/assets/spend/spend/testnet.addr index 4fcad1d6..54c31f14 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/testnet.addr +++ b/examples/acceptance_tests/036/assets/spend/spend/testnet.addr @@ -1 +1 @@ -addr_test1wpzyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90sue6pcu \ No newline at end of file +addr_test1wrehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssh3uwut \ No newline at end of file From a178cee7bf0130233ec725b7fa1657e9b3f58108 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Thu, 19 Jan 2023 18:33:10 +0100 Subject: [PATCH 02/19] Add new acceptance test scenario: 046 --- examples/acceptance_tests/046/aiken.lock | 5 +++++ examples/acceptance_tests/046/aiken.toml | 3 +++ examples/acceptance_tests/046/lib/tests.ak | 9 +++++++++ 3 files changed, 17 insertions(+) create mode 100644 examples/acceptance_tests/046/aiken.lock create mode 100644 examples/acceptance_tests/046/aiken.toml create mode 100644 examples/acceptance_tests/046/lib/tests.ak diff --git a/examples/acceptance_tests/046/aiken.lock b/examples/acceptance_tests/046/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/046/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/046/aiken.toml b/examples/acceptance_tests/046/aiken.toml new file mode 100644 index 00000000..8101704f --- /dev/null +++ b/examples/acceptance_tests/046/aiken.toml @@ -0,0 +1,3 @@ +name = 'aiken-lang/acceptance_test_046' +version = '0.0.0' +description = '' diff --git a/examples/acceptance_tests/046/lib/tests.ak b/examples/acceptance_tests/046/lib/tests.ak new file mode 100644 index 00000000..009e1272 --- /dev/null +++ b/examples/acceptance_tests/046/lib/tests.ak @@ -0,0 +1,9 @@ +test sort_by_1() { + let xs = [[4, 3, 2, 1], [2, 3, 4, 5]] + when xs is { + [[], ys] -> True + [xs, []] -> True + [[x, ..xs2], [y, ..ys2]] -> True + _ -> True + } +} From e36f91c39c3f7eb88abf7ae585e31b7614cae216 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 21 Jan 2023 16:10:26 -0500 Subject: [PATCH 03/19] fix: multiple list cases of the same length weren't being handled --- crates/aiken-lang/src/air.rs | 9 ++- crates/aiken-lang/src/builder.rs | 4 +- crates/aiken-lang/src/uplc.rs | 73 ++++++++++++++++------ examples/acceptance_tests/046/lib/tests.ak | 6 +- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 150c5266..6b845114 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -125,6 +125,10 @@ pub enum Air { complex_clause: bool, }, + WrapClause { + scope: Vec, + }, + TupleClause { scope: Vec, tipo: Arc, @@ -250,6 +254,8 @@ impl Air { | Air::When { scope, .. } | Air::Clause { scope, .. } | Air::ListClause { scope, .. } + | Air::TupleClause { scope, .. } + | Air::WrapClause { scope } | Air::ClauseGuard { scope, .. } | Air::ListClauseGuard { scope, .. } | Air::Discard { scope } @@ -265,8 +271,7 @@ impl Air { | Air::UnOp { scope, .. } | Air::Trace { scope, .. } | Air::TupleAccessor { scope, .. } - | Air::TupleIndex { scope, .. } - | Air::TupleClause { scope, .. } => scope.to_vec(), + | Air::TupleIndex { scope, .. } => scope.to_vec(), } } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 4c791610..5f140886 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -61,7 +61,7 @@ pub enum ClauseProperties { needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, - current_index: usize, + current_index: i64, }, TupleClause { clause_var_name: String, @@ -80,7 +80,7 @@ impl ClauseProperties { needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, - current_index: 0, + current_index: -1, } } else if t.is_tuple() { ClauseProperties::TupleClause { diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 7a550f19..69df1453 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -645,7 +645,14 @@ impl<'a> CodeGenerator<'a> { current_index, .. } => { - let current_clause_index = *current_index; + let current_clause_index = + if let Pattern::List { elements, .. } = &clause.pattern[0] { + elements.len() + } else { + unreachable!() + }; + + let prev_index = *current_index; let subject_name = if current_clause_index == 0 { original_subject_name.clone() @@ -665,20 +672,36 @@ impl<'a> CodeGenerator<'a> { let next_tail = if index == clauses.len() - 1 { None } else { - Some(format!("__tail_{}", current_clause_index)) + let next_list_size = if let Pattern::List { elements, .. } = + &clauses[index + 1].pattern[0] + { + elements.len() + } else { + unreachable!() + }; + + if next_list_size == current_clause_index { + None + } else { + Some(format!("__tail_{}", current_clause_index)) + } }; - ir_stack.push(Air::ListClause { - scope, - tipo: subject_type.clone(), - tail_name: subject_name, - next_tail_name: next_tail, - complex_clause: *clause_properties.is_complex_clause(), - }); + if current_clause_index as i64 == prev_index { + ir_stack.push(Air::WrapClause { scope }); + } else { + ir_stack.push(Air::ListClause { + scope, + tipo: subject_type.clone(), + tail_name: subject_name, + next_tail_name: next_tail, + complex_clause: *clause_properties.is_complex_clause(), + }); + } match clause_properties { ClauseProperties::ListClause { current_index, .. } => { - *current_index += 1; + *current_index = current_clause_index as i64; } _ => unreachable!(), } @@ -1245,24 +1268,18 @@ impl<'a> CodeGenerator<'a> { needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), - current_index: index, + current_index: index as i64, }; let tail_name = format!("{}_{}", new_tail_name, index); if elements.len() - 1 == index { if tail.is_some() { - let tail_name = match *tail.clone().unwrap() { - Pattern::Var { name, .. } => name, - Pattern::Discard { .. } => "_".to_string(), - _ => unreachable!(), - }; - pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: prev_tail_name, - next_tail_name: Some(tail_name), + next_tail_name: None, inverse: true, }); @@ -1331,7 +1348,6 @@ impl<'a> CodeGenerator<'a> { } } - // self.when_recursive_ir(a); Some(item_name) } a @ Pattern::Constructor { @@ -3796,6 +3812,25 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + + Air::WrapClause { .. } => { + let _ = arg_stack.pop().unwrap(); + + let mut term = arg_stack.pop().unwrap(); + let arg = arg_stack.pop().unwrap(); + + term = apply_wrap( + Term::Lambda { + parameter_name: Name { + text: "__other_clauses_delayed".into(), + unique: 0.into(), + }, + body: term.into(), + }, + Term::Delay(arg.into()), + ); + arg_stack.push(term); + } Air::ClauseGuard { subject_name, tipo, .. } => { diff --git a/examples/acceptance_tests/046/lib/tests.ak b/examples/acceptance_tests/046/lib/tests.ak index 009e1272..71276553 100644 --- a/examples/acceptance_tests/046/lib/tests.ak +++ b/examples/acceptance_tests/046/lib/tests.ak @@ -1,9 +1,9 @@ test sort_by_1() { let xs = [[4, 3, 2, 1], [2, 3, 4, 5]] when xs is { - [[], ys] -> True - [xs, []] -> True + [[], ys] -> False + [xs, []] -> False [[x, ..xs2], [y, ..ys2]] -> True - _ -> True + _ -> False } } From d608c5013083368d4942ca02401e207355ab5628 Mon Sep 17 00:00:00 2001 From: Micah Kendall Date: Mon, 23 Jan 2023 01:12:29 +1100 Subject: [PATCH 04/19] Added 048 acceptance test --- examples/acceptance_tests/048/aiken.lock | 5 +++++ examples/acceptance_tests/048/aiken.toml | 3 +++ examples/acceptance_tests/048/lib/tests.ak | 9 +++++++++ examples/acceptance_tests/048/validators/foo.ak | 9 +++++++++ 4 files changed, 26 insertions(+) create mode 100644 examples/acceptance_tests/048/aiken.lock create mode 100644 examples/acceptance_tests/048/aiken.toml create mode 100644 examples/acceptance_tests/048/lib/tests.ak create mode 100644 examples/acceptance_tests/048/validators/foo.ak diff --git a/examples/acceptance_tests/048/aiken.lock b/examples/acceptance_tests/048/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/048/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/048/aiken.toml b/examples/acceptance_tests/048/aiken.toml new file mode 100644 index 00000000..29f3e549 --- /dev/null +++ b/examples/acceptance_tests/048/aiken.toml @@ -0,0 +1,3 @@ +name = 'aiken-lang/acceptance_test_048' +version = '0.0.0' +description = '' diff --git a/examples/acceptance_tests/048/lib/tests.ak b/examples/acceptance_tests/048/lib/tests.ak new file mode 100644 index 00000000..7cef193d --- /dev/null +++ b/examples/acceptance_tests/048/lib/tests.ak @@ -0,0 +1,9 @@ +pub fn when_tuple(a: (Int, Int)) -> Int { + when a is { + (a, b) -> a + } +} + +test when_tuple_1() { + when_tuple((4, 1)) == 4 +} diff --git a/examples/acceptance_tests/048/validators/foo.ak b/examples/acceptance_tests/048/validators/foo.ak new file mode 100644 index 00000000..487266c9 --- /dev/null +++ b/examples/acceptance_tests/048/validators/foo.ak @@ -0,0 +1,9 @@ +fn when_tuple(a: (Int, Int)) -> Int { + when a is { + (a, b) -> a + } +} + +pub fn spend(a, b, c) -> Bool { + when_tuple((4, 1)) == 4 +} From 7206360baa700b36108716793f9823e974984e45 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 24 Jan 2023 10:22:03 -0500 Subject: [PATCH 05/19] feat(when): single when clause now emits warning --- crates/aiken-lang/src/tipo/error.rs | 121 ++++++++++++++++------------ crates/aiken-lang/src/tipo/expr.rs | 15 ++++ 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index edef1333..9068ad6f 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1,7 +1,7 @@ use super::Type; use crate::{ ast::{Annotation, BinOp, CallArg, Span, TodoKind, UntypedPattern}, - expr, + expr::{self, UntypedExpr}, format::Formatter, levenshtein, pretty::Documentable, @@ -172,20 +172,7 @@ You can use '{discard}' and numbers to distinguish between similar names. If you really meant to return that last expression, try to replace it with the following: {sample}"# - , sample = Formatter::new() - .expr(expr) - .to_pretty_string(70) - .lines() - .enumerate() - .map(|(ix, line)| { - if ix == 0 { - format!("╰─▶ {}", line.yellow()) - } else { - format!(" {line}").yellow().to_string() - } - }) - .collect::>() - .join("") + , sample = format_suggestion(expr) ))] LastExpressionIsAssignment { #[label("let-binding as last expression")] @@ -1034,14 +1021,12 @@ fn suggest_import_constructor() -> String { #[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)] pub enum Warning { - #[error("I found a todo left in the code.\n")] - #[diagnostic(help("You probably want to replace that one with real code... eventually."))] - #[diagnostic(code("todo"))] - Todo { - kind: TodoKind, + #[error("I found a record update using all fields; thus redundant.\n")] + #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] + #[diagnostic(code("record_update::all_fields"))] + AllFieldsRecordUpdate { #[label] location: Span, - tipo: Arc, }, #[error( @@ -1056,9 +1041,10 @@ pub enum Warning { location: Span, }, - #[error("I found a literal that is unused.\n")] - #[diagnostic(code("unused::literal"))] - UnusedLiteral { + #[error("I found a record update with no fields; effectively updating nothing.\n")] + #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] + #[diagnostic(code("record_update::no_fields"))] + NoFieldsRecordUpdate { #[label] location: Span, }, @@ -1070,29 +1056,25 @@ pub enum Warning { location: Span, }, - #[error("I found a record update with no fields; effectively updating nothing.\n")] - #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] - #[diagnostic(code("record_update::no_fields"))] - NoFieldsRecordUpdate { + #[error("I found a when expression with a single clause.")] + #[diagnostic( + code("single_when_clause"), + help("Prefer using a {} binding like so...\n\n{}", "let".purple(), format_suggestion(sample)) + )] + SingleWhenClause { #[label] location: Span, + sample: UntypedExpr, }, - #[error("I found a record update using all fields; thus redundant.\n")] - #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] - #[diagnostic(code("record_update::all_fields"))] - AllFieldsRecordUpdate { + #[error("I found a todo left in the code.\n")] + #[diagnostic(help("You probably want to replace that one with real code... eventually."))] + #[diagnostic(code("todo"))] + Todo { + kind: TodoKind, #[label] location: Span, - }, - - #[error("I discovered an unused type: '{}'.\n", name.purple())] - #[diagnostic(code("unused::type"))] - UnusedType { - #[label] - location: Span, - imported: bool, - name: String, + tipo: Arc, }, #[error("I discovered an unused constructor: '{}'.\n", name.purple())] @@ -1107,6 +1089,17 @@ pub enum Warning { name: String, }, + #[error("I discovered an unused imported module: '{}'.\n", name.purple())] + #[diagnostic(help( + "No big deal, but you might want to remove it to get rid of that warning." + ))] + #[diagnostic(code("unused::import::module"))] + UnusedImportedModule { + #[label] + location: Span, + name: String, + }, + #[error("I discovered an unused imported value: '{}'.\n", name.purple())] #[diagnostic(help( "No big deal, but you might want to remove it to get rid of that warning." @@ -1118,12 +1111,21 @@ pub enum Warning { name: String, }, - #[error("I discovered an unused imported module: '{}'.\n", name.purple())] + #[error("I found a literal that is unused.\n")] + #[diagnostic(code("unused::literal"))] + UnusedLiteral { + #[label] + location: Span, + }, + + #[error("I found an unused private function: '{}'.\n", name.purple())] #[diagnostic(help( - "No big deal, but you might want to remove it to get rid of that warning." + "Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ + Otherwise, you might want to get rid of it altogether." + , keyword_pub = "pub".bright_blue() ))] - #[diagnostic(code("unused::import::module"))] - UnusedImportedModule { + #[diagnostic(code("unused::function"))] + UnusedPrivateFunction { #[label] location: Span, name: String, @@ -1142,16 +1144,12 @@ pub enum Warning { name: String, }, - #[error("I found an unused private function: '{}'.\n", name.purple())] - #[diagnostic(help( - "Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ - Otherwise, you might want to get rid of it altogether." - , keyword_pub = "pub".bright_blue() - ))] - #[diagnostic(code("unused::function"))] - UnusedPrivateFunction { + #[error("I discovered an unused type: '{}'.\n", name.purple())] + #[diagnostic(code("unused::type"))] + UnusedType { #[label] location: Span, + imported: bool, name: String, }, @@ -1187,3 +1185,20 @@ pub enum UnknownRecordFieldSituation { /// This unknown record field is being called as a function. i.e. `record.field()` FunctionCall, } + +fn format_suggestion<'a>(sample: &'a UntypedExpr) -> String { + Formatter::new() + .expr(sample) + .to_pretty_string(70) + .lines() + .enumerate() + .map(|(ix, line)| { + if ix == 0 { + format!("╰─▶ {}", line.yellow()) + } else { + format!(" {line}").yellow().to_string() + } + }) + .collect::>() + .join("") +} diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 788178cf..cf9775ab 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1930,6 +1930,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> { clauses: Vec, location: Span, ) -> Result { + // if there is only one clause we want to present a warning + // that suggests that a `let` binding should be used instead. + if clauses.len() == 1 { + self.environment.warnings.push(Warning::SingleWhenClause { + location: clauses[0].location, + sample: UntypedExpr::Assignment { + location: Span::empty(), + value: Box::new(subjects[0].clone()), + pattern: clauses[0].pattern[0].clone(), + kind: AssignmentKind::Let, + annotation: None, + }, + }); + } + let subjects_count = subjects.len(); let mut typed_subjects = Vec::with_capacity(subjects_count); From a485406e3ddc8bd0c79ea39f2bc99206152fb7fb Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 24 Jan 2023 10:26:08 -0500 Subject: [PATCH 06/19] fix: use the location of the pattern not the entire clause --- crates/aiken-lang/src/tipo/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index cf9775ab..caf237a0 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1934,7 +1934,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { // that suggests that a `let` binding should be used instead. if clauses.len() == 1 { self.environment.warnings.push(Warning::SingleWhenClause { - location: clauses[0].location, + location: clauses[0].pattern[0].location(), sample: UntypedExpr::Assignment { location: Span::empty(), value: Box::new(subjects[0].clone()), From ba05f1f070759d716608f84ed81644c6b1df2781 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 24 Jan 2023 11:31:07 -0500 Subject: [PATCH 07/19] feat: add label to span --- crates/aiken-lang/src/tipo/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 9068ad6f..9b99e07f 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1062,7 +1062,7 @@ pub enum Warning { help("Prefer using a {} binding like so...\n\n{}", "let".purple(), format_suggestion(sample)) )] SingleWhenClause { - #[label] + #[label("use let")] location: Span, sample: UntypedExpr, }, From 3c97f057ccb23183b6b4194f8ac20ff0bb395403 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 24 Jan 2023 11:58:52 -0500 Subject: [PATCH 08/19] chore: fix clippy --- crates/aiken-lang/src/tipo/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 9b99e07f..6bd385ee 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1186,7 +1186,7 @@ pub enum UnknownRecordFieldSituation { FunctionCall, } -fn format_suggestion<'a>(sample: &'a UntypedExpr) -> String { +fn format_suggestion(sample: &UntypedExpr) -> String { Formatter::new() .expr(sample) .to_pretty_string(70) From 703fcb451dfadc1f7e5fd44912896ae43b090a70 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 26 Jan 2023 09:33:29 -0500 Subject: [PATCH 09/19] fix(parser,windows): capture carriage return properly --- crates/aiken-lang/src/parser/lexer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 913e27d4..1349ae39 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -52,8 +52,8 @@ pub fn lexer() -> impl Parser, Error = ParseError> { just('|').to(Token::Vbar), just("&&").to(Token::AmperAmper), just('#').to(Token::Hash), - just("\n\n").to(Token::EmptyLine), - just("\n").to(Token::NewLine), + choice((just("\n\n"), just("\r\n\r\n"))).to(Token::EmptyLine), + choice((just("\n"), just("\r\n"))).to(Token::NewLine), )); let grouping = choice(( From 4976ea4164c2ccc83a0bf0c9b0829efc28714390 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 26 Jan 2023 10:07:11 -0500 Subject: [PATCH 10/19] chore: add test for the new token captures --- crates/aiken-lang/src/tests/parser.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 75d5ce75..1d4bb942 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -22,6 +22,22 @@ fn assert_definitions(code: &str, definitions: Vec) { ) } +#[test] +fn windows_newline() { + let code = "use aiken/list\r\n"; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Use(Use { + location: Span::new((), 0..14), + module: vec!["aiken".to_string(), "list".to_string()], + as_name: None, + unqualified: vec![], + package: (), + })], + ) +} + #[test] fn import() { let code = indoc! {r#" From 1af1b6d898bd6fb94bf68002d11e4c8c84a9a771 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 22 Jan 2023 04:18:48 -0500 Subject: [PATCH 11/19] finalize aiken ir with typing --- crates/aiken-lang/src/air.rs | 261 ++++++++++++++++++++----------- crates/aiken-lang/src/builder.rs | 30 +++- crates/aiken-lang/src/uplc.rs | 81 +++++----- 3 files changed, 246 insertions(+), 126 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 6b845114..80ac641a 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -10,6 +10,7 @@ use crate::{ #[derive(Debug, Clone)] pub enum Air { + // Primitives Int { scope: Vec, value: String, @@ -30,17 +31,6 @@ pub enum Air { value: bool, }, - Var { - scope: Vec, - constructor: ValueConstructor, - name: String, - variant_name: String, - }, - - Fn { - scope: Vec, - params: Vec, - }, List { scope: Vec, count: usize, @@ -48,48 +38,30 @@ pub enum Air { tail: bool, }, - ListAccessor { + Tuple { scope: Vec, tipo: Arc, - names: Vec, - tail: bool, + count: usize, }, - ListExpose { + Void { scope: Vec, - tipo: Arc, - tail_head_names: Vec<(String, String)>, - tail: Option<(String, String)>, }, + Var { + scope: Vec, + constructor: ValueConstructor, + name: String, + variant_name: String, + }, + + // Functions Call { scope: Vec, count: usize, - }, - - Builtin { - scope: Vec, - func: DefaultFunction, tipo: Arc, }, - BinOp { - scope: Vec, - name: BinOp, - count: usize, - tipo: Arc, - }, - - Assignment { - scope: Vec, - name: String, - }, - - Assert { - scope: Vec, - constr_index: usize, - }, - DefineFunc { scope: Vec, func_name: String, @@ -99,11 +71,42 @@ pub enum Air { variant_name: String, }, - Lam { + Fn { + scope: Vec, + params: Vec, + }, + + Builtin { + scope: Vec, + func: DefaultFunction, + tipo: Arc, + }, + + // Operators + BinOp { + scope: Vec, + name: BinOp, + count: usize, + tipo: Arc, + }, + + UnOp { + scope: Vec, + op: UnOp, + }, + + // Assignment + Assert { + scope: Vec, + constr_index: usize, + }, + + Let { scope: Vec, name: String, }, + // When When { scope: Vec, tipo: Arc, @@ -153,18 +156,17 @@ pub enum Air { inverse: bool, }, - Discard { - scope: Vec, - }, - Finally { scope: Vec, }, + // If If { scope: Vec, + tipo: Arc, }, + // Record Creation Record { scope: Vec, constr_index: usize, @@ -172,6 +174,14 @@ pub enum Air { count: usize, }, + RecordUpdate { + scope: Vec, + highest_index: usize, + indices: Vec<(usize, Arc)>, + tipo: Arc, + }, + + // Field Access RecordAccess { scope: Vec, record_index: u64, @@ -184,10 +194,26 @@ pub enum Air { indices: Vec<(usize, String, Arc)>, }, - Tuple { + // ListAccess + ListAccessor { scope: Vec, tipo: Arc, - count: usize, + names: Vec, + tail: bool, + }, + + ListExpose { + scope: Vec, + tipo: Arc, + tail_head_names: Vec<(String, String)>, + tail: Option<(String, String)>, + }, + + // Tuple Access + TupleAccessor { + scope: Vec, + names: Vec, + tipo: Arc, }, TupleIndex { @@ -196,6 +222,7 @@ pub enum Air { tuple_index: usize, }, + // Misc. Todo { scope: Vec, label: Option, @@ -213,23 +240,6 @@ pub enum Air { text: Option, tipo: Arc, }, - - RecordUpdate { - scope: Vec, - highest_index: usize, - indices: Vec<(usize, Arc)>, - }, - - UnOp { - scope: Vec, - op: UnOp, - }, - - TupleAccessor { - scope: Vec, - names: Vec, - tipo: Arc, - }, } impl Air { @@ -239,47 +249,93 @@ impl Air { | Air::String { scope, .. } | Air::ByteArray { scope, .. } | Air::Bool { scope, .. } - | Air::Var { scope, .. } | Air::List { scope, .. } - | Air::ListAccessor { scope, .. } - | Air::ListExpose { scope, .. } - | Air::Fn { scope, .. } + | Air::Tuple { scope, .. } + | Air::Void { scope } + | Air::Var { scope, .. } | Air::Call { scope, .. } + | Air::DefineFunc { scope, .. } + | Air::Fn { scope, .. } | Air::Builtin { scope, .. } | Air::BinOp { scope, .. } - | Air::Assignment { scope, .. } + | Air::UnOp { scope, .. } | Air::Assert { scope, .. } - | Air::DefineFunc { scope, .. } - | Air::Lam { scope, .. } + | Air::Let { scope, .. } | Air::When { scope, .. } | Air::Clause { scope, .. } | Air::ListClause { scope, .. } - | Air::TupleClause { scope, .. } | Air::WrapClause { scope } + | Air::TupleClause { scope, .. } | Air::ClauseGuard { scope, .. } | Air::ListClauseGuard { scope, .. } - | Air::Discard { scope } | Air::Finally { scope } | Air::If { scope, .. } | Air::Record { scope, .. } + | Air::RecordUpdate { scope, .. } | Air::RecordAccess { scope, .. } | Air::FieldsExpose { scope, .. } - | Air::Tuple { scope, .. } + | Air::ListAccessor { scope, .. } + | Air::ListExpose { scope, .. } + | Air::TupleAccessor { scope, .. } + | Air::TupleIndex { scope, .. } | Air::Todo { scope, .. } | Air::ErrorTerm { scope, .. } - | Air::RecordUpdate { scope, .. } - | Air::UnOp { scope, .. } - | Air::Trace { scope, .. } - | Air::TupleAccessor { scope, .. } - | Air::TupleIndex { scope, .. } => scope.to_vec(), + | Air::Trace { scope, .. } => scope.clone(), } } pub fn tipo(&self) -> Option> { match self { + Air::Int { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Int".to_string(), + args: vec![], + } + .into(), + ), + Air::String { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "String".to_string(), + args: vec![], + } + .into(), + ), + Air::ByteArray { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "ByteArray".to_string(), + args: vec![], + } + .into(), + ), + Air::Bool { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Bool".to_string(), + args: vec![], + } + .into(), + ), + Air::Void { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Void".to_string(), + args: vec![], + } + .into(), + ), + Air::Var { constructor, .. } => Some(constructor.tipo.clone()), + Air::List { tipo, .. } - | Air::ListAccessor { tipo, .. } - | Air::ListExpose { tipo, .. } + | Air::Tuple { tipo, .. } + | Air::Call { tipo, .. } | Air::Builtin { tipo, .. } | Air::BinOp { tipo, .. } | Air::When { tipo, .. } @@ -287,20 +343,47 @@ impl Air { | Air::ListClause { tipo, .. } | Air::TupleClause { tipo, .. } | Air::ClauseGuard { tipo, .. } + | Air::If { tipo, .. } | Air::ListClauseGuard { tipo, .. } | Air::Record { tipo, .. } + | Air::RecordUpdate { tipo, .. } | Air::RecordAccess { tipo, .. } - | Air::Tuple { tipo, .. } + | Air::ListAccessor { tipo, .. } + | Air::ListExpose { tipo, .. } + | Air::TupleAccessor { tipo, .. } | Air::TupleIndex { tipo, .. } | Air::Todo { tipo, .. } | Air::ErrorTerm { tipo, .. } - | Air::Trace { tipo, .. } - | Air::TupleAccessor { tipo, .. } - | Air::Var { - constructor: ValueConstructor { tipo, .. }, - .. - } => Some(tipo.clone()), - _ => None, + | Air::Trace { tipo, .. } => Some(tipo.clone()), + + Air::DefineFunc { .. } + | Air::Fn { .. } + | Air::Assert { .. } + | Air::Let { .. } + | Air::WrapClause { .. } + | Air::Finally { .. } + | Air::FieldsExpose { .. } => None, + + Air::UnOp { op, .. } => match op { + UnOp::Not => Some( + Type::App { + public: true, + module: String::new(), + name: "Bool".to_string(), + args: vec![], + } + .into(), + ), + UnOp::Negate => Some( + Type::App { + public: true, + module: String::new(), + name: "Int".to_string(), + args: vec![], + } + .into(), + ), + }, } } } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 5f140886..6516bffd 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -1134,7 +1134,7 @@ pub fn monomorphize( } } // TODO check on assignment if type is needed - Air::Assignment { .. } => {} + Air::Let { .. } => {} Air::When { scope, tipo, @@ -1370,8 +1370,10 @@ pub fn monomorphize( scope, highest_index, indices, + tipo, } => { let mut new_indices = vec![]; + let mut tipo = tipo.clone(); for (ind, tipo) in indices { if tipo.is_generic() { let mut tipo = tipo.clone(); @@ -1382,10 +1384,14 @@ pub fn monomorphize( new_indices.push((ind, tipo)); } } + if tipo.is_generic() { + find_generics_to_replace(&mut tipo, &generic_types); + } new_air[index] = Air::RecordUpdate { scope, highest_index, indices: new_indices, + tipo, }; } Air::TupleAccessor { scope, names, tipo } => { @@ -1397,6 +1403,25 @@ pub fn monomorphize( needs_variant = true; } } + + Air::Call { scope, count, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::Call { scope, count, tipo }; + needs_variant = true; + } + } + Air::If { scope, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::If { scope, tipo }; + needs_variant = true; + } + } _ => {} } } @@ -1511,10 +1536,11 @@ pub fn handle_recursion_ir( let current_call = recursion_ir[index - 1].clone(); match current_call { - Air::Call { scope, count } => { + Air::Call { scope, count, tipo } => { recursion_ir[index - 1] = Air::Call { scope, count: count + 1, + tipo, } } _ => unreachable!(), diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 69df1453..25ed2ea1 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -242,6 +242,7 @@ impl<'a> CodeGenerator<'a> { ir_stack.push(Air::Call { scope: scope.clone(), count: args.len(), + tipo: tipo.clone(), }); let mut scope_fun = scope.clone(); scope_fun.push(self.id_gen.next()); @@ -357,7 +358,7 @@ impl<'a> CodeGenerator<'a> { ); if *clause_properties.needs_constr_var() { - ir_stack.push(Air::Lam { + ir_stack.push(Air::Let { scope: scope.clone(), name: constr_var.clone(), }); @@ -403,6 +404,7 @@ impl<'a> CodeGenerator<'a> { TypedExpr::If { branches, final_else, + tipo, .. } => { let mut if_ir = vec![]; @@ -414,10 +416,12 @@ impl<'a> CodeGenerator<'a> { if index == 0 { if_ir.push(Air::If { scope: scope.clone(), + tipo: tipo.clone(), }); } else { if_ir.push(Air::If { scope: branch_scope.clone(), + tipo: tipo.clone(), }); } self.build_ir(&branch.condition, &mut if_ir, branch_scope.clone()); @@ -506,7 +510,9 @@ impl<'a> CodeGenerator<'a> { tipo: tipo.clone(), }); } - TypedExpr::RecordUpdate { spread, args, .. } => { + TypedExpr::RecordUpdate { + spread, args, tipo, .. + } => { let mut update_ir = vec![]; let mut spread_scope = scope.clone(); let mut index_types = vec![]; @@ -531,6 +537,7 @@ impl<'a> CodeGenerator<'a> { scope, highest_index, indices: index_types, + tipo: tipo.clone(), }); ir_stack.append(&mut update_ir); @@ -771,10 +778,10 @@ impl<'a> CodeGenerator<'a> { } Pattern::String { .. } => todo!(), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); - pattern_vec.push(Air::Lam { + pattern_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); @@ -795,7 +802,7 @@ impl<'a> CodeGenerator<'a> { Pattern::VarUsage { .. } => todo!(), Pattern::Assign { name, pattern, .. } => { let mut new_vec = vec![]; - new_vec.push(Air::Lam { + new_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); @@ -824,7 +831,7 @@ impl<'a> CodeGenerator<'a> { ); } Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } Pattern::List { elements, tail, .. } => { @@ -837,7 +844,7 @@ impl<'a> CodeGenerator<'a> { } *clause_properties.needs_constr_var() = false; - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -965,7 +972,7 @@ impl<'a> CodeGenerator<'a> { Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } @@ -1198,7 +1205,7 @@ impl<'a> CodeGenerator<'a> { let pattern_type = &tipo.get_inner_types()[index]; - pattern_vec.push(Air::Lam { + pattern_vec.push(Air::Let { scope: scope.clone(), name: new_name.clone(), }); @@ -1254,7 +1261,7 @@ impl<'a> CodeGenerator<'a> { inverse: false, }); - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); } else { for (index, _) in elements.iter().enumerate() { let prev_tail_name = if index == 0 { @@ -1300,7 +1307,7 @@ impl<'a> CodeGenerator<'a> { inverse: true, }); - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -1445,7 +1452,7 @@ impl<'a> CodeGenerator<'a> { match pattern { Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: name.clone(), scope, }); @@ -1455,7 +1462,7 @@ impl<'a> CodeGenerator<'a> { Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: "_".to_string(), scope, }); @@ -1485,7 +1492,7 @@ impl<'a> CodeGenerator<'a> { } AssignmentKind::Assert => { let name_id = self.id_gen.next(); - pattern_vec.push(Air::Lam { + pattern_vec.push(Air::Let { scope: scope.clone(), name: format!("__constr_{}", name_id), }); @@ -1551,7 +1558,7 @@ impl<'a> CodeGenerator<'a> { Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } @@ -2562,6 +2569,7 @@ impl<'a> CodeGenerator<'a> { highest_index, indices, scope, + tipo, } => { let mut new_indices = vec![]; for (ind, tipo) in indices { @@ -2574,6 +2582,7 @@ impl<'a> CodeGenerator<'a> { scope, indices: new_indices, highest_index, + tipo, }; } Air::Record { @@ -2636,7 +2645,7 @@ impl<'a> CodeGenerator<'a> { let record_type = record.tipo(); if let Some(record_type) = record_type { if check_replaceable_opaque_type(&record_type, self.data_types) { - ir_stack[index] = Air::Lam { + ir_stack[index] = Air::Let { scope, name: indices[0].1.clone(), }; @@ -2669,6 +2678,25 @@ impl<'a> CodeGenerator<'a> { }; } } + Air::Call { scope, count, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::Call { + scope, + tipo: replaced_type, + count, + }; + } + Air::If { scope, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::If { + scope, + tipo: replaced_type, + }; + } _ => {} } } @@ -2868,7 +2896,7 @@ impl<'a> CodeGenerator<'a> { } }; } - Air::Discard { .. } => { + Air::Void { .. } => { arg_stack.push(Term::Constant(UplcConstant::Unit)); } Air::List { @@ -3461,23 +3489,6 @@ impl<'a> CodeGenerator<'a> { }; arg_stack.push(term); } - Air::Assignment { name, .. } => { - let right_hand = arg_stack.pop().unwrap(); - let lam_body = arg_stack.pop().unwrap(); - - let term = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: name, - unique: 0.into(), - }, - body: lam_body.into(), - }, - right_hand, - ); - - arg_stack.push(term); - } Air::Assert { constr_index, .. } => { let constr = arg_stack.pop().unwrap(); @@ -3586,7 +3597,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } } - Air::Lam { name, .. } => { + Air::Let { name, .. } => { let arg = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); From 38c24cca019c867864df74b02428831ab2b5ef5e Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 22 Jan 2023 05:46:27 -0500 Subject: [PATCH 12/19] single clause when not yet implemented --- crates/aiken-lang/src/uplc.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 25ed2ea1..5e3b9f6c 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -307,6 +307,10 @@ impl<'a> CodeGenerator<'a> { // assuming one subject at the moment let subject = subjects[0].clone(); + if clauses.len() <= 1 { + todo!("Single clause cases not implemented") + } + let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) { rearrange_clauses(clauses.clone()) } else { From d4eec1fe799cb314641c98d72d0b5f3a6eebb964 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 23 Jan 2023 14:13:03 -0500 Subject: [PATCH 13/19] chore: Add scaffolding for assert --- crates/aiken-lang/src/air.rs | 15 ++- crates/aiken-lang/src/builder.rs | 2 + crates/aiken-lang/src/tipo.rs | 15 +++ crates/aiken-lang/src/uplc.rs | 151 +++++++++++++++++++++++-------- 4 files changed, 142 insertions(+), 41 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 80ac641a..007fa6f3 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -98,7 +98,8 @@ pub enum Air { // Assignment Assert { scope: Vec, - constr_index: usize, + tipo: Arc, + value_is_data: bool, }, Let { @@ -106,6 +107,14 @@ pub enum Air { name: String, }, + ListAssert { + scope: Vec, + tipo: Arc, + names: Vec, + tail: bool, + value_is_data: bool, + }, + // When When { scope: Vec, @@ -260,6 +269,7 @@ impl Air { | Air::BinOp { scope, .. } | Air::UnOp { scope, .. } | Air::Assert { scope, .. } + | Air::ListAssert { scope, .. } | Air::Let { scope, .. } | Air::When { scope, .. } | Air::Clause { scope, .. } @@ -338,6 +348,8 @@ impl Air { | Air::Call { tipo, .. } | Air::Builtin { tipo, .. } | Air::BinOp { tipo, .. } + | Air::Assert { tipo, .. } + | Air::ListAssert { tipo, .. } | Air::When { tipo, .. } | Air::Clause { tipo, .. } | Air::ListClause { tipo, .. } @@ -358,7 +370,6 @@ impl Air { Air::DefineFunc { .. } | Air::Fn { .. } - | Air::Assert { .. } | Air::Let { .. } | Air::WrapClause { .. } | Air::Finally { .. } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 6516bffd..ce4556d0 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -1673,3 +1673,5 @@ pub fn replace_opaque_type(t: &mut Arc, data_types: IndexMap) {} diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index 1ddbcac5..541a4db4 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -173,6 +173,14 @@ impl Type { } } + pub fn is_data(&self) -> bool { + match self { + Self::App { module, name, .. } => "Data" == name && module.is_empty(), + Self::Var { tipo } => tipo.borrow().is_data(), + _ => false, + } + } + pub fn is_generic(&self) -> bool { match self { Type::App { args, .. } => { @@ -467,6 +475,13 @@ impl TypeVar { } } + pub fn is_data(&self) -> bool { + match self { + Self::Link { tipo } => tipo.is_data(), + _ => false, + } + } + pub fn is_generic(&self) -> bool { match self { TypeVar::Generic { .. } => true, diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 5e3b9f6c..b47c50b8 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -26,12 +26,12 @@ use crate::{ convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, get_generics_and_type, handle_func_dependencies_ir, handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, - replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, FuncComponents, - FunctionAccessKey, + recursive_assert, replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, + FuncComponents, FunctionAccessKey, }, expr::TypedExpr, tipo::{ - self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, + ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, @@ -76,11 +76,16 @@ impl<'a> CodeGenerator<'a> { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; + println!("{:#?}", body); + self.build_ir(&body, &mut ir_stack, scope); + println!("{:#?}", ir_stack); self.define_ir(&mut ir_stack); + println!("{:#?}", ir_stack); self.convert_opaque_type_to_inner_ir(&mut ir_stack); + println!("{:#?}", ir_stack); let mut term = self.uplc_code_gen(&mut ir_stack); @@ -103,6 +108,7 @@ impl<'a> CodeGenerator<'a> { version: (1, 0, 0), term, }; + println!("{}", program.to_pretty()); let mut interner = Interner::new(); @@ -285,6 +291,8 @@ impl<'a> CodeGenerator<'a> { let mut value_scope = scope.clone(); value_scope.push(self.id_gen.next()); + let value_is_data = value.tipo().is_data(); + self.build_ir(value, &mut value_vec, value_scope); self.assignment_ir( @@ -292,6 +300,7 @@ impl<'a> CodeGenerator<'a> { &mut pattern_vec, &mut value_vec, tipo, + value_is_data, *kind, scope, ); @@ -764,7 +773,7 @@ impl<'a> CodeGenerator<'a> { fn when_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, @@ -962,7 +971,7 @@ impl<'a> CodeGenerator<'a> { fn when_recursive_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, clause_properties: &mut ClauseProperties, @@ -1244,7 +1253,7 @@ impl<'a> CodeGenerator<'a> { fn nested_pattern_ir_and_label( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, pattern_type: &Arc, scope: Vec, @@ -1446,22 +1455,38 @@ impl<'a> CodeGenerator<'a> { fn assignment_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, + value_is_data: bool, kind: AssignmentKind, scope: Vec, ) { match pattern { Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Let { - name: name.clone(), - scope, - }); + let mut assert_vec = vec![]; + + if matches!(kind, AssignmentKind::Assert) { + if value_is_data && !tipo.is_data() { + recursive_assert(tipo, &mut assert_vec); + } + + pattern_vec.push(Air::Assert { + scope, + tipo: tipo.clone().into(), + value_is_data, + }); + } else { + pattern_vec.push(Air::Let { + name: name.clone(), + scope, + }); + } pattern_vec.append(value_vec); + pattern_vec.append(&mut assert_vec); } Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), @@ -1474,7 +1499,15 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(value_vec); } list @ Pattern::List { .. } => { - self.pattern_ir(list, pattern_vec, value_vec, tipo, scope); + self.pattern_ir( + list, + pattern_vec, + value_vec, + tipo, + value_is_data, + kind, + scope, + ); } Pattern::Constructor { name: constr_name, @@ -1492,7 +1525,15 @@ impl<'a> CodeGenerator<'a> { .unwrap(); match kind { AssignmentKind::Let => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); + self.pattern_ir( + pattern, + pattern_vec, + value_vec, + tipo, + value_is_data, + kind, + scope, + ); } AssignmentKind::Assert => { let name_id = self.id_gen.next(); @@ -1505,7 +1546,8 @@ impl<'a> CodeGenerator<'a> { pattern_vec.push(Air::Assert { scope: scope.clone(), - constr_index: index, + tipo: tipo.clone(), + value_is_data, }); pattern_vec.push(Air::Var { @@ -1535,6 +1577,8 @@ impl<'a> CodeGenerator<'a> { variant_name: String::new(), }], tipo, + value_is_data, + kind, scope, ); } @@ -1542,17 +1586,27 @@ impl<'a> CodeGenerator<'a> { } } Pattern::Tuple { .. } => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); + self.pattern_ir( + pattern, + pattern_vec, + value_vec, + tipo, + value_is_data, + kind, + scope, + ); } } } fn pattern_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, + value_is_data: bool, + kind: AssignmentKind, scope: Vec, ) { match pattern { @@ -1561,11 +1615,7 @@ impl<'a> CodeGenerator<'a> { Pattern::Var { .. } => todo!(), Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), - Pattern::Discard { .. } => { - pattern_vec.push(Air::Void { scope }); - - pattern_vec.append(values); - } + Pattern::Discard { .. } => todo!(), Pattern::List { elements, tail, .. } => { let mut elements_vec = vec![]; @@ -1601,6 +1651,8 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], + value_is_data, + kind, scope.clone(), ); } @@ -1616,12 +1668,15 @@ impl<'a> CodeGenerator<'a> { } } - pattern_vec.push(Air::ListAccessor { - names, - tail: tail.is_some(), - scope, - tipo: tipo.clone().into(), - }); + if matches!(kind, AssignmentKind::Assert) { + } else { + pattern_vec.push(Air::ListAccessor { + names, + tail: tail.is_some(), + scope, + tipo: tipo.clone().into(), + }); + } pattern_vec.append(values); pattern_vec.append(&mut elements_vec); @@ -1644,9 +1699,7 @@ impl<'a> CodeGenerator<'a> { let mut nested_pattern = vec![]; if *is_record { let field_map = match constructor { - tipo::PatternConstructor::Record { field_map, .. } => { - field_map.clone().unwrap() - } + PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), }; let mut type_map: IndexMap> = IndexMap::new(); @@ -1690,6 +1743,8 @@ impl<'a> CodeGenerator<'a> { variant_name: String::new(), }], tipo, + value_is_data, + kind, scope.clone(), ); @@ -1756,6 +1811,8 @@ impl<'a> CodeGenerator<'a> { variant_name: String::new(), }], tipo, + value_is_data, + kind, scope.clone(), ); @@ -1826,6 +1883,8 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], + value_is_data, + kind, scope.clone(), ); } @@ -3493,7 +3552,11 @@ impl<'a> CodeGenerator<'a> { }; arg_stack.push(term); } - Air::Assert { constr_index, .. } => { + Air::Assert { + tipo, + value_is_data, + .. + } => { let constr = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -3506,19 +3569,29 @@ impl<'a> CodeGenerator<'a> { Term::Delay(Term::Error.into()), ) .force_wrap(); + todo!(); - let condition = apply_wrap( - apply_wrap( - DefaultFunction::EqualsInteger.into(), - Term::Constant(UplcConstant::Integer(constr_index as i128)), - ), - constr_index_exposer(constr), - ); + // let condition = apply_wrap( + // apply_wrap( + // DefaultFunction::EqualsInteger.into(), + // Term::Constant(UplcConstant::Integer(constr_index as i128)), + // ), + // constr_index_exposer(constr), + // ); - term = delayed_if_else(condition, term, trace_error); + // term = delayed_if_else(condition, term, trace_error); arg_stack.push(term); } + Air::ListAssert { + scope, + tipo, + names, + tail, + value_is_data, + } => { + todo!(); + } Air::DefineFunc { func_name, params, From fc880280344e65658e805040666b48e3166081d0 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Thu, 26 Jan 2023 05:20:26 -0500 Subject: [PATCH 14/19] some refactoring and adding assert on lists --- crates/aiken-lang/src/air.rs | 7 + crates/aiken-lang/src/builder.rs | 21 +- crates/aiken-lang/src/uplc.rs | 498 ++++++++++++++++++++++--------- crates/uplc/src/ast/builder.rs | 103 +++++++ 4 files changed, 489 insertions(+), 140 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 007fa6f3..3d0b7e81 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -107,6 +107,11 @@ pub enum Air { name: String, }, + UnWrapData { + scope: Vec, + tipo: Arc, + }, + ListAssert { scope: Vec, tipo: Arc, @@ -271,6 +276,7 @@ impl Air { | Air::Assert { scope, .. } | Air::ListAssert { scope, .. } | Air::Let { scope, .. } + | Air::UnWrapData { scope, .. } | Air::When { scope, .. } | Air::Clause { scope, .. } | Air::ListClause { scope, .. } @@ -350,6 +356,7 @@ impl Air { | Air::BinOp { tipo, .. } | Air::Assert { tipo, .. } | Air::ListAssert { tipo, .. } + | Air::UnWrapData { tipo, .. } | Air::When { tipo, .. } | Air::Clause { tipo, .. } | Air::ListClause { tipo, .. } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index ce4556d0..64ee41e0 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -14,7 +14,7 @@ use uplc::{ use crate::{ air::Air, - ast::{Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType}, + ast::{AssignmentKind, Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType}, expr::TypedExpr, tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant}, }; @@ -48,6 +48,12 @@ pub struct FunctionAccessKey { pub variant_name: String, } +#[derive(Clone, Debug)] +pub struct AssignmentProperties { + pub value_is_data: bool, + pub kind: AssignmentKind, +} + #[derive(Clone, Debug)] pub enum ClauseProperties { ConstrClause { @@ -1133,8 +1139,15 @@ pub fn monomorphize( needs_variant = true; } } - // TODO check on assignment if type is needed - Air::Let { .. } => {} + Air::UnWrapData { scope, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::UnWrapData { scope, tipo }; + needs_variant = true; + } + } Air::When { scope, tipo, @@ -1673,5 +1686,3 @@ pub fn replace_opaque_type(t: &mut Arc, data_types: IndexMap) {} diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index b47c50b8..64e62fda 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -6,7 +6,8 @@ use uplc::{ ast::{ builder::{ self, apply_wrap, choose_list, constr_index_exposer, delayed_choose_list, - delayed_if_else, if_else, repeat_tail_list, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, + delayed_if_else, if_else, repeat_tail_list, ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, + CONSTR_GET_FIELD, }, Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType, }, @@ -26,12 +27,12 @@ use crate::{ convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, get_generics_and_type, handle_func_dependencies_ir, handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, - recursive_assert, replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, - FuncComponents, FunctionAccessKey, + replace_opaque_type, wrap_validator_args, AssignmentProperties, ClauseProperties, + DataTypeKey, FuncComponents, FunctionAccessKey, }, expr::TypedExpr, tipo::{ - ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, + self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, @@ -45,6 +46,7 @@ pub struct CodeGenerator<'a> { module_types: &'a IndexMap, id_gen: IdGenerator, needs_field_access: bool, + used_data_assert_on_list: bool, zero_arg_functions: IndexMap>, } @@ -63,6 +65,7 @@ impl<'a> CodeGenerator<'a> { module_types, id_gen: IdGenerator::new(), needs_field_access: false, + used_data_assert_on_list: false, zero_arg_functions: IndexMap::new(), } } @@ -104,6 +107,12 @@ impl<'a> CodeGenerator<'a> { term = wrap_validator_args(term, arguments); + term = if wrap_as_validator || self.used_data_assert_on_list { + builder::assert_on_list(term) + } else { + term + }; + let mut program = Program { version: (1, 0, 0), term, @@ -300,8 +309,10 @@ impl<'a> CodeGenerator<'a> { &mut pattern_vec, &mut value_vec, tipo, - value_is_data, - *kind, + AssignmentProperties { + value_is_data, + kind: *kind, + }, scope, ); @@ -1459,142 +1470,141 @@ impl<'a> CodeGenerator<'a> { pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, - value_is_data: bool, - kind: AssignmentKind, + assignment_properties: AssignmentProperties, scope: Vec, ) { + if assignment_properties.value_is_data && !tipo.is_data() { + value_vec.insert( + 0, + Air::UnWrapData { + scope: scope.clone(), + tipo: tipo.clone().into(), + }, + ); + } match pattern { Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { - let mut assert_vec = vec![]; - - if matches!(kind, AssignmentKind::Assert) { - if value_is_data && !tipo.is_data() { - recursive_assert(tipo, &mut assert_vec); - } - - pattern_vec.push(Air::Assert { - scope, - tipo: tipo.clone().into(), - value_is_data, - }); - } else { - pattern_vec.push(Air::Let { - name: name.clone(), - scope, - }); - } + pattern_vec.push(Air::Let { + name: name.clone(), + scope: scope.clone(), + }); pattern_vec.append(value_vec); - pattern_vec.append(&mut assert_vec); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + let mut assert_vec = vec![]; + self.recursive_assert_pattern( + pattern, + &mut assert_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + pattern_vec.append(&mut assert_vec); + } } Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => { pattern_vec.push(Air::Let { name: "_".to_string(), - scope, + scope: scope.clone(), }); pattern_vec.append(value_vec); - } - list @ Pattern::List { .. } => { - self.pattern_ir( - list, - pattern_vec, - value_vec, - tipo, - value_is_data, - kind, - scope, - ); - } - Pattern::Constructor { - name: constr_name, - tipo, - .. - } => { - let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo); - let (index, _) = data_type - .unwrap() - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == constr_name) - .unwrap(); - match kind { - AssignmentKind::Let => { - self.pattern_ir( - pattern, - pattern_vec, - value_vec, - tipo, - value_is_data, - kind, - scope, - ); - } - AssignmentKind::Assert => { - let name_id = self.id_gen.next(); - pattern_vec.push(Air::Let { - scope: scope.clone(), - name: format!("__constr_{}", name_id), - }); - - pattern_vec.append(value_vec); - - pattern_vec.push(Air::Assert { - scope: scope.clone(), - tipo: tipo.clone(), - value_is_data, - }); - - pattern_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__constr_{}", name_id), - variant_name: String::new(), - }); - - self.pattern_ir( - pattern, - pattern_vec, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__constr_{}", name_id), - variant_name: String::new(), - }], - tipo, - value_is_data, - kind, - scope, - ); - } - AssignmentKind::Check => todo!(), + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + let mut assert_vec = vec![]; + self.recursive_assert_pattern( + pattern, + &mut assert_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + pattern_vec.append(&mut assert_vec); } } - Pattern::Tuple { .. } => { - self.pattern_ir( - pattern, - pattern_vec, - value_vec, - tipo, - value_is_data, - kind, - scope, - ); + list @ Pattern::List { .. } => { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + list, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + list, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } + } + constr @ Pattern::Constructor { .. } => { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + constr, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + constr, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } + } + tuple @ Pattern::Tuple { .. } => { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + tuple, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + tuple, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } } } } @@ -1605,8 +1615,7 @@ impl<'a> CodeGenerator<'a> { pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, - value_is_data: bool, - kind: AssignmentKind, + assignment_properties: AssignmentProperties, scope: Vec, ) { match pattern { @@ -1618,8 +1627,8 @@ impl<'a> CodeGenerator<'a> { Pattern::Discard { .. } => todo!(), Pattern::List { elements, tail, .. } => { let mut elements_vec = vec![]; - let mut names = vec![]; + for element in elements { match element { Pattern::Var { name, .. } => { @@ -1651,8 +1660,7 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], - value_is_data, - kind, + assignment_properties.clone(), scope.clone(), ); } @@ -1668,7 +1676,7 @@ impl<'a> CodeGenerator<'a> { } } - if matches!(kind, AssignmentKind::Assert) { + if matches!(&assignment_properties.kind, AssignmentKind::Assert) { } else { pattern_vec.push(Air::ListAccessor { names, @@ -1743,8 +1751,7 @@ impl<'a> CodeGenerator<'a> { variant_name: String::new(), }], tipo, - value_is_data, - kind, + assignment_properties.clone(), scope.clone(), ); @@ -1811,8 +1818,7 @@ impl<'a> CodeGenerator<'a> { variant_name: String::new(), }], tipo, - value_is_data, - kind, + assignment_properties.clone(), scope.clone(), ); @@ -1883,8 +1889,7 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], - value_is_data, - kind, + assignment_properties.clone(), scope.clone(), ); } @@ -1903,6 +1908,219 @@ impl<'a> CodeGenerator<'a> { } } + pub fn recursive_assert_pattern( + &mut self, + pattern: &Pattern>, + pattern_vec: &mut Vec, + value_vec: &mut Vec, + tipo: &Type, + assignment_properties: AssignmentProperties, + scope: Vec, + ) { + match pattern { + Pattern::Int { .. } => unreachable!(), + Pattern::String { .. } => unreachable!(), + Pattern::Var { name, .. } => { + self.recursive_assert_tipo(tipo, pattern_vec, name, scope); + } + Pattern::VarUsage { + location, + name, + tipo, + } => todo!(), + Pattern::Assign { + name, + location, + pattern, + } => todo!(), + Pattern::Discard { name, location } => todo!(), + Pattern::List { + location, + elements, + tail, + } => todo!(), + Pattern::Constructor { + is_record, + location, + name, + arguments, + module, + constructor, + with_spread, + tipo, + } => todo!(), + Pattern::Tuple { location, elems } => todo!(), + } + } + + fn recursive_assert_tipo( + &mut self, + tipo: &Type, + assert_vec: &mut Vec, + name: &str, + scope: Vec, + ) { + if tipo.is_bool() + || tipo.is_bytearray() + || tipo.is_int() + || tipo.is_string() + || tipo.is_void() + { + } else if tipo.is_map() { + self.used_data_assert_on_list = true; + let new_id = self.id_gen.next(); + let id_pair = (self.id_gen.next(), self.id_gen.next()); + let inner_list_type = &tipo.get_inner_types()[0]; + let inner_pair_types = inner_list_type.get_inner_types(); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 3, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: ASSERT_ON_LIST.to_string(), + variant_name: String::new(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + assert_vec.push(Air::Fn { + scope: scope.clone(), + params: vec![format!("__pair_{}", new_id)], + }); + + assert_vec.push(Air::TupleAccessor { + scope: scope.clone(), + names: vec![ + format!("__pair_fst_{}", id_pair.0), + format!("__pair_snd_{}", id_pair.1), + ], + tipo: inner_list_type.clone(), + }); + + self.recursive_assert_tipo( + &inner_pair_types[0], + assert_vec, + &format!("__pair_fst_{}", id_pair.0), + scope.clone(), + ); + + self.recursive_assert_tipo( + &inner_pair_types[1], + assert_vec, + &format!("__pair_snd_{}", id_pair.1), + scope, + ); + } else if tipo.is_list() { + self.used_data_assert_on_list = true; + let new_id = self.id_gen.next(); + let id_pair = (self.id_gen.next(), self.id_gen.next()); + let inner_list_type = &tipo.get_inner_types()[0]; + let inner_pair_types = inner_list_type.get_inner_types(); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 3, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: ASSERT_ON_LIST.to_string(), + variant_name: String::new(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + assert_vec.push(Air::Fn { + scope: scope.clone(), + params: vec![format!("__list_item_{}", new_id)], + }); + + assert_vec.push(Air::TupleAccessor { + scope: scope.clone(), + names: vec![ + format!("__pair_fst_{}", id_pair.0), + format!("__pair_snd_{}", id_pair.1), + ], + tipo: inner_list_type.clone(), + }); + + self.recursive_assert_tipo( + &inner_pair_types[0], + assert_vec, + &format!("__pair_fst_{}", id_pair.0), + scope.clone(), + ); + + self.recursive_assert_tipo( + &inner_pair_types[1], + assert_vec, + &format!("__pair_snd_{}", id_pair.1), + scope, + ) + } + } + fn define_ir(&mut self, ir_stack: &mut Vec) { let mut func_components = IndexMap::new(); let mut func_index_map = IndexMap::new(); @@ -2760,6 +2978,15 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, }; } + Air::UnWrapData { scope, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::UnWrapData { + scope, + tipo: replaced_type, + }; + } _ => {} } } @@ -3692,6 +3919,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + Air::UnWrapData { scope, tipo } => todo!(), Air::When { subject_name, tipo, .. } => { diff --git a/crates/uplc/src/ast/builder.rs b/crates/uplc/src/ast/builder.rs index 251db3ca..1b11d16f 100644 --- a/crates/uplc/src/ast/builder.rs +++ b/crates/uplc/src/ast/builder.rs @@ -5,6 +5,7 @@ use super::{Constant, Name, Term}; pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer"; pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; +pub const ASSERT_ON_LIST: &str = "__assert_on_list"; pub fn apply_wrap(function: Term, arg: Term) -> Term { Term::Apply { @@ -31,6 +32,108 @@ pub fn final_wrapper(term: Term) -> Term { ) } +pub fn assert_on_list(term: Term) -> Term { + apply_wrap( + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: term.into(), + }, + apply_wrap( + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + ), + ) + .into(), + }, + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: Term::Lambda { + parameter_name: Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }, + body: Term::Lambda { + parameter_name: Name { + text: "check_with".to_string(), + unique: 0.into(), + }, + body: delayed_choose_list( + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + Term::Constant(Constant::Unit), + apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(), + apply_wrap( + Term::Var(Name { + text: "check_with".to_string(), + unique: 0.into(), + }), + apply_wrap( + Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + ), + ), + ), + apply_wrap( + apply_wrap( + apply_wrap( + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + ), + ), + Term::Var(Name { + text: "check_with".to_string(), + unique: 0.into(), + }), + ), + ), + ) + .into(), + } + .into(), + } + .into(), + }, + ) +} + pub fn constr_fields_exposer(term: Term) -> Term { Term::Apply { function: Term::Lambda { From 60736b4a54de185f990d64963a418b61a7c4586f Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 28 Jan 2023 02:40:53 -0500 Subject: [PATCH 15/19] chore: Add error when type casting without annotation Also implement and test most of assert. Finishing last few cases. --- crates/aiken-lang/src/air.rs | 4 +- crates/aiken-lang/src/builder.rs | 86 +-- crates/aiken-lang/src/tipo/error.rs | 9 + crates/aiken-lang/src/tipo/expr.rs | 26 +- crates/aiken-lang/src/uplc.rs | 811 ++++++++++++++++++++-------- 5 files changed, 681 insertions(+), 255 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 3d0b7e81..ef60e3d9 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -204,8 +204,8 @@ pub enum Air { FieldsExpose { scope: Vec, - count: usize, indices: Vec<(usize, String, Arc)>, + check_last_item: bool, }, // ListAccess @@ -214,6 +214,7 @@ pub enum Air { tipo: Arc, names: Vec, tail: bool, + check_last_item: bool, }, ListExpose { @@ -228,6 +229,7 @@ pub enum Air { scope: Vec, names: Vec, tipo: Arc, + check_last_item: bool, }, TupleIndex { diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 64ee41e0..9df15630 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ - builder::{apply_wrap, if_else}, + builder::{apply_wrap, choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -482,6 +482,7 @@ pub fn list_access_to_uplc( current_index: usize, term: Term, tipo: &Type, + check_last_item: bool, ) -> Term { let (first, names) = names.split_first().unwrap(); @@ -546,30 +547,6 @@ pub fn list_access_to_uplc( } else if names.is_empty() { // Maybe check list is actually empty or should we leave that to when .. is only // this would replace term.into() if we decide to - // body: choose_list( - // apply_wrap( - // Term::Builtin(DefaultFunction::TailList).force_wrap(), - // Term::Var(Name { - // text: format!( - // "tail_index_{}_{}", - // current_index, id_list[current_index] - // ), - // unique: 0.into(), - // }), - // ), - // term, - // apply_wrap( - // apply_wrap( - // Term::Builtin(DefaultFunction::Trace).force_wrap(), - // Term::Constant(UplcConstant::String( - // "List contains more items".to_string(), - // )), - // ), - // Term::Delay(Term::Error.into()), - // ) - // .force_wrap(), - // ) - // .into(), Term::Lambda { parameter_name: Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), @@ -581,7 +558,34 @@ pub fn list_access_to_uplc( text: first.clone(), unique: 0.into(), }, - body: term.into(), + body: if check_last_item { + choose_list( + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + term, + apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Constant(UplcConstant::String( + "List/Tuple contains more items than it should".to_string(), + )), + ), + Term::Delay(Term::Error.into()), + ) + .force_wrap(), + ) + .into() + } else { + term.into() + }, }, apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), @@ -606,7 +610,15 @@ pub fn list_access_to_uplc( unique: 0.into(), }, body: apply_wrap( - list_access_to_uplc(names, id_list, tail, current_index + 1, term, tipo), + list_access_to_uplc( + names, + id_list, + tail, + current_index + 1, + term, + tipo, + check_last_item, + ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var(Name { @@ -1078,6 +1090,7 @@ pub fn monomorphize( tipo, names, tail, + check_last_item, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); @@ -1088,6 +1101,7 @@ pub fn monomorphize( names, tipo, tail, + check_last_item, }; needs_variant = true; } @@ -1359,8 +1373,8 @@ pub fn monomorphize( } Air::FieldsExpose { scope, - count, indices, + check_last_item, } => { let mut new_indices = vec![]; for (ind, name, tipo) in indices { @@ -1375,8 +1389,8 @@ pub fn monomorphize( } new_air[index] = Air::FieldsExpose { scope, - count, indices: new_indices, + check_last_item, }; } Air::RecordUpdate { @@ -1407,12 +1421,22 @@ pub fn monomorphize( tipo, }; } - Air::TupleAccessor { scope, names, tipo } => { + Air::TupleAccessor { + scope, + names, + tipo, + check_last_item, + } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); - new_air[index] = Air::TupleAccessor { scope, names, tipo }; + new_air[index] = Air::TupleAccessor { + scope, + names, + tipo, + check_last_item, + }; needs_variant = true; } } diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 6bd385ee..7166b7e0 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -724,6 +724,15 @@ The best thing to do from here is to remove it."#))] location: Span, name: String, }, + + #[error("I discovered a type cast from Data without an annotation")] + #[diagnostic(code("illegal::type_cast"))] + #[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))] + CastDataNoAnn { + #[label] + location: Span, + value: UntypedExpr, + }, } impl Error { diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index caf237a0..49241522 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -807,8 +807,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { annotation: &Option, location: Span, ) -> Result { - let value = self.in_new_scope(|value_typer| value_typer.infer(value))?; - let mut value_typ = value.tipo(); + let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?; + let mut value_typ = typed_value.tipo(); // Check that any type annotation is accurate. let pattern = if let Some(ann) = annotation { @@ -819,7 +819,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { self.unify( ann_typ.clone(), value_typ.clone(), - value.type_defining_location(), + typed_value.type_defining_location(), )?; value_typ = ann_typ.clone(); @@ -831,6 +831,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> { Some(ann_typ), )? } else { + if value_typ.is_data() { + return Err(Error::CastDataNoAnn { + location, + value: UntypedExpr::Assignment { + location, + value: value.into(), + pattern, + kind, + annotation: Some(Annotation::Constructor { + location: Span::empty(), + module: None, + name: "Type".to_string(), + arguments: vec![], + }), + }, + }); + } + // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( pattern, @@ -860,7 +878,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { tipo: value_typ, kind, pattern, - value: Box::new(value), + value: Box::new(typed_value), }) } diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 64e62fda..4042e7de 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -5,9 +5,9 @@ use itertools::Itertools; use uplc::{ ast::{ builder::{ - self, apply_wrap, choose_list, constr_index_exposer, delayed_choose_list, - delayed_if_else, if_else, repeat_tail_list, ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, - CONSTR_GET_FIELD, + self, apply_wrap, assert_on_list, choose_list, constr_index_exposer, + delayed_choose_list, delayed_if_else, final_wrapper, if_else, repeat_tail_list, + ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, }, Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType, }, @@ -19,7 +19,7 @@ use uplc::{ use crate::{ air::Air, ast::{ - ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType, + ArgName, AssignmentKind, BinOp, CallArg, Clause, Pattern, Span, TypedArg, TypedDataType, TypedFunction, UnOp, }, builder::{ @@ -32,8 +32,8 @@ use crate::{ }, expr::TypedExpr, tipo::{ - self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, - ValueConstructorVariant, + fields::FieldMap, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, TypeVar, + ValueConstructor, ValueConstructorVariant, }, IdGenerator, }; @@ -79,8 +79,6 @@ impl<'a> CodeGenerator<'a> { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; - println!("{:#?}", body); - self.build_ir(&body, &mut ir_stack, scope); println!("{:#?}", ir_stack); @@ -100,7 +98,7 @@ impl<'a> CodeGenerator<'a> { // Wrap the validator body if ifThenElse term unit error term = if wrap_as_validator { - builder::final_wrapper(term) + final_wrapper(term) } else { term }; @@ -108,7 +106,7 @@ impl<'a> CodeGenerator<'a> { term = wrap_validator_args(term, arguments); term = if wrap_as_validator || self.used_data_assert_on_list { - builder::assert_on_list(term) + assert_on_list(term) } else { term }; @@ -1131,7 +1129,6 @@ impl<'a> CodeGenerator<'a> { if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, indices: arguments_index .iter() .map(|(label, var_name, index)| { @@ -1140,6 +1137,7 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } else { @@ -1168,7 +1166,6 @@ impl<'a> CodeGenerator<'a> { if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, indices: arguments_index .iter() .map(|(name, index)| { @@ -1178,6 +1175,7 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } @@ -1473,7 +1471,7 @@ impl<'a> CodeGenerator<'a> { assignment_properties: AssignmentProperties, scope: Vec, ) { - if assignment_properties.value_is_data && !tipo.is_data() { + if assignment_properties.value_is_data && !tipo.is_data() && !pattern.is_discard() { value_vec.insert( 0, Air::UnWrapData { @@ -1513,26 +1511,10 @@ impl<'a> CodeGenerator<'a> { Pattern::Discard { .. } => { pattern_vec.push(Air::Let { name: "_".to_string(), - scope: scope.clone(), + scope, }); pattern_vec.append(value_vec); - - if matches!(assignment_properties.kind, AssignmentKind::Assert) - && assignment_properties.value_is_data - && !tipo.is_data() - { - let mut assert_vec = vec![]; - self.recursive_assert_pattern( - pattern, - &mut assert_vec, - value_vec, - tipo, - assignment_properties, - scope, - ); - pattern_vec.append(&mut assert_vec); - } } list @ Pattern::List { .. } => { if matches!(assignment_properties.kind, AssignmentKind::Assert) @@ -1559,10 +1541,13 @@ impl<'a> CodeGenerator<'a> { } } constr @ Pattern::Constructor { .. } => { + println!("ASSIGNMENT PROPERTIES {:#?}", assignment_properties); + println!("CURRENT TYPE IS {:#?}", tipo); if matches!(assignment_properties.kind, AssignmentKind::Assert) && assignment_properties.value_is_data && !tipo.is_data() { + println!("GOT HERE FOR SURE"); self.recursive_assert_pattern( constr, pattern_vec, @@ -1683,6 +1668,7 @@ impl<'a> CodeGenerator<'a> { tail: tail.is_some(), scope, tipo: tipo.clone().into(), + check_last_item: false, }); } @@ -1690,162 +1676,53 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(&mut elements_vec); } Pattern::Constructor { - is_record, - name: constr_name, arguments, constructor, tipo, .. } => { - let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); - let (_, constructor_type) = data_type - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == constr_name) - .unwrap(); let mut nested_pattern = vec![]; - if *is_record { - let field_map = match constructor { - PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), - }; - let mut type_map: IndexMap> = IndexMap::new(); + let field_map = match constructor { + PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), + }; - for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { - let label = constructor_type.arguments[index].label.clone().unwrap(); - let field_type = arg.clone(); + let mut type_map: IndexMap> = IndexMap::new(); - type_map.insert(label, field_type); - } + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } - let arguments_index = arguments - .iter() - .map(|item| { - let label = item.label.clone().unwrap_or_default(); - let field_index = - field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); - let (discard, var_name) = match &item.value { - Pattern::Var { name, .. } => (false, name.clone()), - Pattern::Discard { .. } => (true, "".to_string()), - Pattern::List { .. } => todo!(), - a @ Pattern::Constructor { - tipo, - name: constr_name, - .. - } => { - let id = self.id_gen.next(); - let constr_name = format!("{constr_name}_{id}"); - self.pattern_ir( - a, - &mut nested_pattern, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_name.clone(), - variant_name: String::new(), - }], - tipo, - assignment_properties.clone(), - scope.clone(), - ); + let arguments_index = arguments + .iter() + .filter_map(|item| { + let label = item.label.clone().unwrap_or_default(); + let field_index = field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); + self.extract_arg_and_index( + &item.value, + *field_index, + &mut nested_pattern, + &assignment_properties, + &scope, + false, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); - (false, constr_name) - } - _ => todo!(), - }; - - (label, var_name, *field_index, discard) - }) - .filter(|(_, _, _, discard)| !discard) - .sorted_by(|item1, item2| item1.2.cmp(&item2.2)) - .collect::>(); - - if !arguments_index.is_empty() { - pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, - indices: arguments_index - .iter() - .map(|(label, var_name, index, _)| { - let field_type = type_map.get(label).unwrap(); - (*index, var_name.clone(), field_type.clone()) - }) - .collect_vec(), - scope, - }); - } - } else { - let mut type_map: IndexMap> = IndexMap::new(); - - for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { - let field_type = arg.clone(); - - type_map.insert(index, field_type); - } - - let arguments_index = arguments - .iter() - .enumerate() - .map(|(index, item)| { - let (discard, var_name) = match &item.value { - Pattern::Var { name, .. } => (false, name.clone()), - Pattern::Discard { .. } => (true, "".to_string()), - Pattern::List { .. } => todo!(), - a @ Pattern::Constructor { - tipo, - name: constr_name, - .. - } => { - let id = self.id_gen.next(); - let constr_name = format!("{constr_name}_{id}"); - self.pattern_ir( - a, - &mut nested_pattern, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_name.clone(), - variant_name: String::new(), - }], - tipo, - assignment_properties.clone(), - scope.clone(), - ); - - (false, constr_name) - } - _ => todo!(), - }; - - (var_name, index, discard) - }) - .filter(|(_, _, discard)| !discard) - .collect::>(); - - if !arguments_index.is_empty() { - pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, - indices: arguments_index - .iter() - .map(|(name, index, _)| { - let field_type = type_map.get(index).unwrap(); - - (*index, name.clone(), field_type.clone()) - }) - .collect_vec(), - scope, - }); - } + if !arguments_index.is_empty() { + pattern_vec.push(Air::FieldsExpose { + indices: arguments_index + .iter() + .map(|(var_name, index)| { + let field_type = type_map.get(index).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) + .collect_vec(), + scope, + check_last_item: false, + }); } pattern_vec.append(values); @@ -1900,6 +1777,7 @@ impl<'a> CodeGenerator<'a> { names, scope, tipo: tipo.clone().into(), + check_last_item: false, }); pattern_vec.append(values); @@ -1921,35 +1799,224 @@ impl<'a> CodeGenerator<'a> { Pattern::Int { .. } => unreachable!(), Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { + pattern_vec.push(Air::Let { + scope: scope.clone(), + name: name.clone(), + }); + + pattern_vec.append(value_vec); + self.recursive_assert_tipo(tipo, pattern_vec, name, scope); } - Pattern::VarUsage { - location, - name, - tipo, - } => todo!(), - Pattern::Assign { - name, - location, - pattern, - } => todo!(), - Pattern::Discard { name, location } => todo!(), - Pattern::List { - location, - elements, - tail, - } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + Pattern::Discard { .. } => unreachable!(), + Pattern::List { elements, tail, .. } => { + let mut assert_list_vec = vec![]; + let inner_list_type = &tipo.get_inner_types()[0]; + let mut names = vec![]; + for element in elements { + match element { + Pattern::Var { name, .. } => { + names.push(name.clone()); + } + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + l @ Pattern::List { .. } => { + let name = format!("list_item_id_{}", self.id_gen.next()); + names.push(name.clone()); + + self.recursive_assert_pattern( + l, + &mut assert_list_vec, + &mut vec![Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name, + variant_name: String::new(), + }], + inner_list_type, + assignment_properties.clone(), + scope.clone(), + ); + } + Pattern::Constructor { .. } => todo!(), + Pattern::Tuple { elems, .. } => todo!(), + _ => {} + } + } + + let name = if let Some(tail) = tail { + match &**tail { + Pattern::Var { name, .. } => name.clone(), + _ => format!("__tail_{}", self.id_gen.next()), + } + } else { + format!("__tail_{}", self.id_gen.next()) + }; + + self.recursive_assert_tipo(tipo, &mut assert_list_vec, &name, scope.clone()); + + pattern_vec.push(Air::ListAccessor { + scope, + tipo: tipo.clone().into(), + names, + tail: true, + check_last_item: false, + }); + + pattern_vec.append(value_vec); + + pattern_vec.append(&mut assert_list_vec); + } Pattern::Constructor { - is_record, - location, - name, arguments, - module, constructor, - with_spread, + name: constr_name, tipo, - } => todo!(), - Pattern::Tuple { location, elems } => todo!(), + .. + } => { + let mut nested_pattern = vec![]; + + let field_map = match constructor { + PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), + }; + + let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); + + let data_type_constr = data_type + .constructors + .iter() + .find(|constr| &constr.name == constr_name) + .unwrap(); + + let mut type_map: IndexMap> = IndexMap::new(); + + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + + println!("TYPE IS {:#?}", type_map); + + let arguments_index = arguments + .iter() + .filter_map(|item| { + let label = item.label.clone().unwrap_or_default(); + let field_index = field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); + self.extract_arg_and_index( + &item.value, + *field_index, + &mut nested_pattern, + &assignment_properties, + &scope, + true, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + let total_fields = data_type_constr.arguments.len(); + let mut final_args = vec![]; + let mut current_index = 0; + for index in 0..total_fields { + if arguments_index.get(current_index).is_some() + && arguments_index[current_index].1 == index + { + final_args.push(arguments_index.get(index).unwrap().clone()); + current_index += 1; + } else { + final_args.push((format!("__field_{index}_{}", self.id_gen.next()), index)); + self.recursive_assert_tipo( + type_map.get(&index).unwrap(), + &mut nested_pattern, + &format!("__field_{index}_{}", self.id_gen.next()), + scope.clone(), + ) + } + } + + if !arguments_index.is_empty() { + pattern_vec.push(Air::FieldsExpose { + indices: final_args + .iter() + .map(|(var_name, index)| { + let field_type = type_map.get(index).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) + .collect_vec(), + scope, + check_last_item: true, + }); + } + + pattern_vec.append(value_vec); + + pattern_vec.append(&mut nested_pattern); + } + Pattern::Tuple { elems, .. } => { + let mut type_map: IndexMap> = IndexMap::new(); + + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + let mut nested_pattern = vec![]; + let arguments_index = elems + .iter() + .enumerate() + .filter_map(|(index, item)| { + let field_index = index; + self.extract_arg_and_index( + item, + field_index, + &mut nested_pattern, + &assignment_properties, + &scope, + true, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + let total_fields = type_map.len(); + let mut final_args = vec![]; + let mut current_index = 0; + for index in 0..total_fields { + if arguments_index.get(current_index).is_some() + && arguments_index[current_index].1 == index + { + final_args.push(arguments_index.get(index).unwrap().clone()); + current_index += 1; + } else { + final_args.push((format!("__field_{index}_{}", self.id_gen.next()), index)); + self.recursive_assert_tipo( + type_map.get(&index).unwrap(), + &mut nested_pattern, + &format!("__field_{index}_{}", self.id_gen.next()), + scope.clone(), + ) + } + } + + if !arguments_index.is_empty() { + pattern_vec.push(Air::TupleAccessor { + scope: scope.clone(), + names: final_args.into_iter().map(|(item, _)| item).collect_vec(), + tipo: tipo.clone().into(), + check_last_item: true, + }); + } + + pattern_vec.append(value_vec); + + pattern_vec.append(&mut nested_pattern); + } } } @@ -1965,6 +2032,7 @@ impl<'a> CodeGenerator<'a> { || tipo.is_int() || tipo.is_string() || tipo.is_void() + || tipo.get_generic().is_some() { } else if tipo.is_map() { self.used_data_assert_on_list = true; @@ -2027,6 +2095,19 @@ impl<'a> CodeGenerator<'a> { format!("__pair_snd_{}", id_pair.1), ], tipo: inner_list_type.clone(), + check_last_item: false, + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: format!("__pair_{}", new_id), + variant_name: String::new(), }); self.recursive_assert_tipo( @@ -2040,14 +2121,14 @@ impl<'a> CodeGenerator<'a> { &inner_pair_types[1], assert_vec, &format!("__pair_snd_{}", id_pair.1), - scope, + scope.clone(), ); + + assert_vec.push(Air::Void { scope }); } else if tipo.is_list() { self.used_data_assert_on_list = true; let new_id = self.id_gen.next(); - let id_pair = (self.id_gen.next(), self.id_gen.next()); let inner_list_type = &tipo.get_inner_types()[0]; - let inner_pair_types = inner_list_type.get_inner_types(); assert_vec.push(Air::Call { scope: scope.clone(), @@ -2096,28 +2177,243 @@ impl<'a> CodeGenerator<'a> { params: vec![format!("__list_item_{}", new_id)], }); - assert_vec.push(Air::TupleAccessor { + assert_vec.push(Air::Let { + scope: scope.clone(), + name: format!("__list_item_{}", new_id), + }); + + assert_vec.push(Air::UnWrapData { scope: scope.clone(), - names: vec![ - format!("__pair_fst_{}", id_pair.0), - format!("__pair_snd_{}", id_pair.1), - ], tipo: inner_list_type.clone(), }); + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: format!("__list_item_{}", new_id), + variant_name: String::new(), + }); + self.recursive_assert_tipo( - &inner_pair_types[0], + inner_list_type, assert_vec, - &format!("__pair_fst_{}", id_pair.0), + &format!("__list_item_{}", new_id), scope.clone(), ); - self.recursive_assert_tipo( - &inner_pair_types[1], - assert_vec, - &format!("__pair_snd_{}", id_pair.1), + assert_vec.push(Air::Void { scope }); + } else if tipo.is_tuple() { + let tuple_inner_types = tipo.get_inner_types(); + let mut new_id_list = vec![]; + for (index, _) in tuple_inner_types.iter().enumerate() { + new_id_list.push((index, self.id_gen.next())); + } + + assert_vec.push(Air::TupleAccessor { + scope: scope.clone(), + names: new_id_list + .iter() + .map(|(index, id)| format!("__tuple_index_{}_{}", index, id)) + .collect_vec(), + tipo: tipo.clone().into(), + check_last_item: true, + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + for (index, name) in new_id_list + .into_iter() + .map(|(index, id)| (index, format!("__tuple_index_{}_{}", index, id))) + { + self.recursive_assert_tipo( + &tuple_inner_types[index], + assert_vec, + &name, + scope.clone(), + ); + } + } else { + let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); + let new_id = self.id_gen.next(); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::When { + scope: scope.clone(), + tipo: tipo.clone().into(), + subject_name: format!("__subject_{}", new_id), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + for (index, constr) in data_type.constructors.iter().enumerate() { + let arg_indices = constr + .arguments + .iter() + .enumerate() + .map(|(index, arg)| { + let arg_name = arg + .label + .clone() + .unwrap_or(format!("__field_{}_{}", index, new_id)); + (index, arg_name, arg.tipo.clone()) + }) + .collect_vec(); + + assert_vec.push(Air::Clause { + scope: scope.clone(), + tipo: tipo.clone().into(), + subject_name: format!("__subject_{}", new_id), + complex_clause: false, + }); + + assert_vec.push(Air::Int { + scope: scope.clone(), + value: index.to_string(), + }); + + assert_vec.push(Air::FieldsExpose { + scope: scope.clone(), + indices: arg_indices.clone(), + check_last_item: true, + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + for (_, name, tipo) in arg_indices { + self.recursive_assert_tipo(&tipo, assert_vec, &name, scope.clone()); + } + + assert_vec.push(Air::Void { + scope: scope.clone(), + }); + } + + assert_vec.push(Air::ErrorTerm { scope, - ) + tipo: tipo.clone().into(), + label: None, + }); + } + } + + fn extract_arg_and_index( + &mut self, + item: &Pattern>, + field_index: usize, + nested_pattern: &mut Vec, + assignment_properties: &AssignmentProperties, + scope: &[u64], + assert: bool, + ) -> Option<(String, usize)> { + { + let (discard, var_name) = match item { + Pattern::Var { name, .. } => (false, name.clone()), + Pattern::Discard { .. } => (true, "".to_string()), + Pattern::List { .. } => todo!(), + a @ Pattern::Constructor { + tipo, + name: constr_name, + .. + } => { + let id = self.id_gen.next(); + let constr_name = format!("{constr_name}_{id}"); + + if assert { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } + + (false, constr_name) + } + _ => todo!(), + }; + + if discard { + None + } else { + Some((var_name, field_index)) + } } } @@ -2627,6 +2923,7 @@ impl<'a> CodeGenerator<'a> { scope, names, tail, + check_last_item, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); @@ -2636,6 +2933,7 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, names, tail, + check_last_item, }; } Air::ListExpose { @@ -2836,7 +3134,12 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, }; } - Air::TupleAccessor { tipo, scope, names } => { + Air::TupleAccessor { + tipo, + scope, + names, + check_last_item, + } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); @@ -2844,6 +3147,7 @@ impl<'a> CodeGenerator<'a> { scope, names, tipo: replaced_type, + check_last_item, }; } Air::RecordUpdate { @@ -2918,9 +3222,9 @@ impl<'a> CodeGenerator<'a> { } } Air::FieldsExpose { - count, indices, scope, + check_last_item, } => { let record = ir_stack[index + 1].clone(); let record_type = record.tipo(); @@ -2941,7 +3245,7 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } else { @@ -2955,7 +3259,7 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } @@ -3276,7 +3580,11 @@ impl<'a> CodeGenerator<'a> { } } Air::ListAccessor { - names, tail, tipo, .. + names, + tail, + tipo, + check_last_item, + .. } => { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -3333,6 +3641,7 @@ impl<'a> CodeGenerator<'a> { current_index, term, &tipo, + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), @@ -4383,6 +4692,64 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } Air::FieldsExpose { indices, .. } => { + // let mut id_list = vec![]; + + // for _ in 0..names.len() { + // id_list.push(self.id_gen.next()); + // } + + // let current_index = 0; + // let (first_name, names) = names.split_first().unwrap(); + + // let head_list = convert_data_to_type( + // apply_wrap( + // Term::Builtin(DefaultFunction::HeadList).force_wrap(), + // Term::Var(Name { + // text: format!("__tuple_{}", list_id), + // unique: 0.into(), + // }), + // ), + // &tipo.get_inner_types()[0], + // ); + + // term = apply_wrap( + // Term::Lambda { + // parameter_name: Name { + // text: format!("__tuple_{}", list_id), + // unique: 0.into(), + // }, + // body: apply_wrap( + // Term::Lambda { + // parameter_name: Name { + // text: first_name.clone(), + // unique: 0.into(), + // }, + // body: apply_wrap( + // list_access_to_uplc( + // names, + // &id_list, + // false, + // current_index, + // term, + // &tipo, + // check_last_item, + // ), + // apply_wrap( + // Term::Builtin(DefaultFunction::TailList).force_wrap(), + // Term::Var(Name { + // text: format!("__tuple_{}", list_id), + // unique: 0.into(), + // }), + // ), + // ) + // .into(), + // }, + // head_list, + // ) + // .into(), + // }, + // value, + // ); self.needs_field_access = true; let constr_var = arg_stack.pop().unwrap(); @@ -4822,7 +5189,12 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::TupleAccessor { tipo, names, .. } => { + Air::TupleAccessor { + tipo, + names, + check_last_item, + .. + } => { let inner_types = tipo.get_inner_types(); let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -4922,6 +5294,7 @@ impl<'a> CodeGenerator<'a> { current_index, term, &tipo, + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), From 4c8089c28aacf1a2581011b913ecb390c56b9166 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 28 Jan 2023 05:14:12 -0500 Subject: [PATCH 16/19] Assert now works for a variety of types. Still some final work to finish and test. Things are good so far --- crates/aiken-lang/src/builder.rs | 21 +-- crates/aiken-lang/src/uplc.rs | 284 +++++++++---------------------- 2 files changed, 88 insertions(+), 217 deletions(-) diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 9df15630..6f02152d 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ - builder::{apply_wrap, choose_list, if_else}, + builder::{apply_wrap, choose_list, delayed_choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -481,12 +481,13 @@ pub fn list_access_to_uplc( tail: bool, current_index: usize, term: Term, - tipo: &Type, + tipos: Vec>, check_last_item: bool, ) -> Term { let (first, names) = names.split_first().unwrap(); + let (current_tipo, tipos) = tipos.split_first().unwrap(); - let head_list = if tipo.is_map() { + let head_list = if current_tipo.is_map() { apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var(Name { @@ -503,7 +504,7 @@ pub fn list_access_to_uplc( unique: 0.into(), }), ), - &tipo.clone().get_inner_types()[0], + ¤t_tipo.to_owned(), ) }; @@ -559,7 +560,7 @@ pub fn list_access_to_uplc( unique: 0.into(), }, body: if check_last_item { - choose_list( + delayed_choose_list( apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var(Name { @@ -587,13 +588,7 @@ pub fn list_access_to_uplc( term.into() }, }, - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - Term::Var(Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }), - ), + head_list, ) .into(), } @@ -616,7 +611,7 @@ pub fn list_access_to_uplc( tail, current_index + 1, term, - tipo, + tipos.to_owned(), check_last_item, ), apply_wrap( diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 4042e7de..89d37b71 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, vec}; +use std::{clone, sync::Arc, vec}; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; @@ -1541,13 +1541,10 @@ impl<'a> CodeGenerator<'a> { } } constr @ Pattern::Constructor { .. } => { - println!("ASSIGNMENT PROPERTIES {:#?}", assignment_properties); - println!("CURRENT TYPE IS {:#?}", tipo); if matches!(assignment_properties.kind, AssignmentKind::Assert) && assignment_properties.value_is_data && !tipo.is_data() { - println!("GOT HERE FOR SURE"); self.recursive_assert_pattern( constr, pattern_vec, @@ -1928,14 +1925,15 @@ impl<'a> CodeGenerator<'a> { if arguments_index.get(current_index).is_some() && arguments_index[current_index].1 == index { - final_args.push(arguments_index.get(index).unwrap().clone()); + final_args.push(arguments_index.get(current_index).unwrap().clone()); current_index += 1; } else { - final_args.push((format!("__field_{index}_{}", self.id_gen.next()), index)); + let id_next = self.id_gen.next(); + final_args.push((format!("__field_{index}_{}", id_next), index)); self.recursive_assert_tipo( type_map.get(&index).unwrap(), &mut nested_pattern, - &format!("__field_{index}_{}", self.id_gen.next()), + &format!("__field_{index}_{}", id_next), scope.clone(), ) } @@ -1994,11 +1992,12 @@ impl<'a> CodeGenerator<'a> { final_args.push(arguments_index.get(index).unwrap().clone()); current_index += 1; } else { - final_args.push((format!("__field_{index}_{}", self.id_gen.next()), index)); + let id_next = self.id_gen.next(); + final_args.push((format!("__tuple_{index}_{}", id_next), index)); self.recursive_assert_tipo( type_map.get(&index).unwrap(), &mut nested_pattern, - &format!("__field_{index}_{}", self.id_gen.next()), + &format!("__tuple_{index}_{}", id_next), scope.clone(), ) } @@ -2006,7 +2005,7 @@ impl<'a> CodeGenerator<'a> { if !arguments_index.is_empty() { pattern_vec.push(Air::TupleAccessor { - scope: scope.clone(), + scope, names: final_args.into_iter().map(|(item, _)| item).collect_vec(), tipo: tipo.clone().into(), check_last_item: true, @@ -2055,7 +2054,7 @@ impl<'a> CodeGenerator<'a> { assert_vec.push(Air::Call { scope: scope.clone(), - count: 3, + count: 2, tipo: tipo.clone().into(), }); @@ -2144,7 +2143,7 @@ impl<'a> CodeGenerator<'a> { assert_vec.push(Air::Call { scope: scope.clone(), - count: 3, + count: 2, tipo: tipo.clone().into(), }); @@ -3621,6 +3620,13 @@ impl<'a> CodeGenerator<'a> { ) }; + let inner_types = tipo + .get_inner_types() + .into_iter() + .cycle() + .take(names.len()) + .collect_vec(); + term = apply_wrap( Term::Lambda { parameter_name: Name { @@ -3640,7 +3646,7 @@ impl<'a> CodeGenerator<'a> { tail, current_index, term, - &tipo, + inner_types, check_last_item, ), apply_wrap( @@ -4228,7 +4234,13 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::UnWrapData { scope, tipo } => todo!(), + Air::UnWrapData { tipo, .. } => { + let mut term = arg_stack.pop().unwrap(); + + term = convert_data_to_type(term, &tipo); + + arg_stack.push(term); + } Air::When { subject_name, tipo, .. } => { @@ -4691,220 +4703,84 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::FieldsExpose { indices, .. } => { - // let mut id_list = vec![]; - - // for _ in 0..names.len() { - // id_list.push(self.id_gen.next()); - // } - - // let current_index = 0; - // let (first_name, names) = names.split_first().unwrap(); - - // let head_list = convert_data_to_type( - // apply_wrap( - // Term::Builtin(DefaultFunction::HeadList).force_wrap(), - // Term::Var(Name { - // text: format!("__tuple_{}", list_id), - // unique: 0.into(), - // }), - // ), - // &tipo.get_inner_types()[0], - // ); - - // term = apply_wrap( - // Term::Lambda { - // parameter_name: Name { - // text: format!("__tuple_{}", list_id), - // unique: 0.into(), - // }, - // body: apply_wrap( - // Term::Lambda { - // parameter_name: Name { - // text: first_name.clone(), - // unique: 0.into(), - // }, - // body: apply_wrap( - // list_access_to_uplc( - // names, - // &id_list, - // false, - // current_index, - // term, - // &tipo, - // check_last_item, - // ), - // apply_wrap( - // Term::Builtin(DefaultFunction::TailList).force_wrap(), - // Term::Var(Name { - // text: format!("__tuple_{}", list_id), - // unique: 0.into(), - // }), - // ), - // ) - // .into(), - // }, - // head_list, - // ) - // .into(), - // }, - // value, - // ); + Air::FieldsExpose { + indices, + check_last_item, + .. + } => { self.needs_field_access = true; - - let constr_var = arg_stack.pop().unwrap(); - let mut body = arg_stack.pop().unwrap(); - - let mut indices = indices.into_iter().rev(); - let highest = indices.next().unwrap(); let mut id_list = vec![]; + let value = arg_stack.pop().unwrap(); + let mut term = arg_stack.pop().unwrap(); + let list_id = self.id_gen.next(); - for _ in 0..highest.0 { + for _ in 0..indices.len() { id_list.push(self.id_gen.next()); } - let constr_name_lam = format!("__constr_fields_{}", self.id_gen.next()); - let highest_loop_index = highest.0 as i32 - 1; - let last_prev_tail = Term::Var(Name { - text: if highest_loop_index == -1 { - constr_name_lam.clone() - } else { - format!( - "__tail_{}_{}", - highest_loop_index, id_list[highest_loop_index as usize] - ) - }, - unique: 0.into(), - }); + let current_index = 0; + let (first_name, indices) = indices.split_first().unwrap(); - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: highest.1, + let head_list = convert_data_to_type( + apply_wrap( + Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Var(Name { + text: format!("__constr_fields_{}", list_id), unique: 0.into(), - }, - body: body.into(), - }, - convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - last_prev_tail, - ), - &highest.2, + }), ), + &first_name.2, ); - let mut current_field = None; - for index in (0..highest.0).rev() { - let current_tail_index = index; - let previous_tail_index = if index == 0 { 0 } else { index - 1 }; - let current_tail_id = id_list[index]; - let previous_tail_id = if index == 0 { 0 } else { id_list[index - 1] }; - if current_field.is_none() { - current_field = indices.next(); - } + let names = indices.iter().cloned().map(|item| item.1).collect_vec(); + let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); - let prev_tail = if index == 0 { - Term::Var(Name { - text: constr_name_lam.clone(), - unique: 0.into(), - }) - } else { - Term::Var(Name { - text: format!("__tail_{previous_tail_index}_{previous_tail_id}"), - unique: 0.into(), - }) - }; - - if let Some(ref field) = current_field { - if field.0 == index { - let unwrapper = convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - prev_tail.clone(), - ), - &field.2, - ); - - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: field.1.clone(), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!( - "__tail_{current_tail_index}_{current_tail_id}" - ), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ) - .into(), - }, - unwrapper, - ); - - current_field = None; - } else { - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!( - "__tail_{current_tail_index}_{current_tail_id}" - ), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ); - } - } else { - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!("__tail_{current_tail_index}_{current_tail_id}"), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ); - } - } - - body = apply_wrap( + term = apply_wrap( Term::Lambda { parameter_name: Name { - text: constr_name_lam, + text: format!("__constr_fields_{}", list_id), unique: 0.into(), }, - body: body.into(), + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first_name.1.clone(), + unique: 0.into(), + }, + body: apply_wrap( + list_access_to_uplc( + &names, + &id_list, + false, + current_index, + term, + inner_types, + check_last_item, + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!("__constr_fields_{}", list_id), + unique: 0.into(), + }), + ), + ) + .into(), + }, + head_list, + ) + .into(), }, apply_wrap( Term::Var(Name { text: CONSTR_FIELDS_EXPOSER.to_string(), unique: 0.into(), }), - constr_var, + value, ), ); - arg_stack.push(body); + arg_stack.push(term); } Air::Tuple { tipo, count, .. } => { let mut args = vec![]; @@ -5293,7 +5169,7 @@ impl<'a> CodeGenerator<'a> { false, current_index, term, - &tipo, + tipo.get_inner_types(), check_last_item, ), apply_wrap( From afd040fda597aea165f7d3af8d60622852ddbe46 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 29 Jan 2023 05:21:55 -0500 Subject: [PATCH 17/19] feat: Finish up assert feature Better error reporing on NonFunctionalApplication Error Refactor redundant code Add handling of whens with one clause. --- crates/aiken-lang/src/air.rs | 18 - crates/aiken-lang/src/builder.rs | 250 ++++++------ crates/aiken-lang/src/tipo.rs | 8 + crates/aiken-lang/src/uplc.rs | 636 +++++++++++++++---------------- crates/uplc/src/machine.rs | 2 +- crates/uplc/src/machine/error.rs | 4 +- 6 files changed, 454 insertions(+), 464 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index ef60e3d9..c80829c7 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -96,12 +96,6 @@ pub enum Air { }, // Assignment - Assert { - scope: Vec, - tipo: Arc, - value_is_data: bool, - }, - Let { scope: Vec, name: String, @@ -112,14 +106,6 @@ pub enum Air { tipo: Arc, }, - ListAssert { - scope: Vec, - tipo: Arc, - names: Vec, - tail: bool, - value_is_data: bool, - }, - // When When { scope: Vec, @@ -275,8 +261,6 @@ impl Air { | Air::Builtin { scope, .. } | Air::BinOp { scope, .. } | Air::UnOp { scope, .. } - | Air::Assert { scope, .. } - | Air::ListAssert { scope, .. } | Air::Let { scope, .. } | Air::UnWrapData { scope, .. } | Air::When { scope, .. } @@ -356,8 +340,6 @@ impl Air { | Air::Call { tipo, .. } | Air::Builtin { tipo, .. } | Air::BinOp { tipo, .. } - | Air::Assert { tipo, .. } - | Air::ListAssert { tipo, .. } | Air::UnWrapData { tipo, .. } | Air::When { tipo, .. } | Air::Clause { tipo, .. } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 6f02152d..d50e7f08 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ - builder::{apply_wrap, choose_list, delayed_choose_list, if_else}, + builder::{apply_wrap, delayed_choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -484,83 +484,50 @@ pub fn list_access_to_uplc( tipos: Vec>, check_last_item: bool, ) -> Term { - let (first, names) = names.split_first().unwrap(); - let (current_tipo, tipos) = tipos.split_first().unwrap(); + if let Some((first, names)) = names.split_first() { + let (current_tipo, tipos) = tipos.split_first().unwrap(); - let head_list = if current_tipo.is_map() { - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - Term::Var(Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }), - ) - } else { - convert_data_to_type( + let head_list = if current_tipo.is_map() { apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var(Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), }), - ), - ¤t_tipo.to_owned(), - ) - }; - - if names.len() == 1 && tail { - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: names[0].clone(), - unique: 0.into(), - }, - body: term.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var(Name { - text: format!( - "tail_index_{}_{}", - current_index, id_list[current_index] - ), - unique: 0.into(), - }), - ), - ) - .into(), - }, - head_list, ) - .into(), - } - } else if names.is_empty() { - // Maybe check list is actually empty or should we leave that to when .. is only - // this would replace term.into() if we decide to - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), + } else { + convert_data_to_type( + apply_wrap( + Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Var(Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), - }, - body: if check_last_item { - delayed_choose_list( + }), + ), + ¤t_tipo.to_owned(), + ) + }; + + if names.len() == 1 && tail { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: names[0].clone(), + unique: 0.into(), + }, + body: term.into(), + }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var(Name { @@ -571,66 +538,101 @@ pub fn list_access_to_uplc( unique: 0.into(), }), ), - term, - apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::Trace).force_wrap(), - Term::Constant(UplcConstant::String( - "List/Tuple contains more items than it should".to_string(), - )), - ), - Term::Delay(Term::Error.into()), - ) - .force_wrap(), ) - .into() - } else { - term.into() + .into(), }, + head_list, + ) + .into(), + } + } else if names.is_empty() { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), }, - head_list, - ) - .into(), + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: if check_last_item { + delayed_choose_list( + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + term, + apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Constant(UplcConstant::String( + "List/Tuple contains more items than it should" + .to_string(), + )), + ), + Term::Delay(Term::Error.into()), + ) + .force_wrap(), + ) + .into() + } else { + term.into() + }, + }, + head_list, + ) + .into(), + } + } else { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: apply_wrap( + list_access_to_uplc( + names, + id_list, + tail, + current_index + 1, + term, + tipos.to_owned(), + check_last_item, + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + ) + .into(), + }, + head_list, + ) + .into(), + } } } else { - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: apply_wrap( - list_access_to_uplc( - names, - id_list, - tail, - current_index + 1, - term, - tipos.to_owned(), - check_last_item, - ), - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var(Name { - text: format!( - "tail_index_{}_{}", - current_index, id_list[current_index] - ), - unique: 0.into(), - }), - ), - ) - .into(), - }, - head_list, - ) - .into(), - } + term } } diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index 541a4db4..fa8f31e3 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -213,6 +213,7 @@ impl Type { match self { Self::Fn { args, .. } => Some(args.clone()), Self::App { args, .. } => Some(args.clone()), + Self::Var { tipo } => tipo.borrow().arg_types(), _ => None, } } @@ -498,6 +499,13 @@ impl TypeVar { } } + pub fn arg_types(&self) -> Option>> { + match self { + Self::Link { tipo } => tipo.arg_types(), + _ => None, + } + } + pub fn get_inner_type(&self) -> Vec> { match self { Self::Link { tipo } => tipo.get_inner_types(), diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 89d37b71..343dc3a7 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1,4 +1,4 @@ -use std::{clone, sync::Arc, vec}; +use std::sync::Arc; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; @@ -19,7 +19,7 @@ use uplc::{ use crate::{ air::Air, ast::{ - ArgName, AssignmentKind, BinOp, CallArg, Clause, Pattern, Span, TypedArg, TypedDataType, + ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType, TypedFunction, UnOp, }, builder::{ @@ -32,8 +32,8 @@ use crate::{ }, expr::TypedExpr, tipo::{ - fields::FieldMap, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, TypeVar, - ValueConstructor, ValueConstructorVariant, + ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, + ValueConstructorVariant, }, IdGenerator, }; @@ -80,13 +80,10 @@ impl<'a> CodeGenerator<'a> { let scope = vec![self.id_gen.next()]; self.build_ir(&body, &mut ir_stack, scope); - println!("{:#?}", ir_stack); self.define_ir(&mut ir_stack); - println!("{:#?}", ir_stack); self.convert_opaque_type_to_inner_ir(&mut ir_stack); - println!("{:#?}", ir_stack); let mut term = self.uplc_code_gen(&mut ir_stack); @@ -115,7 +112,6 @@ impl<'a> CodeGenerator<'a> { version: (1, 0, 0), term, }; - println!("{}", program.to_pretty()); let mut interner = Interner::new(); @@ -324,104 +320,124 @@ impl<'a> CodeGenerator<'a> { // assuming one subject at the moment let subject = subjects[0].clone(); - if clauses.len() <= 1 { - todo!("Single clause cases not implemented") - } + let mut value_vec: Vec = vec![]; + let mut pattern_vec: Vec = vec![]; + let mut subject_vec: Vec = vec![]; - let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) { - rearrange_clauses(clauses.clone()) - } else { - clauses.clone() - }; + self.build_ir(&clauses[0].then, &mut value_vec, scope.clone()); - if let Some((last_clause, clauses)) = clauses.split_last() { - let mut pattern_vec = vec![]; + self.build_ir(&subject, &mut value_vec, scope.clone()); - let mut clause_properties = ClauseProperties::init( - &subject.tipo(), - constr_var.clone(), - subject_name.clone(), - ); - - self.handle_each_clause( + self.assignment_ir( + &clauses[0].pattern[0], &mut pattern_vec, - &mut clause_properties, - clauses, + &mut subject_vec, &subject.tipo(), - scope.clone(), + AssignmentProperties { + value_is_data: false, + kind: AssignmentKind::Let, + }, + scope, ); - let last_pattern = &last_clause.pattern[0]; - - let mut final_scope = scope.clone(); - - final_scope.push(self.id_gen.next()); - - pattern_vec.push(Air::Finally { - scope: final_scope.clone(), - }); - - let mut final_clause_vec = vec![]; - - self.build_ir( - &last_clause.then, - &mut final_clause_vec, - final_scope.clone(), - ); - - self.when_ir( - last_pattern, - &mut pattern_vec, - &mut final_clause_vec, - &subject.tipo(), - &mut clause_properties, - final_scope, - ); - - if *clause_properties.needs_constr_var() { - ir_stack.push(Air::Let { - scope: scope.clone(), - name: constr_var.clone(), - }); - - self.build_ir(&subject, ir_stack, scope.clone()); - - ir_stack.push(Air::When { - scope: scope.clone(), - subject_name, - tipo: subject.tipo(), - }); - - let mut scope = scope; - scope.push(self.id_gen.next()); - - ir_stack.push(Air::Var { - scope, - constructor: ValueConstructor::public( - subject.tipo(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_var, - variant_name: String::new(), - }) - } else { - ir_stack.push(Air::When { - scope: scope.clone(), - subject_name, - tipo: subject.tipo(), - }); - - let mut scope = scope; - scope.push(self.id_gen.next()); - - self.build_ir(&subject, ir_stack, scope); - } - ir_stack.append(&mut pattern_vec); - }; + ir_stack.append(&mut value_vec); + } else { + let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) { + rearrange_clauses(clauses.clone()) + } else { + clauses.clone() + }; + + if let Some((last_clause, clauses)) = clauses.split_last() { + let mut pattern_vec = vec![]; + + let mut clause_properties = ClauseProperties::init( + &subject.tipo(), + constr_var.clone(), + subject_name.clone(), + ); + + self.handle_each_clause( + &mut pattern_vec, + &mut clause_properties, + clauses, + &subject.tipo(), + scope.clone(), + ); + + let last_pattern = &last_clause.pattern[0]; + + let mut final_scope = scope.clone(); + + final_scope.push(self.id_gen.next()); + + pattern_vec.push(Air::Finally { + scope: final_scope.clone(), + }); + + let mut final_clause_vec = vec![]; + + self.build_ir( + &last_clause.then, + &mut final_clause_vec, + final_scope.clone(), + ); + + self.when_ir( + last_pattern, + &mut pattern_vec, + &mut final_clause_vec, + &subject.tipo(), + &mut clause_properties, + final_scope, + ); + + if *clause_properties.needs_constr_var() { + ir_stack.push(Air::Let { + scope: scope.clone(), + name: constr_var.clone(), + }); + + self.build_ir(&subject, ir_stack, scope.clone()); + + ir_stack.push(Air::When { + scope: scope.clone(), + subject_name, + tipo: subject.tipo(), + }); + + let mut scope = scope; + scope.push(self.id_gen.next()); + + ir_stack.push(Air::Var { + scope, + constructor: ValueConstructor::public( + subject.tipo(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var, + variant_name: String::new(), + }) + } else { + ir_stack.push(Air::When { + scope: scope.clone(), + subject_name, + tipo: subject.tipo(), + }); + + let mut scope = scope; + scope.push(self.id_gen.next()); + + self.build_ir(&subject, ir_stack, scope); + } + + ir_stack.append(&mut pattern_vec); + } + } } TypedExpr::If { branches, @@ -479,7 +495,9 @@ impl<'a> CodeGenerator<'a> { tipo, .. } => match constructor { - ModuleValueConstructor::Record { .. } => todo!(), + ModuleValueConstructor::Record { .. } => { + todo!("Records from modules not yet implemented.") + } ModuleValueConstructor::Fn { name, module, .. } => { let func = self.functions.get(&FunctionAccessKey { module_name: module_name.clone(), @@ -798,7 +816,7 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(values); } - Pattern::String { .. } => todo!(), + Pattern::String { .. } => todo!("String matching in whens not yet implemented"), Pattern::Var { name, .. } => { pattern_vec.push(Air::Void { scope: scope.clone(), @@ -821,7 +839,7 @@ impl<'a> CodeGenerator<'a> { }); pattern_vec.append(values); } - Pattern::VarUsage { .. } => todo!(), + Pattern::VarUsage { .. } => unreachable!(), Pattern::Assign { name, pattern, .. } => { let mut new_vec = vec![]; new_vec.push(Air::Let { @@ -988,11 +1006,11 @@ impl<'a> CodeGenerator<'a> { scope: Vec, ) { match pattern { - Pattern::Int { .. } => todo!(), - Pattern::String { .. } => todo!(), - Pattern::Var { .. } => todo!(), - Pattern::VarUsage { .. } => todo!(), - Pattern::Assign { .. } => todo!(), + Pattern::Int { .. } => unreachable!(), + Pattern::String { .. } => unreachable!(), + Pattern::Var { .. } => unreachable!(), + Pattern::VarUsage { .. } => unreachable!(), + Pattern::Assign { .. } => todo!("Nested assign not yet implemented"), Pattern::Discard { .. } => { pattern_vec.push(Air::Void { scope }); @@ -1022,7 +1040,7 @@ impl<'a> CodeGenerator<'a> { tail_name = name.clone(); } Pattern::Discard { .. } => {} - _ => todo!(), + _ => unreachable!("Patterns in tail of list should not allow this"), } } @@ -1458,7 +1476,10 @@ impl<'a> CodeGenerator<'a> { Some(item_name) } - _ => todo!(), + Pattern::Assign { .. } => todo!("Nested assign is not yet done"), + Pattern::Int { .. } => unimplemented!(), + Pattern::String { .. } => unimplemented!(), + Pattern::VarUsage { .. } => unreachable!(), } } @@ -1506,8 +1527,8 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(&mut assert_vec); } } - Pattern::VarUsage { .. } => todo!(), - Pattern::Assign { .. } => todo!(), + Pattern::VarUsage { .. } => unreachable!(), + Pattern::Assign { .. } => todo!("Assign not yet implemented yet"), Pattern::Discard { .. } => { pattern_vec.push(Air::Let { name: "_".to_string(), @@ -1616,7 +1637,9 @@ impl<'a> CodeGenerator<'a> { Pattern::Var { name, .. } => { names.push(name.clone()); } - a @ Pattern::List { .. } => { + a @ (Pattern::List { .. } + | Pattern::Constructor { .. } + | Pattern::Tuple { .. }) => { let mut var_vec = vec![]; let item_name = format!("list_item_id_{}", self.id_gen.next()); names.push(item_name.clone()); @@ -1646,7 +1669,13 @@ impl<'a> CodeGenerator<'a> { scope.clone(), ); } - _ => todo!(), + Pattern::Int { .. } => todo!(), + Pattern::String { .. } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + Pattern::Discard { .. } => { + names.push("_".to_string()); + } } } @@ -1700,9 +1729,9 @@ impl<'a> CodeGenerator<'a> { &item.value, *field_index, &mut nested_pattern, + type_map.get(field_index).unwrap(), &assignment_properties, &scope, - false, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) @@ -1726,59 +1755,45 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(&mut nested_pattern); } Pattern::Tuple { elems, .. } => { - let mut elements_vec = vec![]; + let mut nested_pattern = vec![]; + let mut type_map: IndexMap> = IndexMap::new(); - let mut names = vec![]; - for element in elems { - match element { - Pattern::Var { name, .. } => { - names.push(name.clone()); - } - Pattern::Discard { .. } => { - names.push("_".to_string()); - } - a @ Pattern::List { .. } => { - let mut var_vec = vec![]; - let item_name = format!("list_item_id_{}", self.id_gen.next()); - names.push(item_name.clone()); - var_vec.push(Air::Var { - constructor: ValueConstructor::public( - Type::App { - public: true, - module: String::new(), - name: String::new(), - args: vec![], - } - .into(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: item_name, - scope: scope.clone(), - variant_name: String::new(), - }); - self.pattern_ir( - a, - &mut elements_vec, - &mut var_vec, - &tipo.get_inner_types()[0], - assignment_properties.clone(), - scope.clone(), - ); - } - _ => todo!(), - } + for (index, arg) in tipo.get_inner_types().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + + let arguments_index = elems + .iter() + .enumerate() + .filter_map(|(tuple_index, item)| { + self.extract_arg_and_index( + item, + tuple_index, + &mut nested_pattern, + type_map.get(&tuple_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + if !arguments_index.is_empty() { + pattern_vec.push(Air::TupleAccessor { + scope, + names: arguments_index + .into_iter() + .map(|(item, _)| item) + .collect_vec(), + tipo: tipo.clone().into(), + check_last_item: true, + }); } - pattern_vec.push(Air::TupleAccessor { - names, - scope, - tipo: tipo.clone().into(), - check_last_item: false, - }); pattern_vec.append(values); - pattern_vec.append(&mut elements_vec); + + pattern_vec.append(&mut nested_pattern); } } } @@ -1819,7 +1834,9 @@ impl<'a> CodeGenerator<'a> { } Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), - l @ Pattern::List { .. } => { + l @ (Pattern::List { .. } + | Pattern::Constructor { .. } + | Pattern::Tuple { .. }) => { let name = format!("list_item_id_{}", self.id_gen.next()); names.push(name.clone()); @@ -1842,8 +1859,6 @@ impl<'a> CodeGenerator<'a> { scope.clone(), ); } - Pattern::Constructor { .. } => todo!(), - Pattern::Tuple { elems, .. } => todo!(), _ => {} } } @@ -1899,8 +1914,6 @@ impl<'a> CodeGenerator<'a> { type_map.insert(index, field_type); } - println!("TYPE IS {:#?}", type_map); - let arguments_index = arguments .iter() .filter_map(|item| { @@ -1910,9 +1923,9 @@ impl<'a> CodeGenerator<'a> { &item.value, *field_index, &mut nested_pattern, + type_map.get(field_index).unwrap(), &assignment_properties, &scope, - true, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) @@ -1974,9 +1987,9 @@ impl<'a> CodeGenerator<'a> { item, field_index, &mut nested_pattern, + type_map.get(&field_index).unwrap(), &assignment_properties, &scope, - true, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) @@ -2003,7 +2016,7 @@ impl<'a> CodeGenerator<'a> { } } - if !arguments_index.is_empty() { + if !final_args.is_empty() { pattern_vec.push(Air::TupleAccessor { scope, names: final_args.into_iter().map(|(item, _)| item).collect_vec(), @@ -2336,7 +2349,7 @@ impl<'a> CodeGenerator<'a> { assert_vec.push(Air::ErrorTerm { scope, tipo: tipo.clone().into(), - label: None, + label: Some("Constr index did not match any type variant".to_string()), }); } } @@ -2346,15 +2359,63 @@ impl<'a> CodeGenerator<'a> { item: &Pattern>, field_index: usize, nested_pattern: &mut Vec, + tipo: &Type, assignment_properties: &AssignmentProperties, scope: &[u64], - assert: bool, ) -> Option<(String, usize)> { { let (discard, var_name) = match item { Pattern::Var { name, .. } => (false, name.clone()), Pattern::Discard { .. } => (true, "".to_string()), - Pattern::List { .. } => todo!(), + a @ Pattern::List { .. } => { + let id = self.id_gen.next(); + let list_name = format!("__list_{id}"); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: list_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: list_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } + + (false, list_name) + } a @ Pattern::Constructor { tipo, name: constr_name, @@ -2363,7 +2424,10 @@ impl<'a> CodeGenerator<'a> { let id = self.id_gen.next(); let constr_name = format!("{constr_name}_{id}"); - if assert { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { self.recursive_assert_pattern( a, nested_pattern, @@ -2405,7 +2469,59 @@ impl<'a> CodeGenerator<'a> { (false, constr_name) } - _ => todo!(), + a @ Pattern::Tuple { .. } => { + let id = self.id_gen.next(); + let tuple_name = format!("__tuple_name_{id}"); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: tuple_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: tuple_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } + + (false, tuple_name) + } + Pattern::Int { .. } => todo!(), + Pattern::String { .. } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), }; if discard { @@ -3367,10 +3483,7 @@ impl<'a> CodeGenerator<'a> { })); } ValueConstructorVariant::Record { - name: constr_name, - field_map, - arity, - .. + name: constr_name, .. } => { if constructor.tipo.is_bool() { arg_stack @@ -3391,59 +3504,10 @@ impl<'a> CodeGenerator<'a> { .find(|(_, x)| x.name == *constr_name) .unwrap(); - let mut fields = + let fields = Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![])); - let tipo = constructor.tipo; - - let args_type = tipo.arg_types().unwrap(); - - if let Some(field_map) = field_map.clone() { - for field in field_map - .fields - .iter() - .sorted_by(|item1, item2| { - let (a, _) = item1.1; - let (b, _) = item2.1; - a.cmp(b) - }) - .zip(&args_type) - .rev() - { - // TODO revisit - fields = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), - convert_type_to_data( - Term::Var(Name { - text: field.0 .0.clone(), - unique: 0.into(), - }), - field.1, - ), - ), - fields, - ); - } - } else { - for (index, arg) in args_type.iter().enumerate().take(*arity) { - fields = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), - convert_type_to_data( - Term::Var(Name { - text: format!("__arg_{}", index), - unique: 0.into(), - }), - arg, - ), - ), - fields, - ); - } - } - - let mut term = apply_wrap( + let term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::ConstrData), Term::Constant(UplcConstant::Integer( @@ -3453,37 +3517,6 @@ impl<'a> CodeGenerator<'a> { fields, ); - if let Some(field_map) = field_map { - for field in field_map - .fields - .iter() - .sorted_by(|item1, item2| { - let (a, _) = item1.1; - let (b, _) = item2.1; - a.cmp(b) - }) - .rev() - { - term = Term::Lambda { - parameter_name: Name { - text: field.0.clone(), - unique: 0.into(), - }, - body: term.into(), - }; - } - } else { - for (index, _) in args_type.iter().enumerate().take(*arity) { - term = Term::Lambda { - parameter_name: Name { - text: format!("__arg_{}", index), - unique: 0.into(), - }, - body: term.into(), - }; - } - } - arg_stack.push(term); } } @@ -4094,46 +4127,6 @@ impl<'a> CodeGenerator<'a> { }; arg_stack.push(term); } - Air::Assert { - tipo, - value_is_data, - .. - } => { - let constr = arg_stack.pop().unwrap(); - - let mut term = arg_stack.pop().unwrap(); - - let trace_error = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::Trace).force_wrap(), - Term::Constant(UplcConstant::String("Assert Failed".to_string())), - ), - Term::Delay(Term::Error.into()), - ) - .force_wrap(); - todo!(); - - // let condition = apply_wrap( - // apply_wrap( - // DefaultFunction::EqualsInteger.into(), - // Term::Constant(UplcConstant::Integer(constr_index as i128)), - // ), - // constr_index_exposer(constr), - // ); - - // term = delayed_if_else(condition, term, trace_error); - - arg_stack.push(term); - } - Air::ListAssert { - scope, - tipo, - names, - tail, - value_is_data, - } => { - todo!(); - } Air::DefineFunc { func_name, params, @@ -4735,6 +4728,29 @@ impl<'a> CodeGenerator<'a> { let names = indices.iter().cloned().map(|item| item.1).collect_vec(); let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); + let tail_list = if !indices.is_empty() { + apply_wrap( + list_access_to_uplc( + &names, + &id_list, + false, + current_index, + term, + inner_types, + check_last_item, + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!("__constr_fields_{}", list_id), + unique: 0.into(), + }), + ), + ) + } else { + term + }; + term = apply_wrap( Term::Lambda { parameter_name: Name { @@ -4747,25 +4763,7 @@ impl<'a> CodeGenerator<'a> { text: first_name.1.clone(), unique: 0.into(), }, - body: apply_wrap( - list_access_to_uplc( - &names, - &id_list, - false, - current_index, - term, - inner_types, - check_last_item, - ), - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var(Name { - text: format!("__constr_fields_{}", list_id), - unique: 0.into(), - }), - ), - ) - .into(), + body: tail_list.into(), }, head_list, ) diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 196fdb25..89262cbe 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -343,7 +343,7 @@ impl Machine { Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone())) } } - rest => Err(Error::NonFunctionalApplication(rest)), + rest => Err(Error::NonFunctionalApplication(rest, argument)), } } diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 7132db2c..5749a38f 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -16,8 +16,8 @@ pub enum Error { EvaluationFailure, #[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")] NonPolymorphicInstantiation(Value), - #[error("Attempted to apply a non-function:\n\n{0:#?}")] - NonFunctionalApplication(Value), + #[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")] + NonFunctionalApplication(Value, Value), #[error("Type mismatch expected '{0}' got '{1}'")] TypeMismatch(Type, Type), #[error("Type mismatch expected '(list a)' got '{0}'")] From 8c04ab093a18c4d38240d24e44d8b839e819706f Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 30 Jan 2023 03:24:52 -0500 Subject: [PATCH 18/19] update test 40 --- crates/aiken-lang/src/air.rs | 1 - examples/acceptance_tests/040/lib/tests.ak | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index c80829c7..e17e256a 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -334,7 +334,6 @@ impl Air { .into(), ), Air::Var { constructor, .. } => Some(constructor.tipo.clone()), - Air::List { tipo, .. } | Air::Tuple { tipo, .. } | Air::Call { tipo, .. } diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index 554bde33..6a16c937 100644 --- a/examples/acceptance_tests/040/lib/tests.ak +++ b/examples/acceptance_tests/040/lib/tests.ak @@ -10,13 +10,13 @@ pub type Car { // test update_owner2_should_fail(){ // let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000} -// assert Honda{ owner, ..} = initial_car +// assert Honda{ owner, ..}: Car = initial_car // owner == #[] // } test update_owner1() { let initial_car: Data = Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 } - assert Ford { owner, .. } = initial_car + assert Ford { owner, .. }: Car = initial_car owner == #[] } From a638388747fd9a95d0279cc7c24f46157da618b0 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 30 Jan 2023 04:17:03 -0500 Subject: [PATCH 19/19] Add more complex test 40. Add assert for when constr index is given --- crates/aiken-lang/src/air.rs | 7 ++ crates/aiken-lang/src/uplc.rs | 76 ++++++++++++++++++++-- examples/acceptance_tests/040/lib/tests.ak | 11 +++- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index e17e256a..1b5d1262 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -106,6 +106,11 @@ pub enum Air { tipo: Arc, }, + AssertConstr { + scope: Vec, + constr_index: usize, + }, + // When When { scope: Vec, @@ -263,6 +268,7 @@ impl Air { | Air::UnOp { scope, .. } | Air::Let { scope, .. } | Air::UnWrapData { scope, .. } + | Air::AssertConstr { scope, .. } | Air::When { scope, .. } | Air::Clause { scope, .. } | Air::ListClause { scope, .. } @@ -362,6 +368,7 @@ impl Air { | Air::Fn { .. } | Air::Let { .. } | Air::WrapClause { .. } + | Air::AssertConstr { .. } | Air::Finally { .. } | Air::FieldsExpose { .. } => None, diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 343dc3a7..3aeea6de 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1901,10 +1901,11 @@ impl<'a> CodeGenerator<'a> { let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); - let data_type_constr = data_type + let (index, data_type_constr) = data_type .constructors .iter() - .find(|constr| &constr.name == constr_name) + .enumerate() + .find(|(_, constr)| &constr.name == constr_name) .unwrap(); let mut type_map: IndexMap> = IndexMap::new(); @@ -1952,6 +1953,31 @@ impl<'a> CodeGenerator<'a> { } } + let constr_var = format!("__constr_{}", self.id_gen.next()); + pattern_vec.push(Air::Let { + scope: scope.clone(), + name: constr_var.clone(), + }); + + pattern_vec.append(value_vec); + + pattern_vec.push(Air::AssertConstr { + scope: scope.clone(), + constr_index: index, + }); + + pattern_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var.clone(), + variant_name: String::new(), + }); + if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { indices: final_args @@ -1961,12 +1987,22 @@ impl<'a> CodeGenerator<'a> { (*index, var_name.clone(), field_type.clone()) }) .collect_vec(), - scope, + scope: scope.clone(), check_last_item: true, }); - } - pattern_vec.append(value_vec); + pattern_vec.push(Air::Var { + scope, + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var, + variant_name: String::new(), + }); + } pattern_vec.append(&mut nested_pattern); } @@ -4234,6 +4270,36 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + Air::AssertConstr { constr_index, .. } => { + let constr = arg_stack.pop().unwrap(); + + let mut term = arg_stack.pop().unwrap(); + + let error_term = apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Constant(UplcConstant::String( + "Asserted on incorrect constructor variant.".to_string(), + )), + ), + Term::Delay(Term::Error.into()), + ) + .force_wrap(); + + term = delayed_if_else( + apply_wrap( + apply_wrap( + DefaultFunction::EqualsInteger.into(), + Term::Constant(UplcConstant::Integer(constr_index as i128)), + ), + constr_index_exposer(constr), + ), + term, + error_term, + ); + + arg_stack.push(term); + } Air::When { subject_name, tipo, .. } => { diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index 6a16c937..249423ce 100644 --- a/examples/acceptance_tests/040/lib/tests.ak +++ b/examples/acceptance_tests/040/lib/tests.ak @@ -1,3 +1,9 @@ +pub type Door{ + angle: Int, + locked: Bool +} + + pub type Car { Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int } Ford { @@ -5,18 +11,19 @@ pub type Car { owner: ByteArray, wheels: Int, truck_bed_limit: Int, + car_doors: List } } // test update_owner2_should_fail(){ -// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000} +// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: []} // assert Honda{ owner, ..}: Car = initial_car // owner == #[] // } test update_owner1() { let initial_car: Data = - Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 } + Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] } assert Ford { owner, .. }: Car = initial_car owner == #[] }