feat: refactor code gen to avoid builtin errors when tracing is turned on
This commit is contained in:
parent
b50e4ab63a
commit
956c3d6cf0
|
@ -24,13 +24,16 @@ use crate::{
|
|||
},
|
||||
builtins::{bool, data, int, list, string, void},
|
||||
expr::TypedExpr,
|
||||
gen_uplc::builder::{
|
||||
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, monomorphize, pattern_has_conditions, wrap_as_multi_validator,
|
||||
wrap_validator_condition, CodeGenFunction, SpecificClause,
|
||||
},
|
||||
},
|
||||
line_numbers::LineNumbers,
|
||||
tipo::{
|
||||
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
|
||||
|
@ -840,7 +843,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
// Cast value to or from data so we don't have to worry from this point onward
|
||||
if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() {
|
||||
value = AirTree::cast_from_data(value, tipo.clone());
|
||||
value = AirTree::cast_from_data(value, tipo.clone(), props.msg_func.clone());
|
||||
} else if !props.value_type.is_data() && tipo.is_data() {
|
||||
value = AirTree::cast_to_data(value, props.value_type.clone());
|
||||
}
|
||||
|
@ -1050,7 +1053,11 @@ impl<'a> CodeGenerator<'a> {
|
|||
tail.is_some(),
|
||||
value,
|
||||
props.msg_func,
|
||||
true,
|
||||
if props.full_check {
|
||||
ExpectLevel::Full
|
||||
} else {
|
||||
ExpectLevel::Items
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -1345,8 +1352,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
vec![fst_name.clone(), snd_name.clone()],
|
||||
inner_list_type.clone(),
|
||||
AirTree::local_var(&pair_name, inner_list_type.clone()),
|
||||
None,
|
||||
false,
|
||||
msg_func.clone(),
|
||||
msg_func.is_some(),
|
||||
);
|
||||
|
||||
let expect_fst = self.expect_type_assign(
|
||||
|
@ -1431,6 +1438,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
AirTree::cast_from_data(
|
||||
AirTree::local_var(&item_name, data()),
|
||||
inner_list_type.clone(),
|
||||
msg_func.clone(),
|
||||
),
|
||||
defined_data_types,
|
||||
location,
|
||||
|
@ -2291,7 +2299,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
// So for the msg we pass in empty string if tracing is on
|
||||
// Since check_last_item is false this will never get added to the final uplc anyway
|
||||
None,
|
||||
false,
|
||||
ExpectLevel::None,
|
||||
)
|
||||
} else {
|
||||
assert!(defined_tails.len() >= defined_heads.len());
|
||||
|
@ -4042,7 +4050,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
// tipo here refers to the list type while the actual return
|
||||
// type is nothing since this is an assignment over some expression
|
||||
tipo,
|
||||
is_expect,
|
||||
expect_level,
|
||||
} => {
|
||||
let value = arg_stack.pop().unwrap();
|
||||
|
||||
|
@ -4071,8 +4079,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
&names_types,
|
||||
tail,
|
||||
term,
|
||||
is_expect && !tail,
|
||||
true,
|
||||
expect_level,
|
||||
error_term,
|
||||
)
|
||||
.apply(value);
|
||||
|
@ -4495,12 +4503,16 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
Some(term)
|
||||
}
|
||||
Air::CastFromData { tipo } => {
|
||||
Air::CastFromData { tipo, .. } => {
|
||||
let mut term = arg_stack.pop().unwrap();
|
||||
|
||||
if extract_constant(&term).is_some() {
|
||||
term = builder::convert_data_to_type(term, &tipo);
|
||||
term = if error_term == Term::Error {
|
||||
builder::convert_data_to_type(term, &tipo)
|
||||
} else {
|
||||
builder::convert_data_to_type_debug(term, &tipo, error_term)
|
||||
};
|
||||
|
||||
if extract_constant(&term).is_some() {
|
||||
let mut program: Program<Name> = Program {
|
||||
version: (1, 0, 0),
|
||||
term,
|
||||
|
@ -4515,8 +4527,6 @@ impl<'a> CodeGenerator<'a> {
|
|||
let evaluated_term: Term<NamedDeBruijn> =
|
||||
eval_program.eval(ExBudget::default()).result().unwrap();
|
||||
term = evaluated_term.try_into().unwrap();
|
||||
} else {
|
||||
term = builder::convert_data_to_type(term, &tipo);
|
||||
}
|
||||
|
||||
Some(term)
|
||||
|
@ -4979,8 +4989,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
&names_types,
|
||||
false,
|
||||
term,
|
||||
is_expect,
|
||||
false,
|
||||
is_expect.into(),
|
||||
error_term,
|
||||
);
|
||||
|
||||
|
@ -5213,19 +5223,35 @@ impl<'a> CodeGenerator<'a> {
|
|||
if names[1] != "_" {
|
||||
term = term
|
||||
.lambda(names[1].clone())
|
||||
.apply(builder::convert_data_to_type(
|
||||
.apply(if error_term != Term::Error {
|
||||
builder::convert_data_to_type_debug(
|
||||
Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))),
|
||||
&inner_types[1],
|
||||
));
|
||||
error_term.clone(),
|
||||
)
|
||||
} else {
|
||||
builder::convert_data_to_type(
|
||||
Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))),
|
||||
&inner_types[1],
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
if names[0] != "_" {
|
||||
term = term
|
||||
.lambda(names[0].clone())
|
||||
.apply(builder::convert_data_to_type(
|
||||
Term::fst_pair().apply(Term::var(format!("__tuple_{list_id}"))),
|
||||
&inner_types[0],
|
||||
))
|
||||
.apply(if error_term != Term::Error {
|
||||
builder::convert_data_to_type_debug(
|
||||
Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))),
|
||||
&inner_types[1],
|
||||
error_term,
|
||||
)
|
||||
} else {
|
||||
builder::convert_data_to_type(
|
||||
Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))),
|
||||
&inner_types[1],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
term = term.lambda(format!("__tuple_{list_id}")).apply(value);
|
||||
|
@ -5250,8 +5276,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
&names_types,
|
||||
false,
|
||||
term,
|
||||
is_expect,
|
||||
false,
|
||||
is_expect.into(),
|
||||
error_term,
|
||||
)
|
||||
.apply(value);
|
||||
|
|
|
@ -7,6 +7,23 @@ use crate::{
|
|||
tipo::{Type, ValueConstructor},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
pub enum ExpectLevel {
|
||||
Full,
|
||||
Items,
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<bool> for ExpectLevel {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
ExpectLevel::Full
|
||||
} else {
|
||||
ExpectLevel::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Air {
|
||||
// Primitives
|
||||
|
@ -83,6 +100,7 @@ pub enum Air {
|
|||
},
|
||||
CastFromData {
|
||||
tipo: Rc<Type>,
|
||||
is_expect: bool,
|
||||
},
|
||||
CastToData {
|
||||
tipo: Rc<Type>,
|
||||
|
@ -159,7 +177,7 @@ pub enum Air {
|
|||
tipo: Rc<Type>,
|
||||
names: Vec<String>,
|
||||
tail: bool,
|
||||
is_expect: bool,
|
||||
expect_level: ExpectLevel,
|
||||
},
|
||||
ListExpose {
|
||||
tipo: Rc<Type>,
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
air::Air,
|
||||
air::{Air, ExpectLevel},
|
||||
tree::{AirExpression, AirMsg, AirStatement, AirTree, TreePath},
|
||||
};
|
||||
|
||||
|
@ -1237,6 +1237,140 @@ pub fn convert_data_to_type(term: Term<Name>, field_type: &Rc<Type>) -> Term<Nam
|
|||
}
|
||||
}
|
||||
|
||||
pub fn convert_data_to_type_debug(
|
||||
term: Term<Name>,
|
||||
field_type: &Rc<Type>,
|
||||
error_term: Term<Name>,
|
||||
) -> Term<Name> {
|
||||
if field_type.is_int() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::un_i_data().apply(Term::var("__val")),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_bytearray() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::un_b_data().apply(Term::var("__val")),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_void() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
Term::equals_integer()
|
||||
.apply(Term::integer(0.into()))
|
||||
.apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val"))))
|
||||
.delayed_if_then_else(Term::unit(), Term::Error),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_map() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
Term::unmap_data().apply(Term::var("__val")),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_string() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::Builtin(DefaultFunction::DecodeUtf8)
|
||||
.apply(Term::un_b_data().apply(Term::var("__val"))),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_tuple() && matches!(field_type.get_uplc_type(), UplcType::Pair(_, _)) {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::mk_pair_data()
|
||||
.apply(Term::head_list().apply(Term::var("__list_data")))
|
||||
.apply(Term::head_list().apply(Term::var("__tail")))
|
||||
.lambda("__tail")
|
||||
.apply(Term::tail_list().apply(Term::var("__list_data")))
|
||||
.lambda("__list_data")
|
||||
.apply(Term::unlist_data().apply(Term::var("__val"))),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_list() || field_type.is_tuple() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::unlist_data().apply(Term::var("__val")),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_bool() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
Term::equals_integer()
|
||||
.apply(Term::integer(1.into()))
|
||||
.apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val")))),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_bls381_12_g1() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::bls12_381_g1_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_bls381_12_g2() {
|
||||
Term::var("__val")
|
||||
.delayed_choose_data(
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
error_term.clone(),
|
||||
Term::bls12_381_g2_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))),
|
||||
)
|
||||
.lambda("__val")
|
||||
.apply(term)
|
||||
} else if field_type.is_ml_result() {
|
||||
panic!("ML Result not supported")
|
||||
} else {
|
||||
term
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_constants_to_data(constants: Vec<Rc<UplcConstant>>) -> Vec<UplcConstant> {
|
||||
let mut new_constants = vec![];
|
||||
for constant in constants {
|
||||
|
@ -1391,8 +1525,8 @@ pub fn list_access_to_uplc(
|
|||
names_types_ids: &[(String, Rc<Type>, u64)],
|
||||
tail: bool,
|
||||
term: Term<Name>,
|
||||
check_last_item: bool,
|
||||
is_list_accessor: bool,
|
||||
expect_level: ExpectLevel,
|
||||
error_term: Term<Name>,
|
||||
) -> Term<Name> {
|
||||
let names_len = names_types_ids.len();
|
||||
|
@ -1404,7 +1538,10 @@ pub fn list_access_to_uplc(
|
|||
.collect_vec();
|
||||
|
||||
// If the the is just discards and check_last_item then we check for empty list
|
||||
if no_tailing_discards.is_empty() && !tail && check_last_item {
|
||||
if no_tailing_discards.is_empty()
|
||||
&& !tail
|
||||
&& matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items)
|
||||
{
|
||||
return Term::var("empty_list")
|
||||
.delayed_choose_list(term, error_term)
|
||||
.lambda("empty_list");
|
||||
|
@ -1426,6 +1563,12 @@ pub fn list_access_to_uplc(
|
|||
let head_list =
|
||||
if matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) && is_list_accessor {
|
||||
Term::head_list().apply(Term::var(tail_name.to_string()))
|
||||
} else if matches!(expect_level, ExpectLevel::Full) && error_term != Term::Error {
|
||||
convert_data_to_type_debug(
|
||||
Term::head_list().apply(Term::var(tail_name.to_string())),
|
||||
&tipo.to_owned(),
|
||||
error_term.clone(),
|
||||
)
|
||||
} else {
|
||||
convert_data_to_type(
|
||||
Term::head_list().apply(Term::var(tail_name.to_string())),
|
||||
|
@ -1442,7 +1585,7 @@ pub fn list_access_to_uplc(
|
|||
// case for no tail
|
||||
// name is guaranteed to not be discard at this point
|
||||
|
||||
if check_last_item {
|
||||
if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) {
|
||||
Term::tail_list()
|
||||
.apply(Term::var(tail_name.to_string()))
|
||||
.delayed_choose_list(acc, error_term.clone())
|
||||
|
@ -1453,8 +1596,30 @@ pub fn list_access_to_uplc(
|
|||
acc.lambda(name).apply(head_list).lambda(tail_name)
|
||||
}
|
||||
} else if name == "_" {
|
||||
if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items)
|
||||
&& error_term != Term::Error
|
||||
{
|
||||
Term::var(tail_name.to_string())
|
||||
.delayed_choose_list(
|
||||
error_term.clone(),
|
||||
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))),
|
||||
)
|
||||
.lambda(tail_name)
|
||||
} else {
|
||||
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
|
||||
.lambda(tail_name)
|
||||
}
|
||||
} else if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items)
|
||||
&& error_term != Term::Error
|
||||
{
|
||||
Term::var(tail_name.to_string())
|
||||
.delayed_choose_list(
|
||||
error_term.clone(),
|
||||
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
|
||||
.lambda(name)
|
||||
.apply(head_list),
|
||||
)
|
||||
.lambda(tail_name)
|
||||
} else {
|
||||
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
|
||||
.lambda(name)
|
||||
|
@ -1791,9 +1956,14 @@ pub fn air_holds_msg(air: &Air) -> bool {
|
|||
Air::AssertConstr { .. } | Air::AssertBool { .. } | Air::FieldsEmpty | Air::ListEmpty => {
|
||||
true
|
||||
}
|
||||
|
||||
Air::FieldsExpose { is_expect, .. }
|
||||
| Air::ListAccessor { is_expect, .. }
|
||||
| Air::TupleAccessor { is_expect, .. } => *is_expect,
|
||||
| Air::TupleAccessor { is_expect, .. }
|
||||
| Air::CastFromData { is_expect, .. } => *is_expect,
|
||||
|
||||
Air::ListAccessor { expect_level, .. } => {
|
||||
matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items)
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
tipo::{Type, ValueConstructor, ValueConstructorVariant},
|
||||
};
|
||||
|
||||
use super::air::Air;
|
||||
use super::air::{Air, ExpectLevel};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TreePath {
|
||||
|
@ -179,7 +179,7 @@ pub enum AirStatement {
|
|||
names: Vec<String>,
|
||||
tail: bool,
|
||||
list: Box<AirTree>,
|
||||
is_expect: bool,
|
||||
expect_level: ExpectLevel,
|
||||
msg: Option<AirMsg>,
|
||||
},
|
||||
ListExpose {
|
||||
|
@ -272,6 +272,7 @@ pub enum AirExpression {
|
|||
CastFromData {
|
||||
tipo: Rc<Type>,
|
||||
value: Box<AirTree>,
|
||||
msg: Option<AirMsg>,
|
||||
},
|
||||
CastToData {
|
||||
tipo: Rc<Type>,
|
||||
|
@ -502,10 +503,11 @@ impl AirTree {
|
|||
hoisted_over: None,
|
||||
}
|
||||
}
|
||||
pub fn cast_from_data(value: AirTree, tipo: Rc<Type>) -> AirTree {
|
||||
pub fn cast_from_data(value: AirTree, tipo: Rc<Type>, msg: Option<AirMsg>) -> AirTree {
|
||||
AirTree::Expression(AirExpression::CastFromData {
|
||||
tipo,
|
||||
value: value.into(),
|
||||
msg,
|
||||
})
|
||||
}
|
||||
pub fn cast_to_data(value: AirTree, tipo: Rc<Type>) -> AirTree {
|
||||
|
@ -733,6 +735,7 @@ impl AirTree {
|
|||
vec![list_of_fields],
|
||||
),
|
||||
tipo.clone(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -758,7 +761,7 @@ impl AirTree {
|
|||
tail: bool,
|
||||
list: AirTree,
|
||||
msg: Option<AirMsg>,
|
||||
is_expect: bool,
|
||||
expect_level: ExpectLevel,
|
||||
) -> AirTree {
|
||||
AirTree::Statement {
|
||||
statement: AirStatement::ListAccessor {
|
||||
|
@ -766,7 +769,7 @@ impl AirTree {
|
|||
names,
|
||||
tail,
|
||||
list: list.into(),
|
||||
is_expect,
|
||||
expect_level,
|
||||
msg,
|
||||
},
|
||||
hoisted_over: None,
|
||||
|
@ -816,6 +819,7 @@ impl AirTree {
|
|||
vec![tuple],
|
||||
),
|
||||
tipo.clone(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
pub fn error(tipo: Rc<Type>, validator: bool) -> AirTree {
|
||||
|
@ -1069,13 +1073,13 @@ impl AirTree {
|
|||
tail,
|
||||
list,
|
||||
msg,
|
||||
is_expect,
|
||||
expect_level,
|
||||
} => {
|
||||
air_vec.push(Air::ListAccessor {
|
||||
tipo: tipo.clone(),
|
||||
names: names.clone(),
|
||||
tail: *tail,
|
||||
is_expect: *is_expect,
|
||||
expect_level: *expect_level,
|
||||
});
|
||||
|
||||
if let Some(msg) = msg {
|
||||
|
@ -1227,8 +1231,16 @@ impl AirTree {
|
|||
air_vec.push(Air::UnOp { op: *op });
|
||||
arg.create_air_vec(air_vec);
|
||||
}
|
||||
AirExpression::CastFromData { tipo, value } => {
|
||||
air_vec.push(Air::CastFromData { tipo: tipo.clone() });
|
||||
AirExpression::CastFromData { tipo, value, msg } => {
|
||||
air_vec.push(Air::CastFromData {
|
||||
tipo: tipo.clone(),
|
||||
is_expect: msg.is_some(),
|
||||
});
|
||||
|
||||
if let Some(msg) = msg {
|
||||
msg.to_air_tree().create_air_vec(air_vec);
|
||||
}
|
||||
|
||||
value.create_air_vec(air_vec);
|
||||
}
|
||||
AirExpression::CastToData { tipo, value } => {
|
||||
|
|
Loading…
Reference in New Issue