diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 904c05b7..b314a7ba 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -299,6 +299,19 @@ pub struct FunctionAccessKey { pub type TypedDataType = DataType>; impl TypedDataType { + pub fn data() -> Self { + DataType { + constructors: vec![], + doc: None, + location: Span::empty(), + name: "Data".to_string(), + opaque: false, + parameters: vec![], + public: true, + typed_parameters: vec![], + } + } + pub fn bool() -> Self { DataType { constructors: vec![ diff --git a/crates/aiken-lang/src/builtins.rs b/crates/aiken-lang/src/builtins.rs index e8714020..c0a64a5f 100644 --- a/crates/aiken-lang/src/builtins.rs +++ b/crates/aiken-lang/src/builtins.rs @@ -1211,6 +1211,16 @@ pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap IndexMap { let mut data_types = IndexMap::new(); + // Data + let data_data_type = TypedDataType::data(); + data_types.insert( + DataTypeKey { + module_name: "".to_string(), + defined_type: "Data".to_string(), + }, + data_data_type, + ); + // Ordering let ordering_data_type = TypedDataType::ordering(); data_types.insert( diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 5b490979..81798458 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -7,16 +7,20 @@ use crate::{ UntypedRecordUpdateArg, }, builtins::void, - gen_uplc::builder::{ - check_replaceable_opaque_type, convert_opaque_type, lookup_data_type_by_tipo, - }, parser::token::Base, - tipo::{ModuleValueConstructor, PatternConstructor, Type, TypeVar, ValueConstructor}, + tipo::{ + check_replaceable_opaque_type, convert_opaque_type, lookup_data_type_by_tipo, + ModuleValueConstructor, PatternConstructor, Type, TypeVar, ValueConstructor, + }, }; use indexmap::IndexMap; use pallas::ledger::primitives::alonzo::{Constr, PlutusData}; -use std::rc::Rc; -use uplc::{ast::Data, machine::value::from_pallas_bigint, KeyValuePairs}; +use std::{fmt::Debug, rc::Rc}; +use uplc::{ + ast::Data, + machine::{runtime::convert_tag_to_constr, value::from_pallas_bigint}, + KeyValuePairs, +}; use vec1::Vec1; #[derive(Debug, Clone, PartialEq)] @@ -578,135 +582,18 @@ pub const DEFAULT_TODO_STR: &str = "aiken::todo"; pub const DEFAULT_ERROR_STR: &str = "aiken::error"; impl UntypedExpr { + // Reify some opaque 'Constant' into an 'UntypedExpr', using a Type annotation. We also need + // an extra map to lookup record & enum constructor's names as they're completely erased when + // in their PlutusData form, and the Type annotation only contains type name. + // + // The function performs some sanity check to ensure that the type does indeed somewhat + // correspond to the data being given. pub fn reify_constant( data_types: &IndexMap<&DataTypeKey, &TypedDataType>, cst: uplc::ast::Constant, tipo: &Type, ) -> Result { - if let Type::Var { tipo } = tipo { - if let TypeVar::Link { tipo } = &*tipo.borrow() { - return UntypedExpr::reify_constant(data_types, cst, tipo); - } - } - - match cst { - uplc::ast::Constant::Data(data) => UntypedExpr::reify_data(data_types, data, tipo), - - uplc::ast::Constant::Integer(i) => { - UntypedExpr::reify_data(data_types, Data::integer(i), tipo) - } - - uplc::ast::Constant::ByteString(bytes) => { - UntypedExpr::reify_data(data_types, Data::bytestring(bytes), tipo) - } - - uplc::ast::Constant::ProtoList(_, args) => match tipo { - Type::App { - module, - name, - args: type_args, - .. - } if module.is_empty() && name.as_str() == "List" => { - if let [inner] = &type_args[..] { - Ok(UntypedExpr::List { - location: Span::empty(), - elements: args - .into_iter() - .map(|arg| UntypedExpr::reify_constant(data_types, arg, inner)) - .collect::, _>>()?, - tail: None, - }) - } else { - Err( - "invalid List type annotation: the list has multiple type-parameters." - .to_string(), - ) - } - } - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { - location: Span::empty(), - elems: args - .into_iter() - .zip(elems) - .map(|(arg, arg_type)| { - UntypedExpr::reify_constant(data_types, arg, arg_type) - }) - .collect::, _>>()?, - }), - _ => Err(format!( - "invalid type annotation. expected List but got: {tipo:?}" - )), - }, - - uplc::ast::Constant::ProtoPair(_, _, left, right) => match tipo { - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { - location: Span::empty(), - elems: [left.as_ref(), right.as_ref()] - .into_iter() - .zip(elems) - .map(|(arg, arg_type)| { - UntypedExpr::reify_constant(data_types, arg.to_owned(), arg_type) - }) - .collect::, _>>()?, - }), - _ => Err(format!( - "invalid type annotation. expected Tuple but got: {tipo:?}" - )), - }, - - uplc::ast::Constant::Unit => Ok(UntypedExpr::Var { - location: Span::empty(), - name: "Void".to_string(), - }), - - uplc::ast::Constant::Bool(is_true) => Ok(UntypedExpr::Var { - location: Span::empty(), - name: if is_true { "True" } else { "False" }.to_string(), - }), - - uplc::ast::Constant::String(value) => Ok(UntypedExpr::String { - location: Span::empty(), - value, - }), - - uplc::ast::Constant::Bls12_381G1Element(pt) => Ok(UntypedExpr::CurvePoint { - location: Span::empty(), - point: Curve::Bls12_381(Bls12_381Point::G1(*pt)).into(), - preferred_format: ByteArrayFormatPreference::HexadecimalString, - }), - - uplc::ast::Constant::Bls12_381G2Element(pt) => Ok(UntypedExpr::CurvePoint { - location: Span::empty(), - point: Curve::Bls12_381(Bls12_381Point::G2(*pt)).into(), - preferred_format: ByteArrayFormatPreference::HexadecimalString, - }), - - uplc::ast::Constant::Bls12_381MlResult(ml) => { - let mut bytes = Vec::new(); - - bytes.extend((*ml).to_bendian()); - - // NOTE: We don't actually have a syntax for representing MillerLoop results, so we - // just fake it as a constructor with a bytearray. Note also that the bytearray is - // *large*. - Ok(UntypedExpr::Call { - location: Span::empty(), - arguments: vec![CallArg { - label: None, - location: Span::empty(), - value: UntypedExpr::ByteArray { - location: Span::empty(), - bytes, - preferred_format: ByteArrayFormatPreference::HexadecimalString, - }, - }], - fun: Box::new(UntypedExpr::Var { - name: "MillerLoopResult".to_string(), - location: Span::empty(), - }), - }) - } - } + UntypedExpr::do_reify_constant(&mut IndexMap::new(), data_types, cst, tipo) } // Reify some opaque 'PlutusData' into an 'UntypedExpr', using a Type annotation. We also need @@ -720,9 +607,36 @@ impl UntypedExpr { data: PlutusData, tipo: &Type, ) -> Result { - if let Type::Var { tipo } = tipo { - if let TypeVar::Link { tipo } = &*tipo.borrow() { - return UntypedExpr::reify_data(data_types, data, tipo); + UntypedExpr::do_reify_data(&mut IndexMap::new(), data_types, data, tipo) + } + + fn reify_with( + generics: &mut IndexMap>, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + t: T, + tipo: &Type, + with: F, + ) -> Result + where + T: Debug, + F: Fn( + &mut IndexMap>, + &IndexMap<&DataTypeKey, &TypedDataType>, + T, + &Type, + ) -> Result, + { + if let Type::Var { tipo: var_tipo } = tipo { + match &*var_tipo.borrow() { + TypeVar::Link { tipo } => { + return Self::reify_with(generics, data_types, t, tipo, with); + } + TypeVar::Generic { id } => { + if let Some(tipo) = generics.get(id) { + return Self::reify_with(generics, data_types, t, &tipo.clone(), with); + } + } + _ => unreachable!("unbound type during reification {tipo:?} -> {t:?}"), } } @@ -740,7 +654,7 @@ impl UntypedExpr { let inner_type = convert_opaque_type(&tipo.clone().into(), data_types, false); - let value = UntypedExpr::reify_data(data_types, data, &inner_type)?; + let value = Self::reify_with(generics, data_types, t, &inner_type, with)?; return Ok(UntypedExpr::Call { location: Span::empty(), @@ -756,121 +670,176 @@ impl UntypedExpr { }); } + with(generics, data_types, t, tipo) + } + + fn do_reify_constant( + generics: &mut IndexMap>, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + cst: uplc::ast::Constant, + tipo: &Type, + ) -> Result { + Self::reify_with( + generics, + data_types, + cst, + tipo, + |generics, data_types, cst, tipo| match cst { + uplc::ast::Constant::Data(data) => { + UntypedExpr::do_reify_data(generics, data_types, data, tipo) + } + + uplc::ast::Constant::Integer(i) => { + UntypedExpr::do_reify_data(generics, data_types, Data::integer(i), tipo) + } + + uplc::ast::Constant::ByteString(bytes) => { + UntypedExpr::do_reify_data(generics, data_types, Data::bytestring(bytes), tipo) + } + + uplc::ast::Constant::ProtoList(_, args) => match tipo { + Type::App { + module, + name, + args: type_args, + .. + } if module.is_empty() && name.as_str() == "List" => { + if let [inner] = &type_args[..] { + Ok(UntypedExpr::List { + location: Span::empty(), + elements: args + .into_iter() + .map(|arg| { + UntypedExpr::do_reify_constant( + generics, data_types, arg, inner, + ) + }) + .collect::, _>>()?, + tail: None, + }) + } else { + Err( + "invalid List type annotation: the list has multiple type-parameters." + .to_string(), + ) + } + } + Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + location: Span::empty(), + elems: args + .into_iter() + .zip(elems) + .map(|(arg, arg_type)| { + UntypedExpr::do_reify_constant(generics, data_types, arg, arg_type) + }) + .collect::, _>>()?, + }), + _ => Err(format!( + "invalid type annotation. expected List but got: {tipo:?}" + )), + }, + + uplc::ast::Constant::ProtoPair(_, _, left, right) => match tipo { + Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + location: Span::empty(), + elems: [left.as_ref(), right.as_ref()] + .into_iter() + .zip(elems) + .map(|(arg, arg_type)| { + UntypedExpr::do_reify_constant( + generics, + data_types, + arg.to_owned(), + arg_type, + ) + }) + .collect::, _>>()?, + }), + _ => Err(format!( + "invalid type annotation. expected Tuple but got: {tipo:?}" + )), + }, + + uplc::ast::Constant::Unit => Ok(UntypedExpr::Var { + location: Span::empty(), + name: "Void".to_string(), + }), + + uplc::ast::Constant::Bool(is_true) => Ok(UntypedExpr::Var { + location: Span::empty(), + name: if is_true { "True" } else { "False" }.to_string(), + }), + + uplc::ast::Constant::String(value) => Ok(UntypedExpr::String { + location: Span::empty(), + value, + }), + + uplc::ast::Constant::Bls12_381G1Element(pt) => Ok(UntypedExpr::CurvePoint { + location: Span::empty(), + point: Curve::Bls12_381(Bls12_381Point::G1(*pt)).into(), + preferred_format: ByteArrayFormatPreference::HexadecimalString, + }), + + uplc::ast::Constant::Bls12_381G2Element(pt) => Ok(UntypedExpr::CurvePoint { + location: Span::empty(), + point: Curve::Bls12_381(Bls12_381Point::G2(*pt)).into(), + preferred_format: ByteArrayFormatPreference::HexadecimalString, + }), + + uplc::ast::Constant::Bls12_381MlResult(ml) => { + let mut bytes = Vec::new(); + + bytes.extend((*ml).to_bendian()); + + // NOTE: We don't actually have a syntax for representing MillerLoop results, so we + // just fake it as a constructor with a bytearray. Note also that the bytearray is + // *large*. + Ok(UntypedExpr::Call { + location: Span::empty(), + arguments: vec![CallArg { + label: None, + location: Span::empty(), + value: UntypedExpr::ByteArray { + location: Span::empty(), + bytes, + preferred_format: ByteArrayFormatPreference::HexadecimalString, + }, + }], + fun: Box::new(UntypedExpr::Var { + name: "MillerLoopResult".to_string(), + location: Span::empty(), + }), + }) + } + }, + ) + } + + fn reify_blind(data: PlutusData) -> Self { match data { - PlutusData::BigInt(ref i) => Ok(UntypedExpr::UInt { + PlutusData::BigInt(ref i) => UntypedExpr::UInt { location: Span::empty(), base: Base::Decimal { numeric_underscore: false, }, value: from_pallas_bigint(i).to_string(), - }), + }, - PlutusData::BoundedBytes(bytes) => Ok(UntypedExpr::ByteArray { + PlutusData::BoundedBytes(bytes) => UntypedExpr::ByteArray { location: Span::empty(), bytes: bytes.into(), preferred_format: ByteArrayFormatPreference::HexadecimalString, - }), - - PlutusData::Array(args) => match tipo { - Type::App { - module, - name, - args: type_args, - .. - } if module.is_empty() && name.as_str() == "List" => { - if let [inner] = &type_args[..] { - Ok(UntypedExpr::List { - location: Span::empty(), - elements: args - .into_iter() - .map(|arg| UntypedExpr::reify_data(data_types, arg, inner)) - .collect::, _>>()?, - tail: None, - }) - } else { - Err( - "invalid List type annotation: the list has multiple type-parameters." - .to_string(), - ) - } - } - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { - location: Span::empty(), - elems: args - .into_iter() - .zip(elems) - .map(|(arg, arg_type)| UntypedExpr::reify_data(data_types, arg, arg_type)) - .collect::, _>>()?, - }), - _ => Err(format!( - "invalid type annotation. expected List but got: {tipo:?}" - )), }, - PlutusData::Constr(Constr { - tag, - any_constructor, - fields, - }) => { - let ix = if tag == 102 { - any_constructor.unwrap() as usize - } else if tag < 128 { - tag as usize - 121 - } else { - tag as usize - 1280 + 7 - }; - - if let Type::App { .. } = tipo { - if let Some(DataType { constructors, .. }) = - lookup_data_type_by_tipo(data_types, tipo) - { - let constructor = &constructors[ix]; - - return if fields.is_empty() { - Ok(UntypedExpr::Var { - location: Span::empty(), - name: constructor.name.to_string(), - }) - } else { - let arguments = fields - .into_iter() - .zip(constructor.arguments.iter()) - .map( - |( - field, - RecordConstructorArg { - ref label, - ref tipo, - .. - }, - )| { - UntypedExpr::reify_data(data_types, field, tipo).map( - |value| CallArg { - label: label.clone(), - location: Span::empty(), - value, - }, - ) - }, - ) - .collect::, _>>()?; - - Ok(UntypedExpr::Call { - location: Span::empty(), - arguments, - fun: Box::new(UntypedExpr::Var { - name: constructor.name.to_string(), - location: Span::empty(), - }), - }) - }; - } - } - - Err(format!( - "invalid type annotation {tipo:?} for constructor: {tag:?} with {fields:?}" - )) - } + PlutusData::Array(elems) => UntypedExpr::List { + location: Span::empty(), + elements: elems + .into_iter() + .map(UntypedExpr::reify_blind) + .collect::>(), + tail: None, + }, PlutusData::Map(indef_or_def) => { let kvs = match indef_or_def { @@ -878,19 +847,229 @@ impl UntypedExpr { KeyValuePairs::Indef(kvs) => kvs, }; - UntypedExpr::reify_data( - data_types, - PlutusData::Array( - kvs.into_iter() - .map(|(k, v)| PlutusData::Array(vec![k, v])) - .collect(), - ), - tipo, - ) + UntypedExpr::List { + location: Span::empty(), + elements: kvs + .into_iter() + .map(|(k, v)| UntypedExpr::Tuple { + location: Span::empty(), + elems: vec![UntypedExpr::reify_blind(k), UntypedExpr::reify_blind(v)], + }) + .collect::>(), + tail: None, + } + } + + PlutusData::Constr(Constr { + tag, + any_constructor, + fields, + }) => { + let ix = convert_tag_to_constr(tag).or(any_constructor).unwrap() as usize; + + let fields = fields + .into_iter() + .map(|field| CallArg { + location: Span::empty(), + label: None, + value: UntypedExpr::reify_blind(field), + }) + .collect::>(); + + let mut arguments = vec![CallArg { + location: Span::empty(), + label: None, + value: UntypedExpr::UInt { + location: Span::empty(), + value: ix.to_string(), + base: Base::Decimal { + numeric_underscore: false, + }, + }, + }]; + arguments.extend(fields); + + UntypedExpr::Call { + location: Span::empty(), + arguments, + fun: UntypedExpr::Var { + name: "Constr".to_string(), + location: Span::empty(), + } + .into(), + } } } } + fn do_reify_data( + generics: &mut IndexMap>, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + data: PlutusData, + tipo: &Type, + ) -> Result { + if let Type::App { name, module, .. } = tipo { + if module.is_empty() && name == "Data" { + return Ok(Self::reify_blind(data)); + } + } + + Self::reify_with( + generics, + data_types, + data, + tipo, + |generics, data_types, data, tipo| match data { + PlutusData::BigInt(ref i) => Ok(UntypedExpr::UInt { + location: Span::empty(), + base: Base::Decimal { + numeric_underscore: false, + }, + value: from_pallas_bigint(i).to_string(), + }), + + PlutusData::BoundedBytes(bytes) => Ok(UntypedExpr::ByteArray { + location: Span::empty(), + bytes: bytes.into(), + preferred_format: ByteArrayFormatPreference::HexadecimalString, + }), + + PlutusData::Array(args) => match tipo { + Type::App { + module, + name, + args: type_args, + .. + } if module.is_empty() && name.as_str() == "List" => { + if let [inner] = &type_args[..] { + Ok(UntypedExpr::List { + location: Span::empty(), + elements: args + .into_iter() + .map(|arg| { + UntypedExpr::do_reify_data(generics, data_types, arg, inner) + }) + .collect::, _>>()?, + tail: None, + }) + } else { + Err( + "invalid List type annotation: the list has multiple type-parameters." + .to_string(), + ) + } + } + Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + location: Span::empty(), + elems: args + .into_iter() + .zip(elems) + .map(|(arg, arg_type)| { + UntypedExpr::do_reify_data(generics, data_types, arg, arg_type) + }) + .collect::, _>>()?, + }), + _ => Err(format!( + "invalid type annotation. expected List but got: {tipo:?}" + )), + }, + + PlutusData::Constr(Constr { + tag, + any_constructor, + fields, + }) => { + let ix = convert_tag_to_constr(tag).or(any_constructor).unwrap() as usize; + + if let Type::App { args, .. } = tipo { + if let Some(DataType { + constructors, + typed_parameters, + .. + }) = lookup_data_type_by_tipo(data_types, tipo) + { + let constructor = &constructors[ix]; + + typed_parameters + .iter() + .zip(args) + .for_each(|(generic, arg)| { + if let Some(ix) = generic.get_generic() { + if !generics.contains_key(&ix) { + generics.insert(ix, arg.clone()); + } + } + }); + + return if fields.is_empty() { + Ok(UntypedExpr::Var { + location: Span::empty(), + name: constructor.name.to_string(), + }) + } else { + let arguments = + fields + .into_iter() + .zip(constructor.arguments.iter()) + .map( + |( + field, + RecordConstructorArg { + ref label, + ref tipo, + .. + }, + )| { + UntypedExpr::do_reify_data( + generics, data_types, field, tipo, + ) + .map(|value| CallArg { + label: label.clone(), + location: Span::empty(), + value, + }) + }, + ) + .collect::, _>>()?; + + Ok(UntypedExpr::Call { + location: Span::empty(), + arguments, + fun: Box::new(UntypedExpr::Var { + name: constructor.name.to_string(), + location: Span::empty(), + }), + }) + }; + } + } + + Err(format!( + "invalid type annotation {tipo:?} for constructor: {tag:?} with {fields:?}" + )) + } + + PlutusData::Map(indef_or_def) => { + let kvs = match indef_or_def { + KeyValuePairs::Def(kvs) => kvs, + KeyValuePairs::Indef(kvs) => kvs, + }; + + UntypedExpr::do_reify_data( + generics, + data_types, + PlutusData::Array( + kvs.into_iter() + .map(|(k, v)| PlutusData::Array(vec![k, v])) + .collect(), + ), + tipo, + ) + } + }, + ) + } + pub fn todo(reason: Option, location: Span) -> Self { UntypedExpr::Trace { location, diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 674254ad..f87c8fc8 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -6,9 +6,8 @@ use self::{ air::Air, builder::{ air_holds_msg, cast_validator_args, constants_ir, convert_type_to_data, extract_constant, - lookup_data_type_by_tipo, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, - AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, - HoistableFunction, Variant, + modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, AssignmentProperties, + ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant, }, tree::{AirMsg, AirTree, TreePath}, }; @@ -23,15 +22,16 @@ use crate::{ gen_uplc::{ air::ExpectLevel, builder::{ - check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations, - find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name, - get_generic_id_and_type, get_generic_variant_name, get_line_columns_by_span, - get_src_code_by_span, known_data_to_type, monomorphize, pattern_has_conditions, - wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause, + erase_opaque_type_operations, find_list_clause_or_default_first, + get_generic_variant_name, get_line_columns_by_span, get_src_code_by_span, + known_data_to_type, monomorphize, pattern_has_conditions, wrap_as_multi_validator, + wrap_validator_condition, CodeGenFunction, SpecificClause, }, }, line_numbers::LineNumbers, tipo::{ + check_replaceable_opaque_type, convert_opaque_type, find_and_replace_generics, + get_arg_type_name, get_generic_id_and_type, lookup_data_type_by_tipo, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, @@ -3905,9 +3905,11 @@ impl<'a> CodeGenerator<'a> { } else if constructor.tipo.is_void() { Some(Term::Constant(UplcConstant::Unit.into())) } else { - let data_type = - builder::lookup_data_type_by_tipo(&self.data_types, &constructor.tipo) - .unwrap(); + let data_type = crate::tipo::lookup_data_type_by_tipo( + &self.data_types, + &constructor.tipo, + ) + .unwrap(); let (constr_index, constr_type) = data_type .constructors diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index b4b9d0aa..b5278ed7 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -4,14 +4,18 @@ use super::{ }; use crate::{ ast::{ - AssignmentKind, BinOp, ClauseGuard, Constant, DataType, DataTypeKey, FunctionAccessKey, - Pattern, Span, TraceLevel, TypedArg, TypedClause, TypedClauseGuard, TypedDataType, - TypedPattern, UnOp, + AssignmentKind, BinOp, ClauseGuard, Constant, DataTypeKey, FunctionAccessKey, Pattern, + Span, TraceLevel, TypedArg, TypedClause, TypedClauseGuard, TypedDataType, TypedPattern, + UnOp, }, builtins::{bool, data, function, int, list, string, void}, expr::TypedExpr, line_numbers::{LineColumn, LineNumbers}, - tipo::{PatternConstructor, Type, TypeVar, ValueConstructor, ValueConstructorVariant}, + tipo::{ + check_replaceable_opaque_type, convert_opaque_type, find_and_replace_generics, + lookup_data_type_by_tipo, PatternConstructor, Type, ValueConstructor, + ValueConstructorVariant, + }, }; use indexmap::{IndexMap, IndexSet}; use itertools::{Itertools, Position}; @@ -274,239 +278,6 @@ impl Default for CodeGenSpecialFuncs { } } -pub fn get_generic_id_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Rc)> { - let mut generics_ids = vec![]; - - if let Some(id) = tipo.get_generic() { - generics_ids.push((id, param.clone().into())); - return generics_ids; - } - - for (tipo, param_type) in tipo - .get_inner_types() - .iter() - .zip(param.get_inner_types().iter()) - { - generics_ids.append(&mut get_generic_id_and_type(tipo, param_type)); - } - generics_ids -} - -pub fn lookup_data_type_by_tipo( - data_types: &IndexMap<&DataTypeKey, &TypedDataType>, - tipo: &Type, -) -> Option>> { - match tipo { - Type::Fn { ret, .. } => match ret.as_ref() { - Type::App { module, name, .. } => { - let data_type_key = DataTypeKey { - module_name: module.clone(), - defined_type: name.clone(), - }; - data_types.get(&data_type_key).map(|item| (*item).clone()) - } - _ => None, - }, - Type::App { module, name, .. } => { - let data_type_key = DataTypeKey { - module_name: module.clone(), - defined_type: name.clone(), - }; - - data_types.get(&data_type_key).map(|item| (*item).clone()) - } - Type::Var { tipo } => { - if let TypeVar::Link { tipo } = &*tipo.borrow() { - lookup_data_type_by_tipo(data_types, tipo) - } else { - None - } - } - _ => None, - } -} - -pub fn get_arg_type_name(tipo: &Type) -> String { - match tipo { - Type::App { name, args, .. } => { - let inner_args = args.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); - format!("{}_{}", name, inner_args.join("_")) - } - Type::Var { tipo } => match tipo.borrow().clone() { - TypeVar::Link { tipo } => get_arg_type_name(tipo.as_ref()), - _ => unreachable!(), - }, - Type::Tuple { elems } => { - let inner_args = elems.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); - inner_args.join("_") - } - _ => unreachable!(), - } -} - -pub fn convert_opaque_type( - t: &Rc, - data_types: &IndexMap<&DataTypeKey, &TypedDataType>, - deep: bool, -) -> Rc { - if check_replaceable_opaque_type(t, data_types) && matches!(t.as_ref(), Type::App { .. }) { - let data_type = lookup_data_type_by_tipo(data_types, t).unwrap(); - let new_type_fields = data_type.typed_parameters; - - let mut mono_type_vec = vec![]; - - for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) { - mono_type_vec.append(&mut get_generic_id_and_type(tipo, ¶m)); - } - let mono_types = mono_type_vec.into_iter().collect(); - - let generic_type = &data_type.constructors[0].arguments[0].tipo; - - let mono_type = find_and_replace_generics(generic_type, &mono_types); - - if deep { - convert_opaque_type(&mono_type, data_types, deep) - } else { - mono_type - } - } else { - match t.as_ref() { - Type::App { - public, - module, - name, - args, - } => { - let mut new_args = vec![]; - for arg in args { - let arg = convert_opaque_type(arg, data_types, deep); - new_args.push(arg); - } - Type::App { - public: *public, - module: module.clone(), - name: name.clone(), - args: new_args, - } - .into() - } - Type::Fn { args, ret } => { - let mut new_args = vec![]; - for arg in args { - let arg = convert_opaque_type(arg, data_types, deep); - new_args.push(arg); - } - - let ret = convert_opaque_type(ret, data_types, deep); - - Type::Fn { - args: new_args, - ret, - } - .into() - } - Type::Var { tipo: var_tipo } => { - if let TypeVar::Link { tipo } = &var_tipo.borrow().clone() { - convert_opaque_type(tipo, data_types, deep) - } else { - t.clone() - } - } - Type::Tuple { elems } => { - let mut new_elems = vec![]; - for arg in elems { - let arg = convert_opaque_type(arg, data_types, deep); - new_elems.push(arg); - } - Type::Tuple { elems: new_elems }.into() - } - } - } -} - -pub fn check_replaceable_opaque_type( - t: &Type, - data_types: &IndexMap<&DataTypeKey, &TypedDataType>, -) -> bool { - let data_type = lookup_data_type_by_tipo(data_types, t); - - if let Some(data_type) = data_type { - assert!(!data_type.constructors.is_empty()); - let data_type_args = &data_type.constructors[0].arguments; - data_type_args.len() == 1 && data_type.opaque && data_type.constructors.len() == 1 - } else { - false - } -} - -pub fn find_and_replace_generics( - tipo: &Rc, - mono_types: &IndexMap>, -) -> Rc { - if let Some(id) = tipo.get_generic() { - // If a generic does not have a type we know of - // like a None in option then just use same type - mono_types.get(&id).unwrap_or(tipo).clone() - } else if tipo.is_generic() { - match &**tipo { - Type::App { - args, - public, - module, - name, - } => { - let mut new_args = vec![]; - for arg in args { - let arg = find_and_replace_generics(arg, mono_types); - new_args.push(arg); - } - let t = Type::App { - args: new_args, - public: *public, - module: module.clone(), - name: name.clone(), - }; - t.into() - } - Type::Fn { args, ret } => { - let mut new_args = vec![]; - for arg in args { - let arg = find_and_replace_generics(arg, mono_types); - new_args.push(arg); - } - - let ret = find_and_replace_generics(ret, mono_types); - - let t = Type::Fn { - args: new_args, - ret, - }; - - t.into() - } - Type::Tuple { elems } => { - let mut new_elems = vec![]; - for elem in elems { - let elem = find_and_replace_generics(elem, mono_types); - new_elems.push(elem); - } - let t = Type::Tuple { elems: new_elems }; - t.into() - } - Type::Var { tipo: var_tipo } => { - let var_type = var_tipo.as_ref().borrow().clone(); - - match var_type { - TypeVar::Link { tipo } => find_and_replace_generics(&tipo, mono_types), - TypeVar::Generic { .. } | TypeVar::Unbound { .. } => unreachable!(), - } - } - } - } else { - tipo.clone() - } -} - pub fn constants_ir(literal: &Constant) -> AirTree { match literal { Constant::Int { value, .. } => AirTree::int(value), diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index c1ee9711..e5ed6483 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -1,9 +1,14 @@ use self::{environment::Environment, pretty::Printer}; use crate::{ - ast::{Annotation, Constant, DefinitionLocation, ModuleKind, Span}, + ast::{ + Annotation, Constant, DataType, DataTypeKey, DefinitionLocation, ModuleKind, Span, + TypedDataType, + }, builtins::{G1_ELEMENT, G2_ELEMENT, MILLER_LOOP_RESULT}, tipo::fields::FieldMap, }; +use indexmap::IndexMap; +use itertools::Itertools; use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc}; use uplc::{ast::Type as UplcType, builtins::DefaultFunction}; @@ -437,6 +442,240 @@ impl Type { } } +pub fn lookup_data_type_by_tipo( + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + tipo: &Type, +) -> Option>> { + match tipo { + Type::Fn { ret, .. } => match ret.as_ref() { + Type::App { module, name, .. } => { + let data_type_key = DataTypeKey { + module_name: module.clone(), + defined_type: name.clone(), + }; + data_types.get(&data_type_key).map(|item| (*item).clone()) + } + _ => None, + }, + Type::App { module, name, .. } => { + let data_type_key = DataTypeKey { + module_name: module.clone(), + defined_type: name.clone(), + }; + + data_types.get(&data_type_key).map(|item| (*item).clone()) + } + Type::Var { tipo } => { + if let TypeVar::Link { tipo } = &*tipo.borrow() { + lookup_data_type_by_tipo(data_types, tipo) + } else { + None + } + } + _ => None, + } +} + +pub fn get_generic_id_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Rc)> { + let mut generics_ids = vec![]; + + if let Some(id) = tipo.get_generic() { + generics_ids.push((id, param.clone().into())); + return generics_ids; + } + + for (tipo, param_type) in tipo + .get_inner_types() + .iter() + .zip(param.get_inner_types().iter()) + { + generics_ids.append(&mut get_generic_id_and_type(tipo, param_type)); + } + generics_ids +} + +pub fn get_arg_type_name(tipo: &Type) -> String { + match tipo { + Type::App { name, args, .. } => { + let inner_args = args.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); + format!("{}_{}", name, inner_args.join("_")) + } + Type::Var { tipo } => match tipo.borrow().clone() { + TypeVar::Link { tipo } => get_arg_type_name(tipo.as_ref()), + _ => unreachable!(), + }, + Type::Tuple { elems } => { + let inner_args = elems.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); + inner_args.join("_") + } + _ => unreachable!(), + } +} + +pub fn convert_opaque_type( + t: &Rc, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + deep: bool, +) -> Rc { + if check_replaceable_opaque_type(t, data_types) && matches!(t.as_ref(), Type::App { .. }) { + let data_type = lookup_data_type_by_tipo(data_types, t).unwrap(); + + let new_type_fields = data_type.typed_parameters; + + let mut mono_type_vec = vec![]; + + for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) { + mono_type_vec.append(&mut get_generic_id_and_type(tipo, ¶m)); + } + let mono_types = mono_type_vec.into_iter().collect(); + + let generic_type = &data_type.constructors[0].arguments[0].tipo; + + let mono_type = find_and_replace_generics(generic_type, &mono_types); + + if deep { + convert_opaque_type(&mono_type, data_types, deep) + } else { + mono_type + } + } else { + match t.as_ref() { + Type::App { + public, + module, + name, + args, + } => { + let mut new_args = vec![]; + for arg in args { + let arg = convert_opaque_type(arg, data_types, deep); + new_args.push(arg); + } + Type::App { + public: *public, + module: module.clone(), + name: name.clone(), + args: new_args, + } + .into() + } + Type::Fn { args, ret } => { + let mut new_args = vec![]; + for arg in args { + let arg = convert_opaque_type(arg, data_types, deep); + new_args.push(arg); + } + + let ret = convert_opaque_type(ret, data_types, deep); + + Type::Fn { + args: new_args, + ret, + } + .into() + } + Type::Var { tipo: var_tipo } => { + if let TypeVar::Link { tipo } = &var_tipo.borrow().clone() { + convert_opaque_type(tipo, data_types, deep) + } else { + t.clone() + } + } + Type::Tuple { elems } => { + let mut new_elems = vec![]; + for arg in elems { + let arg = convert_opaque_type(arg, data_types, deep); + new_elems.push(arg); + } + Type::Tuple { elems: new_elems }.into() + } + } + } +} + +pub fn check_replaceable_opaque_type( + t: &Type, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, +) -> bool { + let data_type = lookup_data_type_by_tipo(data_types, t); + + if let Some(data_type) = data_type { + if let [constructor] = &data_type.constructors[..] { + return constructor.arguments.len() == 1 && data_type.opaque; + } + } + + false +} + +pub fn find_and_replace_generics( + tipo: &Rc, + mono_types: &IndexMap>, +) -> Rc { + if let Some(id) = tipo.get_generic() { + // If a generic does not have a type we know of + // like a None in option then just use same type + mono_types.get(&id).unwrap_or(tipo).clone() + } else if tipo.is_generic() { + match &**tipo { + Type::App { + args, + public, + module, + name, + } => { + let mut new_args = vec![]; + for arg in args { + let arg = find_and_replace_generics(arg, mono_types); + new_args.push(arg); + } + let t = Type::App { + args: new_args, + public: *public, + module: module.clone(), + name: name.clone(), + }; + t.into() + } + Type::Fn { args, ret } => { + let mut new_args = vec![]; + for arg in args { + let arg = find_and_replace_generics(arg, mono_types); + new_args.push(arg); + } + + let ret = find_and_replace_generics(ret, mono_types); + + let t = Type::Fn { + args: new_args, + ret, + }; + + t.into() + } + Type::Tuple { elems } => { + let mut new_elems = vec![]; + for elem in elems { + let elem = find_and_replace_generics(elem, mono_types); + new_elems.push(elem); + } + let t = Type::Tuple { elems: new_elems }; + t.into() + } + Type::Var { tipo: var_tipo } => { + let var_type = var_tipo.as_ref().borrow().clone(); + + match var_type { + TypeVar::Link { tipo } => find_and_replace_generics(&tipo, mono_types), + TypeVar::Generic { .. } | TypeVar::Unbound { .. } => unreachable!(), + } + } + } + } else { + tipo.clone() + } +} + #[derive(Debug, Clone, PartialEq)] pub enum TypeVar { /// Unbound is an unbound variable. It is one specific type but we don't @@ -594,10 +833,12 @@ impl TypeVar { Self::Link { tipo } => tipo.get_inner_types(), Self::Unbound { .. } => vec![], var => { - vec![Type::Var { - tipo: RefCell::new(var.clone()).into(), - } - .into()] + vec![ + Type::Var { + tipo: RefCell::new(var.clone()).into(), + } + .into(), + ] } } } diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index b7bee950..d0a444d0 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1,7 +1,11 @@ -use crate::line_numbers::LineNumbers; -use std::{cmp::Ordering, collections::HashMap, rc::Rc}; -use vec1::Vec1; - +use super::{ + environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, + error::{Error, Warning}, + hydrator::Hydrator, + pattern::PatternTyper, + pipe::PipeTyper, + RecordAccessor, Type, ValueConstructor, ValueConstructorVariant, +}; use crate::{ ast::{ Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference, @@ -13,17 +17,11 @@ use crate::{ builtins::{bool, byte_array, function, g1_element, g2_element, int, list, string, tuple}, expr::{FnStyle, TypedExpr, UntypedExpr}, format, + line_numbers::LineNumbers, tipo::fields::FieldMap, }; - -use super::{ - environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, - error::{Error, Warning}, - hydrator::Hydrator, - pattern::PatternTyper, - pipe::PipeTyper, - RecordAccessor, Type, ValueConstructor, ValueConstructorVariant, -}; +use std::{cmp::Ordering, collections::HashMap, rc::Rc}; +use vec1::Vec1; #[derive(Debug)] pub(crate) struct ExprTyper<'a, 'b> { @@ -593,7 +591,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { _ => { return Err(Error::RecordUpdateInvalidConstructor { location: constructor.location(), - }) + }); } }; @@ -1135,7 +1133,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } ValueConstructorVariant::ModuleConstant { literal, .. } => { - return Ok(ClauseGuard::Constant(literal.clone())) + return Ok(ClauseGuard::Constant(literal.clone())); } }; diff --git a/crates/aiken-project/src/test_framework.rs b/crates/aiken-project/src/test_framework.rs index 14ab2a64..d1819cb9 100644 --- a/crates/aiken-project/src/test_framework.rs +++ b/crates/aiken-project/src/test_framework.rs @@ -3,8 +3,8 @@ use aiken_lang::{ builtins::bool, expr::{TypedExpr, UntypedExpr}, format::Formatter, - gen_uplc::{builder::convert_opaque_type, CodeGenerator}, - tipo::Type, + gen_uplc::CodeGenerator, + tipo::{convert_opaque_type, Type}, }; use cryptoxide::{blake2b::Blake2b, digest::Digest}; use indexmap::IndexMap;