Start work on revamping assignment so we can handle soft casting properly

This commit is contained in:
microproofs 2024-07-28 16:14:41 -04:00 committed by Kasey
parent 9ea54afd12
commit dd5badd884
4 changed files with 529 additions and 268 deletions

View File

@ -38,6 +38,7 @@ use crate::{
}, },
IdGenerator, IdGenerator,
}; };
use builder::unknown_data_to_type;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use itertools::Itertools; use itertools::Itertools;
use petgraph::{algo, Graph}; use petgraph::{algo, Graph};
@ -271,17 +272,16 @@ impl<'a> CodeGenerator<'a> {
let msg_func_name = msg.split_whitespace().join(""); let msg_func_name = msg.split_whitespace().join("");
self.special_functions.insert_new_function( if msg_func_name.is_empty() {
msg_func_name.clone(), None
if msg.is_empty() { } else {
Term::Error.delay() self.special_functions.insert_new_function(
} else { msg_func_name.clone(),
Term::Error.delayed_trace(Term::string(msg)).delay() Term::Error.delayed_trace(Term::string(msg)).delay(),
}, void(),
void(), );
); Some(self.special_functions.use_function_tree(msg_func_name))
}
self.special_functions.use_function_tree(msg_func_name)
}; };
let (then, context) = context.split_first().unwrap(); let (then, context) = context.split_first().unwrap();
@ -569,7 +569,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::let_(), kind: AssignmentKind::let_(),
remove_unused: false, remove_unused: false,
full_check: false, full_check: false,
otherwise: AirTree::error(void(), false), otherwise: None,
}, },
) )
} else { } else {
@ -625,43 +625,47 @@ impl<'a> CodeGenerator<'a> {
// //
// if <expr:condition> is <pattern>: <annotation> { <expr:body> } // if <expr:condition> is <pattern>: <annotation> { <expr:body> }
// [(builtin ifThenElse) (condition is pattern) (body) (else) ] // [(builtin ifThenElse) (condition is pattern) (body) (else) ]
TypedExpr::If { a @ TypedExpr::If {
branches, branches,
final_else, final_else,
tipo, tipo,
.. ..
} => branches.iter().rfold( } => {
self.build(final_else, module_build_name, &[]), println!("A: {:#?}", a);
|acc, branch| { branches.iter().rfold(
let condition = self.build(&branch.condition, module_build_name, &[]); self.build(final_else, module_build_name, &[]),
let body = self.build(&branch.body, module_build_name, &[]); |acc, branch| {
let condition = self.build(&branch.condition, module_build_name, &[]);
let body = self.build(&branch.body, module_build_name, &[]);
match &branch.is { match &branch.is {
Some((pattern, tipo)) => { Some((pattern, tipo)) => {
AirTree::let_assignment( println!("PATTERN TYPE: {:#?}", pattern);
"acc_var", println!("Branch TYPE: {:#?}", tipo);
// use anon function as a delay to avoid evaluating the acc AirTree::let_assignment(
AirTree::anon_func(vec![], acc, true), "acc_var",
self.assignment( // use anon function as a delay to avoid evaluating the acc
pattern, AirTree::anon_func(vec![], acc, true),
condition, self.assignment(
body, pattern,
tipo, condition,
AssignmentProperties { body,
value_type: branch.condition.tipo(), tipo,
kind: AssignmentKind::Expect { backpassing: () }, AssignmentProperties {
remove_unused: false, value_type: branch.condition.tipo(),
full_check: true, kind: AssignmentKind::Expect { backpassing: () },
otherwise: AirTree::local_var("acc_var", void()), remove_unused: false,
}, full_check: true,
), otherwise: AirTree::local_var("acc_var", void()),
) },
),
)
}
None => AirTree::if_branch(tipo.clone(), condition, body, acc),
} }
None => AirTree::if_branch(tipo.clone(), condition, body, acc), },
} )
}, }
),
TypedExpr::RecordAccess { TypedExpr::RecordAccess {
tipo, tipo,
index, index,
@ -890,7 +894,7 @@ impl<'a> CodeGenerator<'a> {
pub fn assignment( pub fn assignment(
&mut self, &mut self,
pattern: &TypedPattern, pattern: &TypedPattern,
mut value: AirTree, value: AirTree,
then: AirTree, then: AirTree,
tipo: &Rc<Type>, tipo: &Rc<Type>,
props: AssignmentProperties, props: AssignmentProperties,
@ -906,41 +910,90 @@ impl<'a> CodeGenerator<'a> {
); );
// Cast value to or from data so we don't have to worry from this point onward // 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() { let assign_casted_value = |name, value, then| {
value = AirTree::cast_from_data(value, tipo.clone(), props.otherwise.clone(), true); if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() {
} else if !props.value_type.is_data() && tipo.is_data() { if props.otherwise.is_some() {
value = AirTree::cast_to_data(value, props.value_type.clone()); AirTree::soft_cast_assignment(
} name,
tipo.clone(),
value,
then,
props.otherwise.as_ref().unwrap().clone(),
)
} else {
AirTree::let_assignment(
name,
AirTree::cast_from_data(value, tipo.clone(), true),
then,
)
}
} else if !props.value_type.is_data() && tipo.is_data() {
AirTree::let_assignment(
name,
AirTree::cast_to_data(value, props.value_type.clone()),
then,
)
} else {
AirTree::let_assignment(name, value, then)
}
};
let otherwise = match &props.otherwise {
Some(x) => x.clone(),
// (delay (error ))
None => AirTree::anon_func(vec![], AirTree::error(void(), false), true),
};
match pattern { match pattern {
Pattern::Int { Pattern::Int {
value: expected_int, value: expected_int,
location, location,
.. ..
} => AirTree::assign_literal_pattern( } => {
format!( let name = format!(
"__expected_by_{}_span_{}_{}", "__expected_by_{}_span_{}_{}",
expected_int, location.start, location.end expected_int, location.start, location.end
), );
AirTree::int(expected_int),
value, let expect = AirTree::binop(
int(), BinOp::Eq,
props, bool(),
then, AirTree::int(expected_int),
), AirTree::local_var(&name, int()),
int(),
);
assign_casted_value(
name,
value,
AirTree::assert_bool(true, expect, then, otherwise),
)
}
Pattern::ByteArray { Pattern::ByteArray {
location,
value: expected_bytes, value: expected_bytes,
location,
.. ..
} => AirTree::assign_literal_pattern( } => {
format!("__expected_bytes_span_{}_{}", location.start, location.end), let name = format!(
AirTree::byte_array(expected_bytes.clone()), "__expected_bytes_span_{}_{}",
value, location.start, location.end
byte_array(), );
props,
then, let expect = AirTree::binop(
), BinOp::Eq,
bool(),
AirTree::byte_array(expected_bytes),
AirTree::local_var(&name, byte_string()),
byte_string(),
);
assign_casted_value(
name,
value,
AirTree::assert_bool(true, expect, then, otherwise),
)
}
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
if props.full_check { if props.full_check {
@ -951,26 +1004,28 @@ impl<'a> CodeGenerator<'a> {
let val = AirTree::local_var(name, tipo.clone()); let val = AirTree::local_var(name, tipo.clone());
if non_opaque_tipo.is_primitive() { if non_opaque_tipo.is_primitive() {
AirTree::let_assignment(name, value, then) assign_casted_value(name.clone(), value, then)
} else { } else {
let expect = self.expect_type_assign( assign_casted_value(
&non_opaque_tipo, name.clone(),
val, value,
&mut index_map, self.expect_type_assign(
pattern.location(), &non_opaque_tipo,
props.otherwise, val,
); &mut index_map,
pattern.location(),
let assign_expect = AirTree::let_assignment("_", expect, then); then,
otherwise,
AirTree::let_assignment(name, value, assign_expect) ),
)
} }
} else { } else {
AirTree::let_assignment(name, value, then) assign_casted_value(name.clone(), value, then)
} }
} }
Pattern::Assign { name, pattern, .. } => { Pattern::Assign { name, pattern, .. } => {
// Don't need any data casting for Assign
let inner_pattern = self.assignment( let inner_pattern = self.assignment(
pattern, pattern,
AirTree::local_var(name, tipo.clone()), AirTree::local_var(name, tipo.clone()),
@ -983,30 +1038,31 @@ impl<'a> CodeGenerator<'a> {
Pattern::Discard { name, .. } => { Pattern::Discard { name, .. } => {
if props.full_check { if props.full_check {
let name = &format!("__discard_expect_{}", name); let name = format!("__discard_expect_{}", name);
let mut index_map = IndexMap::new(); let mut index_map = IndexMap::new();
let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true); let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true);
let val = AirTree::local_var(name, tipo.clone()); let val = AirTree::local_var(&name, tipo.clone());
if non_opaque_tipo.is_primitive() { if non_opaque_tipo.is_primitive() {
AirTree::let_assignment(name, value, then) assign_casted_value(name, value, then)
} else { } else {
let expect = self.expect_type_assign( assign_casted_value(
&non_opaque_tipo, name,
val, value,
&mut index_map, self.expect_type_assign(
pattern.location(), &non_opaque_tipo,
props.otherwise, val,
); &mut index_map,
pattern.location(),
let assignment = AirTree::let_assignment("_", expect, then); then,
otherwise,
AirTree::let_assignment(name, value, assignment) ),
)
} }
} else if !props.remove_unused { } else if !props.remove_unused {
AirTree::let_assignment(name, value, then) assign_casted_value(name.clone(), value, then)
} else { } else {
then then
} }
@ -1030,6 +1086,7 @@ impl<'a> CodeGenerator<'a> {
Some(tail) => { Some(tail) => {
let tail_name = match tail.as_ref() { let tail_name = match tail.as_ref() {
Pattern::Var { name, .. } => name.to_string(), Pattern::Var { name, .. } => name.to_string(),
// This Pattern one doesn't even make sense
Pattern::Assign { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(),
Pattern::Discard { name, .. } => { Pattern::Discard { name, .. } => {
if props.full_check { if props.full_check {
@ -1038,6 +1095,7 @@ impl<'a> CodeGenerator<'a> {
"_".to_string() "_".to_string()
} }
} }
// This should be unreachable
_ => format!( _ => format!(
"tail_span_{}_{}", "tail_span_{}_{}",
tail.location().start, tail.location().start,
@ -1056,6 +1114,9 @@ impl<'a> CodeGenerator<'a> {
AssignmentProperties { AssignmentProperties {
value_type: tipo.clone(), value_type: tipo.clone(),
kind: props.kind, kind: props.kind,
// The reason the top level of recursion might have remove_unused
// false is to deal with expect _ = thing
// next_thing
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
otherwise: props.otherwise.clone(), otherwise: props.otherwise.clone(),
@ -1120,21 +1181,37 @@ impl<'a> CodeGenerator<'a> {
elems.reverse(); elems.reverse();
let name = format!(
"__List_span_{}_{}",
pattern.location().start,
pattern.location().end
);
let casted_var = AirTree::local_var(&name, tipo.clone());
if elements.is_empty() { if elements.is_empty() {
AirTree::list_empty(value, then, props.otherwise.clone()) assign_casted_value(
} else { name,
AirTree::list_access(
elems,
tipo.clone(),
tail.is_some(),
value, value,
if props.full_check { AirTree::list_empty(casted_var, then, otherwise),
ExpectLevel::Full )
} else { } else {
ExpectLevel::Items assign_casted_value(
}, name,
then, value,
props.otherwise.clone(), AirTree::list_access(
elems,
tipo.clone(),
tail.is_some(),
casted_var,
if props.full_check {
ExpectLevel::Full
} else {
ExpectLevel::Items
},
then,
otherwise,
),
) )
} }
} }
@ -1214,8 +1291,7 @@ impl<'a> CodeGenerator<'a> {
// This `value` is either value param that was passed in or // This `value` is either value param that was passed in or
// local var // local var
let constructor_name = format!( let constructor_name = format!(
"__constructor_{}_span_{}_{}", "Pair_span_{}_{}",
"Pair",
pattern.location().start, pattern.location().start,
pattern.location().end pattern.location().end
); );
@ -1238,11 +1314,11 @@ impl<'a> CodeGenerator<'a> {
local_value, local_value,
props.full_check, props.full_check,
then, then,
props.otherwise.clone(), otherwise,
) )
}; };
AirTree::let_assignment(constructor_name, value, then) assign_casted_value(constructor_name, value, then)
} }
Pattern::Constructor { Pattern::Constructor {
@ -1251,13 +1327,25 @@ impl<'a> CodeGenerator<'a> {
} if tipo.is_bool() => { } if tipo.is_bool() => {
assert!(props.kind.is_expect()); assert!(props.kind.is_expect());
AirTree::assert_bool(name == "True", value, then, props.otherwise.clone()) let name_var = format!(
"__Bool_{}_{}",
pattern.location().start,
pattern.location().end
);
let local_var = AirTree::local_var(&name_var, tipo.clone());
assign_casted_value(
name_var,
value,
AirTree::assert_bool(name == "True", local_var, then, otherwise),
)
} }
Pattern::Constructor { .. } if tipo.is_void() => { Pattern::Constructor { .. } if tipo.is_void() => {
// Void type is checked when casting from data // Void type is checked when casting from data
// So we just assign the value and move on // So we just assign the value and move on
AirTree::let_assignment("_", value, then) assign_casted_value("_".to_string(), value, then)
} }
Pattern::Constructor { Pattern::Constructor {
@ -1376,7 +1464,7 @@ impl<'a> CodeGenerator<'a> {
local_value, local_value,
props.full_check, props.full_check,
then, then,
props.otherwise.clone(), otherwise.clone(),
) )
}; };
@ -1404,7 +1492,7 @@ impl<'a> CodeGenerator<'a> {
index, index,
AirTree::local_var(&subject_name, tipo.clone()), AirTree::local_var(&subject_name, tipo.clone()),
then, then,
props.otherwise.clone(), otherwise,
), ),
) )
} else { } else {
@ -1415,7 +1503,7 @@ impl<'a> CodeGenerator<'a> {
then then
}; };
AirTree::let_assignment(constructor_name, value, then) assign_casted_value(constructor_name, value, then)
} }
Pattern::Tuple { Pattern::Tuple {
@ -1487,13 +1575,25 @@ impl<'a> CodeGenerator<'a> {
// This `value` is either value param that was passed in or local var // This `value` is either value param that was passed in or local var
AirTree::tuple_access( let name = format!(
fields, "__Tuple_span_{}_{}",
tipo.clone(), pattern.location().start,
pattern.location().end
);
let local_var = AirTree::local_var(&name, tipo.clone());
assign_casted_value(
name,
value, value,
props.full_check, AirTree::tuple_access(
then, fields,
props.otherwise.clone(), tipo.clone(),
local_var,
props.full_check,
then,
otherwise,
),
) )
} }
} }
@ -1505,6 +1605,7 @@ impl<'a> CodeGenerator<'a> {
value: AirTree, value: AirTree,
defined_data_types: &mut IndexMap<String, u64>, defined_data_types: &mut IndexMap<String, u64>,
location: Span, location: Span,
then: AirTree,
otherwise: AirTree, otherwise: AirTree,
) -> AirTree { ) -> AirTree {
assert!(tipo.get_generic().is_none()); assert!(tipo.get_generic().is_none());
@ -1621,27 +1722,29 @@ impl<'a> CodeGenerator<'a> {
let mut tuple_expect_items = vec![]; let mut tuple_expect_items = vec![];
let then = tuple_inner_types.iter().enumerate().rfold( let then =
AirTree::void(), tuple_inner_types
|then, (index, arg)| { .iter()
let tuple_index_name = format!( .enumerate()
"__tuple_index_{}_span_{}_{}", .rfold(then, |then, (index, arg)| {
index, location.start, location.end let tuple_index_name = format!(
); "__tuple_index_{}_span_{}_{}",
index, location.start, location.end
);
let expect_tuple_item = self.expect_type_assign( let expect_tuple_item = self.expect_type_assign(
arg, arg,
AirTree::local_var(&tuple_index_name, arg.clone()), AirTree::local_var(&tuple_index_name, arg.clone()),
defined_data_types, defined_data_types,
location, location,
otherwise.clone(), then,
); otherwise.clone(),
);
tuple_expect_items.push(tuple_index_name); tuple_expect_items.push(tuple_index_name);
AirTree::let_assignment("_", expect_tuple_item, then) expect_tuple_item
}, });
);
tuple_expect_items.reverse(); tuple_expect_items.reverse();
@ -1663,11 +1766,53 @@ impl<'a> CodeGenerator<'a> {
let inner_list_type = &tipo.get_inner_types()[0]; let inner_list_type = &tipo.get_inner_types()[0];
if inner_list_type.is_data() { if inner_list_type.is_data() {
value then
} else { } else {
let list_name = format!("__list_span_{}_{}", location.start, location.end); let list_name = format!("__list_span_{}_{}", location.start, location.end);
let item_name = format!("__item_span_{}_{}", location.start, location.end); let item_name = format!("__item_span_{}_{}", location.start, location.end);
let g = AirTree::anon_func(
vec!["__list".to_string(), "__curried_expect_on_list".to_string()],
AirTree::list_empty(
AirTree::local_var("__list", tipo.clone()),
then,
AirTree::let_assignment(
&item_name,
AirTree::builtin(
DefaultFunction::HeadList,
data(),
vec![AirTree::local_var("__list", tipo.clone())],
),
AirTree::let_assignment(
&item_name,
self.expect_type_assign(
inner_list_type,
AirTree::cast_from_data(
AirTree::local_var(item_name, data()),
inner_list_type.clone(),
otherwise.clone(),
true,
),
defined_data_types,
location,
AirTree::void(),
otherwise,
),
AirTree::call(
AirTree::local_var("__curried_expect_on_list", void()),
void(),
vec![AirTree::builtin(
DefaultFunction::TailList,
list(data()),
vec![AirTree::local_var("__list", tipo.clone())],
)],
),
),
),
),
false,
);
let expect_item = self.expect_type_assign( let expect_item = self.expect_type_assign(
inner_list_type, inner_list_type,
AirTree::cast_from_data( AirTree::cast_from_data(
@ -4051,7 +4196,7 @@ impl<'a> CodeGenerator<'a> {
if otherwise == Term::Error.delay() { if otherwise == Term::Error.delay() {
builder::unknown_data_to_type(term, tipo) builder::unknown_data_to_type(term, tipo)
} else { } else {
builder::unknown_data_to_type_otherwise(term, tipo, otherwise) builder::softcast_data_to_type_otherwise(term, tipo, otherwise)
} }
}; };
@ -4766,14 +4911,8 @@ impl<'a> CodeGenerator<'a> {
Air::CastFromData { tipo, full_cast } => { Air::CastFromData { tipo, full_cast } => {
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = if full_cast {
arg_stack.pop().unwrap()
} else {
Term::Error.delay()
};
term = if full_cast { term = if full_cast {
convert_data_to_type(term, &tipo, otherwise) unknown_data_to_type(term, &tipo)
} else { } else {
known_data_to_type(term, &tipo) known_data_to_type(term, &tipo)
}; };

View File

@ -101,6 +101,10 @@ pub enum Air {
Let { Let {
name: String, name: String,
}, },
SoftCastLet {
name: String,
tipo: Rc<Type>,
},
CastFromData { CastFromData {
tipo: Rc<Type>, tipo: Rc<Type>,
full_cast: bool, full_cast: bool,

View File

@ -70,7 +70,7 @@ pub struct AssignmentProperties {
pub kind: TypedAssignmentKind, pub kind: TypedAssignmentKind,
pub remove_unused: bool, pub remove_unused: bool,
pub full_check: bool, pub full_check: bool,
pub otherwise: AirTree, pub otherwise: Option<AirTree>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -948,9 +948,11 @@ pub fn unknown_data_to_type(term: Term<Name>, field_type: &Type) -> Term<Name> {
/// Due to the nature of the types BLS12_381_G1Element and BLS12_381_G2Element and String coming from bytearray /// Due to the nature of the types BLS12_381_G1Element and BLS12_381_G2Element and String coming from bytearray
/// We don't have error handling if the bytearray is not properly aligned to the type. Oh well lol /// We don't have error handling if the bytearray is not properly aligned to the type. Oh well lol
/// For BLS12_381_G1Element and BLS12_381_G2Element, hash to group exists so just adopt that. /// For BLS12_381_G1Element and BLS12_381_G2Element, hash to group exists so just adopt that.
pub fn unknown_data_to_type_otherwise( pub fn softcast_data_to_type_otherwise(
term: Term<Name>, value: Term<Name>,
name: &String,
field_type: &Type, field_type: &Type,
then: Term<Name>,
otherwise_delayed: Term<Name>, otherwise_delayed: Term<Name>,
) -> Term<Name> { ) -> Term<Name> {
let uplc_type = field_type.get_uplc_type(); let uplc_type = field_type.get_uplc_type();
@ -961,59 +963,67 @@ pub fn unknown_data_to_type_otherwise(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::un_i_data().apply(Term::var("__val")).delay(), then.lambda(name)
.apply(Term::un_i_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::ByteString) => Term::var("__val") Some(UplcType::ByteString) => Term::var("__val")
.choose_data( .choose_data(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::un_b_data().apply(Term::var("__val")).delay(), then.lambda(name)
.apply(Term::un_b_data().apply(Term::var("__val")))
.delay(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::String) => Term::var("__val") Some(UplcType::String) => Term::var("__val")
.choose_data( .choose_data(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::decode_utf8() then.lambda(name)
.apply(Term::un_b_data().apply(Term::var("__val"))) .apply(Term::decode_utf8().apply(Term::un_b_data().apply(Term::var("__val"))))
.delay(), .delay(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val") Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val")
.choose_data( .choose_data(
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::unmap_data().apply(Term::var("__val")).delay(), then.lambda(name)
.apply(Term::unmap_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::List(_)) => Term::var("__val") Some(UplcType::List(_)) => Term::var("__val")
.choose_data( .choose_data(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::unlist_data().apply(Term::var("__val")).delay(), then.lambda(name)
.apply(Term::unlist_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Bls12_381G1Element) => Term::var("__val") Some(UplcType::Bls12_381G1Element) => Term::var("__val")
.choose_data( .choose_data(
@ -1021,26 +1031,32 @@ pub fn unknown_data_to_type_otherwise(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::bls12_381_g1_uncompress() then.lambda(name)
.apply(Term::un_b_data().apply(Term::var("__val"))) .apply(
Term::bls12_381_g1_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val"))),
)
.delay(), .delay(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Bls12_381G2Element) => Term::var("__val") Some(UplcType::Bls12_381G2Element) => Term::var("__val")
.choose_data( .choose_data(
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
Term::bls12_381_g2_uncompress() then.lambda(name)
.apply(Term::un_b_data().apply(Term::var("__val"))) .apply(
Term::bls12_381_g2_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val"))),
)
.delay(), .delay(),
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"), Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"),
Some(UplcType::Pair(_, _)) => Term::var("__val") Some(UplcType::Pair(_, _)) => Term::var("__val")
.choose_data( .choose_data(
@ -1055,11 +1071,18 @@ pub fn unknown_data_to_type_otherwise(
Term::tail_list() Term::tail_list()
.apply(Term::var("__tail")) .apply(Term::var("__tail"))
.choose_list( .choose_list(
Term::mk_pair_data() then.lambda(name)
.apply( .apply(
Term::head_list().apply(Term::var("__list_data")), Term::mk_pair_data()
.apply(
Term::head_list()
.apply(Term::var("__list_data")),
)
.apply(
Term::head_list()
.apply(Term::var("__tail")),
),
) )
.apply(Term::head_list().apply(Term::var("__tail")))
.delay(), .delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
) )
@ -1080,25 +1103,22 @@ pub fn unknown_data_to_type_otherwise(
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Bool) => Term::var("__val") Some(UplcType::Bool) => Term::var("__val")
.choose_data( .choose_data(
Term::snd_pair() Term::snd_pair()
.apply(Term::var("__pair__")) .apply(Term::var("__pair__"))
.choose_list( .choose_list(
Term::equals_integer() Term::less_than_equals_integer()
.apply(Term::integer(1.into())) .apply(Term::integer(2.into()))
.apply(Term::fst_pair().apply(Term::var("__pair__"))) .apply(Term::fst_pair().apply(Term::var("__pair__")))
.delayed_if_then_else( .delayed_if_then_else(
Term::bool(true), otherwise_delayed.clone(),
Term::equals_integer() then.lambda(name).apply(
.apply(Term::integer(0.into())) Term::equals_integer()
.apply(Term::fst_pair().apply(Term::var("__pair__"))) .apply(Term::fst_pair().apply(Term::var("__pair__")))
.if_then_else( .apply(Term::integer(1.into())),
Term::bool(false).delay(), ),
otherwise_delayed.clone(),
)
.force(),
) )
.delay(), .delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
@ -1114,7 +1134,7 @@ pub fn unknown_data_to_type_otherwise(
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Unit) => Term::var("__val") Some(UplcType::Unit) => Term::var("__val")
.choose_data( .choose_data(
Term::equals_integer() Term::equals_integer()
@ -1123,7 +1143,10 @@ pub fn unknown_data_to_type_otherwise(
.if_then_else( .if_then_else(
Term::snd_pair() Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__val"))) .apply(Term::unconstr_data().apply(Term::var("__val")))
.choose_list(Term::unit().delay(), otherwise_delayed.clone()) .choose_list(
then.lambda(name).apply(Term::unit()).delay(),
otherwise_delayed.clone(),
)
.force() .force()
.delay(), .delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
@ -1137,13 +1160,13 @@ pub fn unknown_data_to_type_otherwise(
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
Some(UplcType::Data) => term, Some(UplcType::Data) => then.lambda(name).apply(value),
// constr type // constr type
None => Term::var("__val") None => Term::var("__val")
.choose_data( .choose_data(
Term::var("__val").delay(), then.lambda(name).apply(Term::var("__val")).delay(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(), otherwise_delayed.clone(),
@ -1151,7 +1174,7 @@ pub fn unknown_data_to_type_otherwise(
) )
.force() .force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(value),
} }
} }
@ -1354,30 +1377,32 @@ pub fn list_access_to_uplc(
let tail_name = |id| format!("tail_id_{}", id); let tail_name = |id| format!("tail_id_{}", id);
let head_item = |name, tipo: &Rc<Type>, tail_name: &str| { let head_item = |name, tipo: &Rc<Type>, tail_name: &str, then: Term<Name>| {
if name == "_" { if name == "_" {
Term::unit() then
} else if tipo.is_pair() && is_list_accessor { } else if tipo.is_pair() && is_list_accessor {
Term::head_list().apply(Term::var(tail_name.to_string())) Term::head_list().apply(Term::var(tail_name.to_string()))
} else if matches!(expect_level, ExpectLevel::Full) { } else if matches!(expect_level, ExpectLevel::Full) {
// Expect level is full so we have an unknown piece of data to cast // Expect level is full so we have an unknown piece of data to cast
if otherwise_delayed == Term::Error.delay() { if otherwise_delayed == Term::Error.delay() {
unknown_data_to_type( then.lambda(name).apply(unknown_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
) ))
} else { } else {
unknown_data_to_type_otherwise( softcast_data_to_type_otherwise(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
name,
&tipo.to_owned(), &tipo.to_owned(),
then,
otherwise_delayed.clone(), otherwise_delayed.clone(),
) )
} }
} else { } else {
known_data_to_type( then.lambda(name).apply(known_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
) ))
} }
}; };
@ -1399,48 +1424,56 @@ pub fn list_access_to_uplc(
// case for no tail, but last item // case for no tail, but last item
let tail_name = tail_name(id); let tail_name = tail_name(id);
let head_item = head_item(name, tipo, &tail_name); // let head_item = head_item(name, tipo, &tail_name);
match expect_level { match expect_level {
ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name), ExpectLevel::None => {
head_item(name, tipo, &tail_name, acc).lambda(tail_name)
}
ExpectLevel::Full | ExpectLevel::Items => { ExpectLevel::Full | ExpectLevel::Items => {
if otherwise_delayed == Term::Error.delay() && tail_present { if otherwise_delayed == Term::Error.delay() && tail_present {
// No need to check last item if tail was present // No need to check last item if tail was present
acc.lambda(name).apply(head_item).lambda(tail_name) head_item(name, tipo, &tail_name, acc).lambda(tail_name)
} else if tail_present { } else if tail_present {
// Custom error instead of trying to do head_item on a possibly empty list. // Custom error instead of trying to do head_item on a possibly empty list.
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.choose_list( .choose_list(
otherwise_delayed.clone(), otherwise_delayed.clone(),
acc.lambda(name).apply(head_item).delay(), head_item(name, tipo, &tail_name, acc).delay(),
) )
.force() .force()
.lambda(tail_name) .lambda(tail_name)
} else if otherwise_delayed == Term::Error.delay() { } else if otherwise_delayed == Term::Error.delay() {
// Check head is last item in this list // Check head is last item in this list
Term::tail_list() head_item(
.apply(Term::var(tail_name.to_string())) name,
.choose_list(acc.delay(), Term::Error.delay()) tipo,
.force() &tail_name,
.lambda(name) Term::tail_list()
.apply(head_item) .apply(Term::var(tail_name.to_string()))
.lambda(tail_name) .choose_list(acc.delay(), Term::Error.delay())
.force(),
)
.lambda(tail_name)
} else { } else {
// Custom error if list is not empty after this head // Custom error if list is not empty after this head
Term::var(tail_name.to_string()) head_item(
.choose_list( name,
otherwise_delayed.clone(), tipo,
Term::tail_list() &tail_name,
.apply(Term::var(tail_name.to_string())) Term::var(tail_name.to_string())
.choose_list(acc.delay(), otherwise_delayed.clone()) .choose_list(
.force() otherwise_delayed.clone(),
.lambda(name) Term::tail_list()
.apply(head_item) .apply(Term::var(tail_name.to_string()))
.delay(), .choose_list(acc.delay(), otherwise_delayed.clone())
) .force(),
.force() )
.lambda(tail_name) .delay(),
)
.force()
.lambda(tail_name)
} }
} }
} }
@ -1450,26 +1483,33 @@ pub fn list_access_to_uplc(
// case for every item except the last item // case for every item except the last item
let tail_name = tail_name(id); let tail_name = tail_name(id);
let head_item = head_item(name, tipo, &tail_name); // let head_item = head_item(name, tipo, &tail_name);
if matches!(expect_level, ExpectLevel::None) if matches!(expect_level, ExpectLevel::None)
|| otherwise_delayed == Term::Error.delay() || otherwise_delayed == Term::Error.delay()
{ {
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))) head_item(
.lambda(name) name,
.apply(head_item) tipo,
.lambda(tail_name) &tail_name,
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))),
)
.lambda(tail_name)
} else { } else {
// case for a custom error if the list is empty at this point // case for a custom error if the list is empty at this point
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.choose_list( .choose_list(
otherwise_delayed.clone(), otherwise_delayed.clone(),
acc.apply( head_item(
Term::tail_list().apply(Term::var(tail_name.to_string())), name,
) tipo,
.lambda(name) &tail_name,
.apply(head_item) acc.apply(
.delay(), Term::tail_list().apply(Term::var(tail_name.to_string())),
)
.delay(),
),
) )
.force() .force()
.lambda(tail_name) .lambda(tail_name)

View File

@ -125,6 +125,13 @@ pub enum AirTree {
value: Box<AirTree>, value: Box<AirTree>,
then: Box<AirTree>, then: Box<AirTree>,
}, },
SoftCastLet {
name: String,
tipo: Rc<Type>,
value: Box<AirTree>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
DefineFunc { DefineFunc {
func_name: String, func_name: String,
module_name: String, module_name: String,
@ -312,7 +319,6 @@ pub enum AirTree {
CastFromData { CastFromData {
tipo: Rc<Type>, tipo: Rc<Type>,
value: Box<AirTree>, value: Box<AirTree>,
otherwise: Box<AirTree>,
full_cast: bool, full_cast: bool,
}, },
CastToData { CastToData {
@ -589,39 +595,26 @@ impl AirTree {
} }
} }
pub fn assign_literal_pattern( pub fn soft_cast_assignment(
name: String, name: impl ToString,
pattern: AirTree,
rhs: AirTree,
tipo: Rc<Type>, tipo: Rc<Type>,
props: AssignmentProperties, value: AirTree,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
assert!(props.kind.is_expect()); AirTree::SoftCastLet {
name: name.to_string(),
let expect = AirTree::binop(
BinOp::Eq,
bool(),
pattern,
AirTree::local_var(&name, tipo.clone()),
tipo, tipo,
); value: value.into(),
then: then.into(),
let expr = AirTree::let_assignment(name, rhs, expect); otherwise: otherwise.into(),
}
AirTree::assert_bool(true, expr, then, props.otherwise.clone())
} }
pub fn cast_from_data( pub fn cast_from_data(value: AirTree, tipo: Rc<Type>, full_cast: bool) -> AirTree {
value: AirTree,
tipo: Rc<Type>,
otherwise: AirTree,
full_cast: bool,
) -> AirTree {
AirTree::CastFromData { AirTree::CastFromData {
tipo, tipo,
value: value.into(), value: value.into(),
otherwise: otherwise.into(),
full_cast, full_cast,
} }
} }
@ -888,7 +881,6 @@ impl AirTree {
vec![list_of_fields], vec![list_of_fields],
), ),
tipo.clone(), tipo.clone(),
AirTree::error(void(), false),
false, false,
) )
} }
@ -994,7 +986,6 @@ impl AirTree {
vec![tuple], vec![tuple],
), ),
tipo.clone(), tipo.clone(),
AirTree::error(void(), false),
false, false,
) )
} }
@ -1045,6 +1036,32 @@ impl AirTree {
} }
} }
pub fn expect_on_list2() -> AirTree {
let expect_on_list = AirTree::var(
ValueConstructor::public(
void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
module: "".to_string(),
arity: 1,
location: Span::empty(),
builtin: None,
},
),
EXPECT_ON_LIST,
"",
);
let list_var = AirTree::local_var("__list_to_check", list(data()));
AirTree::call(
AirTree::local_var("__check_with", void()),
void(),
vec![list_var, expect_on_list],
)
}
pub fn expect_on_list() -> AirTree { pub fn expect_on_list() -> AirTree {
let list_var = AirTree::local_var("__list_to_check", list(data())); let list_var = AirTree::local_var("__list_to_check", list(data()));
@ -1108,6 +1125,21 @@ impl AirTree {
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
} }
AirTree::SoftCastLet {
name,
tipo,
value,
then,
otherwise,
} => {
air_vec.push(Air::SoftCastLet {
name: name.clone(),
tipo: tipo.clone(),
});
value.create_air_vec(air_vec);
then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::DefineFunc { AirTree::DefineFunc {
func_name, func_name,
module_name, module_name,
@ -1457,7 +1489,6 @@ impl AirTree {
AirTree::CastFromData { AirTree::CastFromData {
tipo, tipo,
value, value,
otherwise,
full_cast, full_cast,
} => { } => {
air_vec.push(Air::CastFromData { air_vec.push(Air::CastFromData {
@ -1466,9 +1497,6 @@ impl AirTree {
}); });
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
if *full_cast {
otherwise.create_air_vec(air_vec);
}
} }
AirTree::CastToData { tipo, value } => { AirTree::CastToData { tipo, value } => {
air_vec.push(Air::CastToData { tipo: tipo.clone() }); air_vec.push(Air::CastToData { tipo: tipo.clone() });
@ -1670,6 +1698,7 @@ impl AirTree {
| AirTree::PairClause { then, .. } | AirTree::PairClause { then, .. }
| AirTree::Finally { then, .. } | AirTree::Finally { then, .. }
| AirTree::Let { then, .. } | AirTree::Let { then, .. }
| AirTree::SoftCastLet { then, .. }
| AirTree::DefineFunc { then, .. } | AirTree::DefineFunc { then, .. }
| AirTree::DefineCyclicFuncs { then, .. } | AirTree::DefineCyclicFuncs { then, .. }
| AirTree::AssertConstr { then, .. } | AirTree::AssertConstr { then, .. }
@ -1715,7 +1744,8 @@ impl AirTree {
| AirTree::Constr { tipo, .. } | AirTree::Constr { tipo, .. }
| AirTree::ErrorTerm { tipo, .. } | AirTree::ErrorTerm { tipo, .. }
| AirTree::Trace { tipo, .. } | AirTree::Trace { tipo, .. }
| AirTree::Pair { tipo, .. } => vec![tipo], | AirTree::Pair { tipo, .. }
| AirTree::SoftCastLet { tipo, .. } => vec![tipo],
AirTree::FieldsExpose { indices, .. } => { AirTree::FieldsExpose { indices, .. } => {
let mut types = vec![]; let mut types = vec![];
@ -1821,6 +1851,30 @@ impl AirTree {
); );
} }
AirTree::SoftCastLet {
name: _,
tipo: _,
value,
then: _,
otherwise,
} => {
value.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::ThirdField,
with,
apply_with_func_last,
);
otherwise.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::FifthField,
with,
apply_with_func_last,
);
}
AirTree::AssertConstr { AirTree::AssertConstr {
constr_index: _, constr_index: _,
constr, constr,
@ -2291,7 +2345,6 @@ impl AirTree {
AirTree::CastFromData { AirTree::CastFromData {
tipo: _, tipo: _,
value, value,
otherwise: _,
full_cast: _, full_cast: _,
} => { } => {
value.do_traverse_tree_with( value.do_traverse_tree_with(
@ -2560,6 +2613,21 @@ impl AirTree {
apply_with_func_last, apply_with_func_last,
); );
} }
AirTree::SoftCastLet {
name: _,
tipo: _,
value: _,
then,
otherwise: _,
} => {
then.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::FourthField,
with,
apply_with_func_last,
);
}
AirTree::AssertConstr { AirTree::AssertConstr {
constr_index: _, constr_index: _,
constr: _, constr: _,
@ -2806,6 +2874,18 @@ impl AirTree {
Fields::ThirdField => then.as_mut().do_find_air_tree_node(tree_path_iter), Fields::ThirdField => then.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"), _ => panic!("Tree Path index outside tree children nodes"),
}, },
AirTree::SoftCastLet {
name: _,
tipo: _,
value,
then,
otherwise,
} => match field {
Fields::ThirdField => value.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::FourthField => then.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::FifthField => otherwise.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"),
},
AirTree::AssertConstr { AirTree::AssertConstr {
constr_index: _, constr_index: _,
constr, constr,
@ -3011,11 +3091,9 @@ impl AirTree {
AirTree::CastFromData { AirTree::CastFromData {
tipo: _, tipo: _,
value, value,
otherwise,
full_cast: _, full_cast: _,
} => match field { } => match field {
Fields::SecondField => value.as_mut().do_find_air_tree_node(tree_path_iter), Fields::SecondField => value.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::ThirdField => otherwise.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"), _ => panic!("Tree Path index outside tree children nodes"),
}, },
AirTree::CastToData { tipo: _, value } => match field { AirTree::CastToData { tipo: _, value } => match field {