Handle (recursive) generic types during reification.

Also moved a bunch of functions from code-gen back into _tipo_, as
  they're better suited and generic enough to be reused elsewhere.
This commit is contained in:
KtorZ 2024-03-07 18:06:36 +01:00
parent a3fbe6c155
commit 23a22a65cb
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
8 changed files with 730 additions and 516 deletions

View File

@ -299,6 +299,19 @@ pub struct FunctionAccessKey {
pub type TypedDataType = DataType<Rc<Type>>;
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![

View File

@ -1211,6 +1211,16 @@ pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap<FunctionAccessKey, Ty
pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDataType> {
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(

View File

@ -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<Self, String> {
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::<Result<Vec<_>, _>>()?,
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::<Result<Vec<_>, _>>()?,
}),
_ => 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::<Result<Vec<_>, _>>()?,
}),
_ => 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<Self, String> {
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<T, F>(
generics: &mut IndexMap<u64, Rc<Type>>,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
t: T,
tipo: &Type,
with: F,
) -> Result<Self, String>
where
T: Debug,
F: Fn(
&mut IndexMap<u64, Rc<Type>>,
&IndexMap<&DataTypeKey, &TypedDataType>,
T,
&Type,
) -> Result<Self, String>,
{
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<u64, Rc<Type>>,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
cst: uplc::ast::Constant,
tipo: &Type,
) -> Result<Self, String> {
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::<Result<Vec<_>, _>>()?,
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::<Result<Vec<_>, _>>()?,
}),
_ => 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::<Result<Vec<_>, _>>()?,
}),
_ => 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::<Result<Vec<_>, _>>()?,
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::<Result<Vec<_>, _>>()?,
}),
_ => 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::<Result<Vec<_>, _>>()?;
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::<Vec<_>>(),
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::<Vec<_>>(),
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::<Vec<_>>();
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<u64, Rc<Type>>,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
data: PlutusData,
tipo: &Type,
) -> Result<Self, String> {
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::<Result<Vec<_>, _>>()?,
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::<Result<Vec<_>, _>>()?,
}),
_ => 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::<Result<Vec<_>, _>>()?;
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<Self>, location: Span) -> Self {
UntypedExpr::Trace {
location,

View File

@ -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

View File

@ -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<Type>)> {
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<DataType<Rc<Type>>> {
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<Type>,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
deep: bool,
) -> Rc<Type> {
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, &param));
}
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<Type>,
mono_types: &IndexMap<u64, Rc<Type>>,
) -> Rc<Type> {
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),

View File

@ -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<DataType<Rc<Type>>> {
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<Type>)> {
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<Type>,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
deep: bool,
) -> Rc<Type> {
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, &param));
}
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<Type>,
mono_types: &IndexMap<u64, Rc<Type>>,
) -> Rc<Type> {
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(),
]
}
}
}

View File

@ -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()));
}
};

View File

@ -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;