diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 6b845114..1b5d1262 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,47 @@ 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 + Let { scope: Vec, name: String, }, + UnWrapData { + scope: Vec, + tipo: Arc, + }, + + AssertConstr { + scope: Vec, + constr_index: usize, + }, + + // When When { scope: Vec, tipo: Arc, @@ -153,18 +161,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 +179,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, @@ -180,14 +195,32 @@ pub enum Air { FieldsExpose { scope: Vec, - count: usize, indices: Vec<(usize, String, Arc)>, + check_last_item: bool, }, - Tuple { + // ListAccess + ListAccessor { scope: Vec, tipo: Arc, - count: usize, + names: Vec, + tail: bool, + check_last_item: 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, + check_last_item: bool, }, TupleIndex { @@ -196,6 +229,7 @@ pub enum Air { tuple_index: usize, }, + // Misc. Todo { scope: Vec, label: Option, @@ -213,23 +247,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,68 +256,142 @@ 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::Assert { scope, .. } - | Air::DefineFunc { scope, .. } - | Air::Lam { scope, .. } + | Air::UnOp { scope, .. } + | Air::Let { scope, .. } + | Air::UnWrapData { scope, .. } + | Air::AssertConstr { 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::UnWrapData { tipo, .. } | Air::When { tipo, .. } | Air::Clause { tipo, .. } | 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::Let { .. } + | Air::WrapClause { .. } + | Air::AssertConstr { .. } + | 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..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, if_else}, + builder::{apply_wrap, delayed_choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -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 { @@ -475,108 +481,22 @@ 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(); + if let Some((first, names)) = names.split_first() { + let (current_tipo, tipos) = tipos.split_first().unwrap(); - let head_list = if 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(), }), - ), - &tipo.clone().get_inner_types()[0], - ) - }; - - 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 - // 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]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: term.into(), - }, + } else { + convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var(Name { @@ -584,40 +504,135 @@ pub fn list_access_to_uplc( unique: 0.into(), }), ), + ¤t_tipo.to_owned(), ) - .into(), + }; + + 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() { + 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: 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, tipo), - 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 } } @@ -1072,6 +1087,7 @@ pub fn monomorphize( tipo, names, tail, + check_last_item, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); @@ -1082,6 +1098,7 @@ pub fn monomorphize( names, tipo, tail, + check_last_item, }; needs_variant = true; } @@ -1133,8 +1150,15 @@ pub fn monomorphize( needs_variant = true; } } - // TODO check on assignment if type is needed - Air::Assignment { .. } => {} + 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, @@ -1346,8 +1370,8 @@ pub fn monomorphize( } Air::FieldsExpose { scope, - count, indices, + check_last_item, } => { let mut new_indices = vec![]; for (ind, name, tipo) in indices { @@ -1362,16 +1386,18 @@ pub fn monomorphize( } new_air[index] = Air::FieldsExpose { scope, - count, indices: new_indices, + check_last_item, }; } Air::RecordUpdate { 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,18 +1408,51 @@ 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 } => { + 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; + } + } + + 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 +1570,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/tipo.rs b/crates/aiken-lang/src/tipo.rs index 1ddbcac5..fa8f31e3 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, .. } => { @@ -205,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, } } @@ -467,6 +476,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, @@ -483,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/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 69df1453..3aeea6de 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1,12 +1,13 @@ -use std::{sync::Arc, vec}; +use std::sync::Arc; use indexmap::{IndexMap, IndexSet}; 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, 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, }, @@ -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, - replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, FuncComponents, - FunctionAccessKey, + replace_opaque_type, wrap_validator_args, AssignmentProperties, ClauseProperties, + DataTypeKey, FuncComponents, FunctionAccessKey, }, expr::TypedExpr, tipo::{ - self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, + 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(), } } @@ -92,13 +95,19 @@ 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 }; term = wrap_validator_args(term, arguments); + term = if wrap_as_validator || self.used_data_assert_on_list { + assert_on_list(term) + } else { + term + }; + let mut program = Program { version: (1, 0, 0), term, @@ -242,6 +251,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()); @@ -284,6 +294,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( @@ -291,7 +303,10 @@ impl<'a> CodeGenerator<'a> { &mut pattern_vec, &mut value_vec, tipo, - *kind, + AssignmentProperties { + value_is_data, + kind: *kind, + }, scope, ); @@ -305,104 +320,129 @@ impl<'a> CodeGenerator<'a> { // assuming one subject at the moment let subject = subjects[0].clone(); + if clauses.len() <= 1 { + 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::Lam { - 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, final_else, + tipo, .. } => { let mut if_ir = vec![]; @@ -414,10 +454,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()); @@ -453,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(), @@ -506,7 +550,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 +577,7 @@ impl<'a> CodeGenerator<'a> { scope, highest_index, indices: index_types, + tipo: tipo.clone(), }); ir_stack.append(&mut update_ir); @@ -753,7 +800,7 @@ impl<'a> CodeGenerator<'a> { fn when_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, @@ -769,12 +816,12 @@ 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::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(), }); @@ -792,10 +839,10 @@ 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::Lam { + new_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); @@ -824,7 +871,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 +884,7 @@ impl<'a> CodeGenerator<'a> { } *clause_properties.needs_constr_var() = false; - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -951,7 +998,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, @@ -959,13 +1006,13 @@ 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::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } @@ -993,7 +1040,7 @@ impl<'a> CodeGenerator<'a> { tail_name = name.clone(); } Pattern::Discard { .. } => {} - _ => todo!(), + _ => unreachable!("Patterns in tail of list should not allow this"), } } @@ -1100,7 +1147,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)| { @@ -1109,6 +1155,7 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } else { @@ -1137,7 +1184,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)| { @@ -1147,6 +1193,7 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } @@ -1198,7 +1245,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(), }); @@ -1233,7 +1280,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, @@ -1254,7 +1301,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 +1347,7 @@ impl<'a> CodeGenerator<'a> { inverse: true, }); - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -1429,33 +1476,61 @@ 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!(), } } fn assignment_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, - kind: AssignmentKind, + assignment_properties: AssignmentProperties, scope: Vec, ) { + if assignment_properties.value_is_data && !tipo.is_data() && !pattern.is_discard() { + value_vec.insert( + 0, + Air::UnWrapData { + scope: scope.clone(), + tipo: tipo.clone().into(), + }, + ); + } match pattern { Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: name.clone(), - scope, + scope: scope.clone(), }); 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); + } } - Pattern::VarUsage { .. } => todo!(), - Pattern::Assign { .. } => todo!(), + Pattern::VarUsage { .. } => unreachable!(), + Pattern::Assign { .. } => todo!("Assign not yet implemented yet"), Pattern::Discard { .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: "_".to_string(), scope, }); @@ -1463,85 +1538,87 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(value_vec); } list @ Pattern::List { .. } => { - self.pattern_ir(list, pattern_vec, value_vec, tipo, 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, 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!(), + 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, + ); } } - Pattern::Tuple { .. } => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, 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, + ); + } } } } fn pattern_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, + assignment_properties: AssignmentProperties, scope: Vec, ) { match pattern { @@ -1550,21 +1627,19 @@ impl<'a> CodeGenerator<'a> { Pattern::Var { .. } => todo!(), Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), - Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); - - pattern_vec.append(values); - } + 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, .. } => { 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()); @@ -1590,10 +1665,17 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], + assignment_properties.clone(), scope.clone(), ); } - _ => todo!(), + Pattern::Int { .. } => todo!(), + Pattern::String { .. } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + Pattern::Discard { .. } => { + names.push("_".to_string()); + } } } @@ -1605,230 +1687,883 @@ impl<'a> CodeGenerator<'a> { } } - pattern_vec.push(Air::ListAccessor { - names, - tail: tail.is_some(), - scope, - tipo: tipo.clone().into(), - }); + if matches!(&assignment_properties.kind, AssignmentKind::Assert) { + } else { + pattern_vec.push(Air::ListAccessor { + names, + tail: tail.is_some(), + scope, + tipo: tipo.clone().into(), + check_last_item: false, + }); + } pattern_vec.append(values); 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 { - tipo::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, - 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, + type_map.get(field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .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, - 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); 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(); + 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.append(values); + + pattern_vec.append(&mut nested_pattern); + } + } + } + + 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, .. } => { + 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 { .. } => 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 elems { + for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } - Pattern::Discard { .. } => { - names.push("_".to_string()); + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + l @ (Pattern::List { .. } + | Pattern::Constructor { .. } + | Pattern::Tuple { .. }) => { + 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(), + ); } - 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 { + _ => {} + } + } + + 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 { + arguments, + constructor, + name: constr_name, + tipo, + .. + } => { + 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 (index, data_type_constr) = data_type + .constructors + .iter() + .enumerate() + .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); + } + + 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, + type_map.get(field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .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(current_index).unwrap().clone()); + current_index += 1; + } else { + 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}_{}", id_next), + scope.clone(), + ) + } + } + + 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 + .iter() + .map(|(var_name, index)| { + let field_type = type_map.get(index).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) + .collect_vec(), + scope: scope.clone(), + check_last_item: true, + }); + + 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); + } + 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, + type_map.get(&field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .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 { + 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!("__tuple_{index}_{}", id_next), + scope.clone(), + ) + } + } + + if !final_args.is_empty() { + pattern_vec.push(Air::TupleAccessor { + scope, + 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); + } + } + } + + 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() + || tipo.get_generic().is_some() + { + } 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: 2, + 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(), + 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( + &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.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 inner_list_type = &tipo.get_inner_types()[0]; + + 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: 2, + 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::Let { + scope: scope.clone(), + name: format!("__list_item_{}", new_id), + }); + + assert_vec.push(Air::UnWrapData { + scope: scope.clone(), + 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_list_type, + assert_vec, + &format!("__list_item_{}", new_id), + scope.clone(), + ); + + 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: Some("Constr index did not match any type variant".to_string()), + }); + } + } + + fn extract_arg_and_index( + &mut self, + item: &Pattern>, + field_index: usize, + nested_pattern: &mut Vec, + tipo: &Type, + assignment_properties: &AssignmentProperties, + scope: &[u64], + ) -> Option<(String, usize)> { + { + let (discard, var_name) = match item { + Pattern::Var { name, .. } => (false, name.clone()), + Pattern::Discard { .. } => (true, "".to_string()), + 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( - Type::App { - public: true, - module: String::new(), - name: String::new(), - args: vec![], - } - .into(), + tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), - name: item_name, - scope: scope.clone(), + name: list_name.clone(), variant_name: String::new(), - }); - self.pattern_ir( - a, - &mut elements_vec, - &mut var_vec, - &tipo.get_inner_types()[0], - scope.clone(), - ); - } - _ => todo!(), + }], + 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(), + ); } - } - pattern_vec.push(Air::TupleAccessor { - names, - scope, - tipo: tipo.clone().into(), - }); - pattern_vec.append(values); - pattern_vec.append(&mut elements_vec); + (false, list_name) + } + a @ Pattern::Constructor { + tipo, + name: constr_name, + .. + } => { + let id = self.id_gen.next(); + let constr_name = format!("{constr_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(), + 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) + } + 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 { + None + } else { + Some((var_name, field_index)) } } } @@ -2339,6 +3074,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()); @@ -2348,6 +3084,7 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, names, tail, + check_last_item, }; } Air::ListExpose { @@ -2548,7 +3285,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()); @@ -2556,12 +3298,14 @@ impl<'a> CodeGenerator<'a> { scope, names, tipo: replaced_type, + check_last_item, }; } Air::RecordUpdate { highest_index, indices, scope, + tipo, } => { let mut new_indices = vec![]; for (ind, tipo) in indices { @@ -2574,6 +3318,7 @@ impl<'a> CodeGenerator<'a> { scope, indices: new_indices, highest_index, + tipo, }; } Air::Record { @@ -2628,15 +3373,15 @@ impl<'a> CodeGenerator<'a> { } } Air::FieldsExpose { - count, indices, scope, + check_last_item, } => { let record = ir_stack[index + 1].clone(); 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(), }; @@ -2651,7 +3396,7 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } else { @@ -2665,10 +3410,38 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } + 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, + }; + } + 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, + }; + } _ => {} } } @@ -2746,10 +3519,7 @@ impl<'a> CodeGenerator<'a> { })); } ValueConstructorVariant::Record { - name: constr_name, - field_map, - arity, - .. + name: constr_name, .. } => { if constructor.tipo.is_bool() { arg_stack @@ -2770,59 +3540,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( @@ -2832,43 +3553,12 @@ 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); } } }; } - Air::Discard { .. } => { + Air::Void { .. } => { arg_stack.push(Term::Constant(UplcConstant::Unit)); } Air::List { @@ -2958,7 +3648,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(); @@ -2995,6 +3689,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 { @@ -3014,7 +3715,8 @@ impl<'a> CodeGenerator<'a> { tail, current_index, term, - &tipo, + inner_types, + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), @@ -3461,49 +4163,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(); - - 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, params, @@ -3586,7 +4245,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(); @@ -3604,6 +4263,43 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + Air::UnWrapData { tipo, .. } => { + let mut term = arg_stack.pop().unwrap(); + + term = convert_data_to_type(term, &tipo); + + 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, .. } => { @@ -4066,162 +4762,89 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::FieldsExpose { indices, .. } => { + 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(), - }) - }; + 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 + }; - 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: tail_list.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![]; @@ -4506,7 +5129,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(); @@ -4605,7 +5233,8 @@ impl<'a> CodeGenerator<'a> { false, current_index, term, - &tipo, + tipo.get_inner_types(), + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), 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 { 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}'")] diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index 554bde33..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} -// assert Honda{ owner, ..} = initial_car +// 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 } - assert Ford { owner, .. } = initial_car + Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] } + assert Ford { owner, .. }: Car = initial_car owner == #[] }