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 == #[] }