From 7b023911af16d90dedbecdb3f9a9e1b5ff5b86b0 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 13 Jan 2023 02:46:02 -0500 Subject: [PATCH] add assert and record Record is a more efficient air for making records Assert guarantees a custom is of a certain variant. --- crates/aiken-lang/src/air.rs | 16 +- crates/aiken-lang/src/builder.rs | 32 ++-- crates/aiken-lang/src/uplc.rs | 190 +++++++++++++++++++-- examples/acceptance_tests/040/lib/tests.ak | 6 +- 4 files changed, 212 insertions(+), 32 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 163c4f4d..df78bf2c 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -59,7 +59,6 @@ pub enum Air { Call { scope: Vec, count: usize, - tipo: Arc, }, Builtin { @@ -82,6 +81,7 @@ pub enum Air { Assert { scope: Vec, + constr_index: usize, }, DefineFunc { @@ -155,13 +155,10 @@ pub enum Air { scope: Vec, }, - Constr { - scope: Vec, - count: usize, - }, - - Fields { + Record { scope: Vec, + constr_index: usize, + constr_type: Arc, count: usize, }, @@ -240,7 +237,7 @@ impl Air { | Air::Builtin { scope, .. } | Air::BinOp { scope, .. } | Air::Assignment { scope, .. } - | Air::Assert { scope } + | Air::Assert { scope, .. } | Air::DefineFunc { scope, .. } | Air::Lam { scope, .. } | Air::When { scope, .. } @@ -251,8 +248,7 @@ impl Air { | Air::Discard { scope } | Air::Finally { scope } | Air::If { scope, .. } - | Air::Constr { scope, .. } - | Air::Fields { scope, .. } + | Air::Record { scope, .. } | Air::RecordAccess { scope, .. } | Air::FieldsExpose { scope, .. } | Air::Tuple { scope, .. } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 27581398..61493261 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -1123,15 +1123,6 @@ pub fn monomorphize( } // TODO check on assignment if type is needed Air::Assignment { .. } => {} - 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::When { scope, tipo, @@ -1227,6 +1218,26 @@ pub fn monomorphize( needs_variant = true; } } + Air::Record { + scope, + constr_index, + constr_type, + count, + } => { + if constr_type.is_generic() { + let mut constr_type = constr_type.clone(); + find_generics_to_replace(&mut constr_type, &generic_types); + + new_air[index] = Air::Record { + scope, + constr_type, + constr_index, + count, + }; + needs_variant = true; + } + } + Air::RecordAccess { scope, index: record_index, @@ -1454,11 +1465,10 @@ pub fn handle_recursion_ir( let current_call = recursion_ir[index - 1].clone(); match current_call { - Air::Call { scope, count, tipo } => { + Air::Call { scope, count } => { 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 673eed99..5f69c35c 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -210,10 +210,39 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Call { fun, args, tipo, .. } => { + if let Some(data_type) = self.lookup_data_type_by_tipo(tipo) { + if let TypedExpr::Var { constructor, .. } = &**fun { + if let ValueConstructorVariant::Record { + name: constr_name, .. + } = &constructor.variant + { + let (constr_index, _) = data_type + .constructors + .iter() + .enumerate() + .find(|(_, dt)| &dt.name == constr_name) + .unwrap(); + + ir_stack.push(Air::Record { + scope: scope.clone(), + constr_index, + constr_type: constructor.tipo.clone(), + count: args.len(), + }); + + for arg in args { + let mut scope = scope.clone(); + scope.push(self.id_gen.next()); + self.build_ir(&arg.value, ir_stack, scope); + } + return; + } + } + } + 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()); @@ -250,7 +279,6 @@ impl<'a> CodeGenerator<'a> { tipo, .. } => { - let mut define_vec: Vec = vec![]; let mut value_vec: Vec = vec![]; let mut pattern_vec: Vec = vec![]; @@ -268,7 +296,6 @@ impl<'a> CodeGenerator<'a> { scope, ); - ir_stack.append(&mut define_vec); ir_stack.append(&mut pattern_vec); } TypedExpr::When { @@ -1405,12 +1432,81 @@ impl<'a> CodeGenerator<'a> { } Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), - Pattern::Discard { .. } => {} + Pattern::Discard { .. } => { + pattern_vec.push(Air::Assignment { + name: "_".to_string(), + scope, + }); + + pattern_vec.append(value_vec); + } list @ Pattern::List { .. } => { self.pattern_ir(list, pattern_vec, value_vec, tipo, scope); } - Pattern::Constructor { .. } => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); + Pattern::Constructor { + name: constr_name, + tipo, + .. + } => { + let data_type = self.lookup_data_type_by_tipo(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, scope); + } + AssignmentKind::Assert => { + let name_id = self.id_gen.next(); + pattern_vec.push(Air::Lam { + scope: scope.clone(), + name: format!("__constr_{}", name_id), + }); + + pattern_vec.append(value_vec); + + pattern_vec.push(Air::Assert { + 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: 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, + scope, + ); + } + AssignmentKind::Check => todo!(), + } } Pattern::Tuple { .. } => { self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); @@ -2959,8 +3055,31 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::Assert { .. } => { - todo!() + Air::Assert { constr_index, .. } => { + 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(); + + 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::DefineFunc { func_name, @@ -3382,8 +3501,59 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::Constr { .. } => todo!(), - Air::Fields { .. } => todo!(), + Air::Record { + constr_index, + constr_type, + count, + .. + } => { + let mut arg_vec = vec![]; + for _ in 0..count { + arg_vec.push(arg_stack.pop().unwrap()); + } + + let mut term = Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![])); + + for (index, arg) in arg_vec.iter().enumerate().rev() { + term = apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::MkCons).force_wrap(), + convert_type_to_data( + arg.clone(), + &constr_type.arg_types().unwrap()[index], + ), + ), + term, + ); + } + + term = apply_wrap( + apply_wrap( + DefaultFunction::ConstrData.into(), + Term::Constant(UplcConstant::Integer(constr_index as i128)), + ), + term, + ); + + if arg_vec.iter().all(|item| matches!(item, Term::Constant(_))) { + let mut program: Program = Program { + version: (1, 0, 0), + term, + }; + + let mut interner = Interner::new(); + + interner.program(&mut program); + + let eval_program: Program = program.try_into().unwrap(); + + let evaluated_term: Term = + eval_program.eval(ExBudget::default()).0.unwrap(); + term = evaluated_term.try_into().unwrap(); + } + + arg_stack.push(term); + } Air::RecordAccess { index, tipo, .. } => { let constr = arg_stack.pop().unwrap(); diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index f13c4e76..097917f2 100644 --- a/examples/acceptance_tests/040/lib/tests.ak +++ b/examples/acceptance_tests/040/lib/tests.ak @@ -7,10 +7,14 @@ 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 +// owner == #[] +// } test update_owner1(){ let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000} assert Ford{ owner, ..} = initial_car owner == #[] - }