feat: Add specific messages for using expect with booleans

TODO: fill out the rest of the expects with messages
This commit is contained in:
microproofs 2024-01-02 21:52:01 -05:00 committed by Kasey
parent 7b452c21f0
commit 71cfb6f6af
8 changed files with 184 additions and 68 deletions

View File

@ -27,9 +27,9 @@ use crate::{
gen_uplc::builder::{ gen_uplc::builder::{
check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations, 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, find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name,
get_generic_id_and_type, get_variant_name, monomorphize, pattern_has_conditions, get_generic_id_and_type, get_src_code_by_span, get_variant_name, monomorphize,
wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause, pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction,
CONSTR_INDEX_MISMATCH, SpecificClause, CONSTR_INDEX_MISMATCH,
}, },
tipo::{ tipo::{
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
@ -52,16 +52,21 @@ use self::{
#[derive(Clone)] #[derive(Clone)]
pub struct CodeGenerator<'a> { pub struct CodeGenerator<'a> {
defined_functions: IndexMap<FunctionAccessKey, ()>, /// immutable index maps
functions: IndexMap<FunctionAccessKey, &'a TypedFunction>, functions: IndexMap<FunctionAccessKey, &'a TypedFunction>,
data_types: IndexMap<DataTypeKey, &'a TypedDataType>, data_types: IndexMap<DataTypeKey, &'a TypedDataType>,
module_types: IndexMap<&'a String, &'a TypeInfo>, module_types: IndexMap<&'a String, &'a TypeInfo>,
module_src: IndexMap<String, String>,
/// immutable option
tracing: bool,
/// mutable index maps that are reset
defined_functions: IndexMap<FunctionAccessKey, ()>,
special_functions: CodeGenSpecialFuncs, special_functions: CodeGenSpecialFuncs,
code_gen_functions: IndexMap<String, CodeGenFunction>, code_gen_functions: IndexMap<String, CodeGenFunction>,
zero_arg_functions: IndexMap<(FunctionAccessKey, Variant), Vec<Air>>, zero_arg_functions: IndexMap<(FunctionAccessKey, Variant), Vec<Air>>,
cyclic_functions: cyclic_functions:
IndexMap<(FunctionAccessKey, Variant), (CycleFunctionNames, usize, FunctionAccessKey)>, IndexMap<(FunctionAccessKey, Variant), (CycleFunctionNames, usize, FunctionAccessKey)>,
tracing: bool, /// mutable and reset as well
id_gen: IdGenerator, id_gen: IdGenerator,
} }
@ -70,18 +75,20 @@ impl<'a> CodeGenerator<'a> {
functions: IndexMap<FunctionAccessKey, &'a TypedFunction>, functions: IndexMap<FunctionAccessKey, &'a TypedFunction>,
data_types: IndexMap<DataTypeKey, &'a TypedDataType>, data_types: IndexMap<DataTypeKey, &'a TypedDataType>,
module_types: IndexMap<&'a String, &'a TypeInfo>, module_types: IndexMap<&'a String, &'a TypeInfo>,
module_src: IndexMap<String, String>,
tracing: bool, tracing: bool,
) -> Self { ) -> Self {
CodeGenerator { CodeGenerator {
defined_functions: IndexMap::new(),
functions, functions,
data_types, data_types,
module_types, module_types,
module_src,
tracing,
defined_functions: IndexMap::new(),
special_functions: CodeGenSpecialFuncs::new(), special_functions: CodeGenSpecialFuncs::new(),
code_gen_functions: IndexMap::new(), code_gen_functions: IndexMap::new(),
zero_arg_functions: IndexMap::new(), zero_arg_functions: IndexMap::new(),
cyclic_functions: IndexMap::new(), cyclic_functions: IndexMap::new(),
tracing,
id_gen: IdGenerator::new(), id_gen: IdGenerator::new(),
} }
} }
@ -101,7 +108,6 @@ impl<'a> CodeGenerator<'a> {
&mut self, &mut self,
module_name: String, module_name: String,
function_name: String, function_name: String,
_variant_name: String,
value: &'a TypedFunction, value: &'a TypedFunction,
) -> Option<&'a TypedFunction> { ) -> Option<&'a TypedFunction> {
self.functions.insert( self.functions.insert(
@ -121,8 +127,9 @@ impl<'a> CodeGenerator<'a> {
params, params,
.. ..
}: &TypedValidator, }: &TypedValidator,
module_name: &String,
) -> Program<Name> { ) -> Program<Name> {
let mut air_tree_fun = self.build(&fun.body); let mut air_tree_fun = self.build(&fun.body, module_name);
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing); air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
@ -141,7 +148,7 @@ impl<'a> CodeGenerator<'a> {
if let Some(other) = other_fun { if let Some(other) = other_fun {
self.reset(false); self.reset(false);
let mut air_tree_fun_other = self.build(&other.body); let mut air_tree_fun_other = self.build(&other.body, module_name);
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing); air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
@ -177,8 +184,8 @@ impl<'a> CodeGenerator<'a> {
self.finalize(term) self.finalize(term)
} }
pub fn generate_test(&mut self, test_body: &TypedExpr) -> Program<Name> { pub fn generate_test(&mut self, test_body: &TypedExpr, module_name: &String) -> Program<Name> {
let mut air_tree = self.build(test_body); let mut air_tree = self.build(test_body, module_name);
air_tree = AirTree::no_op().hoist_over(air_tree); air_tree = AirTree::no_op().hoist_over(air_tree);
@ -215,7 +222,7 @@ impl<'a> CodeGenerator<'a> {
program program
} }
fn build(&mut self, body: &TypedExpr) -> AirTree { fn build(&mut self, body: &TypedExpr, module_name: &String) -> AirTree {
match body { match body {
TypedExpr::UInt { value, .. } => AirTree::int(value), TypedExpr::UInt { value, .. } => AirTree::int(value),
TypedExpr::String { value, .. } => AirTree::string(value), TypedExpr::String { value, .. } => AirTree::string(value),
@ -228,10 +235,13 @@ impl<'a> CodeGenerator<'a> {
"Sequence or Pipeline should have at least one expression" "Sequence or Pipeline should have at least one expression"
); );
let mut last_exp = self.build(&expressions.pop().unwrap_or_else(|| unreachable!())); let mut last_exp = self.build(
&expressions.pop().unwrap_or_else(|| unreachable!()),
module_name,
);
while let Some(expression) = expressions.pop() { while let Some(expression) = expressions.pop() {
let exp_tree = self.build(&expression); let exp_tree = self.build(&expression, module_name);
last_exp = exp_tree.hoist_over(last_exp); last_exp = exp_tree.hoist_over(last_exp);
} }
@ -249,7 +259,7 @@ impl<'a> CodeGenerator<'a> {
args.iter() args.iter()
.map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string())
.collect_vec(), .collect_vec(),
self.build(body), self.build(body, module_name),
), ),
TypedExpr::List { TypedExpr::List {
@ -258,9 +268,12 @@ impl<'a> CodeGenerator<'a> {
tail, tail,
.. ..
} => AirTree::list( } => AirTree::list(
elements.iter().map(|elem| self.build(elem)).collect_vec(), elements
.iter()
.map(|elem| self.build(elem, module_name))
.collect_vec(),
tipo.clone(), tipo.clone(),
tail.as_ref().map(|tail| self.build(tail)), tail.as_ref().map(|tail| self.build(tail, module_name)),
), ),
TypedExpr::Call { TypedExpr::Call {
@ -302,9 +315,12 @@ impl<'a> CodeGenerator<'a> {
.zip(constr_tipo.arg_types().unwrap()) .zip(constr_tipo.arg_types().unwrap())
.map(|(arg, tipo)| { .map(|(arg, tipo)| {
if tipo.is_data() { if tipo.is_data() {
AirTree::cast_to_data(self.build(&arg.value), arg.value.tipo()) AirTree::cast_to_data(
self.build(&arg.value, module_name),
arg.value.tipo(),
)
} else { } else {
self.build(&arg.value) self.build(&arg.value, module_name)
} }
}) })
.collect_vec(); .collect_vec();
@ -331,7 +347,7 @@ impl<'a> CodeGenerator<'a> {
.iter() .iter()
.zip(fun_arg_types) .zip(fun_arg_types)
.map(|(arg, arg_tipo)| { .map(|(arg, arg_tipo)| {
let mut arg_val = self.build(&arg.value); let mut arg_val = self.build(&arg.value, module_name);
if arg_tipo.is_data() && !arg.value.tipo().is_data() { if arg_tipo.is_data() && !arg.value.tipo().is_data() {
arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
@ -343,7 +359,11 @@ impl<'a> CodeGenerator<'a> {
if let Some(func) = builtin { if let Some(func) = builtin {
AirTree::builtin(*func, tipo.clone(), func_args) AirTree::builtin(*func, tipo.clone(), func_args)
} else { } else {
AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) AirTree::call(
self.build(fun.as_ref(), module_name),
tipo.clone(),
func_args,
)
} }
} }
@ -370,7 +390,7 @@ impl<'a> CodeGenerator<'a> {
.iter() .iter()
.zip(fun_arg_types) .zip(fun_arg_types)
.map(|(arg, arg_tipo)| { .map(|(arg, arg_tipo)| {
let mut arg_val = self.build(&arg.value); let mut arg_val = self.build(&arg.value, module_name);
if arg_tipo.is_data() && !arg.value.tipo().is_data() { if arg_tipo.is_data() && !arg.value.tipo().is_data() {
arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
@ -382,7 +402,11 @@ impl<'a> CodeGenerator<'a> {
if let Some(func) = builtin { if let Some(func) = builtin {
AirTree::builtin(*func, tipo.clone(), func_args) AirTree::builtin(*func, tipo.clone(), func_args)
} else { } else {
AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) AirTree::call(
self.build(fun.as_ref(), module_name),
tipo.clone(),
func_args,
)
} }
} }
_ => { _ => {
@ -397,7 +421,7 @@ impl<'a> CodeGenerator<'a> {
.iter() .iter()
.zip(fun_arg_types) .zip(fun_arg_types)
.map(|(arg, arg_tipo)| { .map(|(arg, arg_tipo)| {
let mut arg_val = self.build(&arg.value); let mut arg_val = self.build(&arg.value, module_name);
if arg_tipo.is_data() && !arg.value.tipo().is_data() { if arg_tipo.is_data() && !arg.value.tipo().is_data() {
arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
@ -406,7 +430,11 @@ impl<'a> CodeGenerator<'a> {
}) })
.collect_vec(); .collect_vec();
AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) AirTree::call(
self.build(fun.as_ref(), module_name),
tipo.clone(),
func_args,
)
} }
}, },
TypedExpr::BinOp { TypedExpr::BinOp {
@ -418,8 +446,8 @@ impl<'a> CodeGenerator<'a> {
} => AirTree::binop( } => AirTree::binop(
*name, *name,
tipo.clone(), tipo.clone(),
self.build(left), self.build(left, module_name),
self.build(right), self.build(right, module_name),
left.tipo(), left.tipo(),
), ),
@ -428,11 +456,12 @@ impl<'a> CodeGenerator<'a> {
value, value,
pattern, pattern,
kind, kind,
location,
.. ..
} => { } => {
let replaced_type = convert_opaque_type(tipo, &self.data_types); let replaced_type = convert_opaque_type(tipo, &self.data_types);
let air_value = self.build(value); let air_value = self.build(value, module_name);
self.assignment( self.assignment(
pattern, pattern,
@ -443,13 +472,19 @@ impl<'a> CodeGenerator<'a> {
kind: *kind, kind: *kind,
remove_unused: kind.is_let(), remove_unused: kind.is_let(),
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
location: *location,
module_name: module_name.clone(),
}, },
) )
} }
TypedExpr::Trace { TypedExpr::Trace {
tipo, then, text, .. tipo, then, text, ..
} => AirTree::trace(self.build(text), tipo.clone(), self.build(then)), } => AirTree::trace(
self.build(text, module_name),
tipo.clone(),
self.build(then, module_name),
),
TypedExpr::When { TypedExpr::When {
tipo, tipo,
@ -464,11 +499,11 @@ impl<'a> CodeGenerator<'a> {
} else if clauses.len() == 1 { } else if clauses.len() == 1 {
let last_clause = clauses.pop().unwrap(); let last_clause = clauses.pop().unwrap();
let clause_then = self.build(&last_clause.then); let clause_then = self.build(&last_clause.then, module_name);
let subject_type = subject.tipo(); let subject_type = subject.tipo();
let subject_val = self.build(subject); let subject_val = self.build(subject, module_name);
let assignment = self.assignment( let assignment = self.assignment(
&last_clause.pattern, &last_clause.pattern,
@ -479,6 +514,8 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::Let, kind: AssignmentKind::Let,
remove_unused: false, remove_unused: false,
full_check: false, full_check: false,
location: Span::empty(),
module_name: module_name.clone(),
}, },
); );
@ -513,9 +550,11 @@ impl<'a> CodeGenerator<'a> {
constr_var.clone(), constr_var.clone(),
subject_name.clone(), subject_name.clone(),
), ),
module_name,
); );
let constr_assign = AirTree::let_assignment(&constr_var, self.build(subject)); let constr_assign =
AirTree::let_assignment(&constr_var, self.build(subject, module_name));
let when_assign = AirTree::when( let when_assign = AirTree::when(
subject_name, subject_name,
@ -537,10 +576,15 @@ impl<'a> CodeGenerator<'a> {
} => AirTree::if_branches( } => AirTree::if_branches(
branches branches
.iter() .iter()
.map(|branch| (self.build(&branch.condition), self.build(&branch.body))) .map(|branch| {
(
self.build(&branch.condition, module_name),
self.build(&branch.body, module_name),
)
})
.collect_vec(), .collect_vec(),
tipo.clone(), tipo.clone(),
self.build(final_else), self.build(final_else, module_name),
), ),
TypedExpr::RecordAccess { TypedExpr::RecordAccess {
@ -550,7 +594,7 @@ impl<'a> CodeGenerator<'a> {
.. ..
} => { } => {
if check_replaceable_opaque_type(&record.tipo(), &self.data_types) { if check_replaceable_opaque_type(&record.tipo(), &self.data_types) {
self.build(record) self.build(record, module_name)
} else { } else {
let function_name = format!("__access_index_{}", *index); let function_name = format!("__access_index_{}", *index);
@ -582,7 +626,7 @@ impl<'a> CodeGenerator<'a> {
void(), void(),
), ),
list(data()), list(data()),
vec![self.build(record)], vec![self.build(record, module_name)],
); );
AirTree::index_access(function_name, tipo.clone(), list_of_fields) AirTree::index_access(function_name, tipo.clone(), list_of_fields)
@ -653,7 +697,10 @@ impl<'a> CodeGenerator<'a> {
}, },
TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple( TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple(
elems.iter().map(|elem| self.build(elem)).collect_vec(), elems
.iter()
.map(|elem| self.build(elem, module_name))
.collect_vec(),
tipo.clone(), tipo.clone(),
), ),
@ -661,7 +708,7 @@ impl<'a> CodeGenerator<'a> {
index, tuple, tipo, .. index, tuple, tipo, ..
} => { } => {
if tuple.tipo().is_2_tuple() { if tuple.tipo().is_2_tuple() {
AirTree::pair_index(*index, tipo.clone(), self.build(tuple)) AirTree::pair_index(*index, tipo.clone(), self.build(tuple, module_name))
} else { } else {
let function_name = format!("__access_index_{}", *index); let function_name = format!("__access_index_{}", *index);
@ -687,7 +734,11 @@ impl<'a> CodeGenerator<'a> {
); );
} }
AirTree::index_access(function_name, tipo.clone(), self.build(tuple)) AirTree::index_access(
function_name,
tipo.clone(),
self.build(tuple, module_name),
)
} }
} }
@ -705,7 +756,7 @@ impl<'a> CodeGenerator<'a> {
.iter() .iter()
.sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index))
{ {
let arg_val = self.build(&arg.value); let arg_val = self.build(&arg.value, module_name);
if arg.index > highest_index { if arg.index > highest_index {
highest_index = arg.index; highest_index = arg.index;
@ -719,12 +770,12 @@ impl<'a> CodeGenerator<'a> {
index_types, index_types,
highest_index, highest_index,
tipo.clone(), tipo.clone(),
self.build(spread), self.build(spread, module_name),
update_args, update_args,
) )
} }
TypedExpr::UnOp { value, op, .. } => AirTree::unop(*op, self.build(value)), TypedExpr::UnOp { value, op, .. } => AirTree::unop(*op, self.build(value, module_name)),
TypedExpr::CurvePoint { point, .. } => AirTree::curve(*point.as_ref()), TypedExpr::CurvePoint { point, .. } => AirTree::curve(*point.as_ref()),
} }
} }
@ -737,13 +788,18 @@ impl<'a> CodeGenerator<'a> {
props: AssignmentProperties, props: AssignmentProperties,
) -> AirTree { ) -> AirTree {
assert!( assert!(
if let AirTree::Expression(AirExpression::Var { name, .. }) = &value { match &value {
name != "_" AirTree::Expression(AirExpression::Var { name, .. })
} else { if props.kind == AssignmentKind::Let =>
true {
name != "_"
}
_ => true,
}, },
"No discard expressions or let bindings should be in the tree at this point." "No discard expressions or let bindings should be in the tree at this point."
); );
// 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() { 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());
} else if !props.value_type.is_data() && tipo.is_data() { } else if !props.value_type.is_data() && tipo.is_data() {
@ -772,7 +828,10 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(name, int()), AirTree::local_var(name, int()),
int(), int(),
); );
AirTree::assert_bool(true, assignment.hoist_over(expect)) let msg =
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
AirTree::assert_bool(true, assignment.hoist_over(expect), msg)
} }
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
if props.full_check { if props.full_check {
@ -889,6 +948,8 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location,
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -930,6 +991,8 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location,
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -964,7 +1027,10 @@ 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) let msg =
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
AirTree::assert_bool(name == "True", value, msg)
} else if tipo.is_void() { } else if tipo.is_void() {
AirTree::let_assignment("_", value) AirTree::let_assignment("_", value)
} else { } else {
@ -1074,6 +1140,8 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location,
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -1162,6 +1230,8 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location,
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -1635,6 +1705,7 @@ impl<'a> CodeGenerator<'a> {
final_clause: TypedClause, final_clause: TypedClause,
subject_tipo: &Rc<Type>, subject_tipo: &Rc<Type>,
props: &mut ClauseProperties, props: &mut ClauseProperties,
module_name: &String,
) -> AirTree { ) -> AirTree {
assert!( assert!(
!subject_tipo.is_void(), !subject_tipo.is_void(),
@ -1643,7 +1714,7 @@ impl<'a> CodeGenerator<'a> {
props.complex_clause = false; props.complex_clause = false;
if let Some((clause, rest_clauses)) = clauses.split_first() { if let Some((clause, rest_clauses)) = clauses.split_first() {
let mut clause_then = self.build(&clause.then); let mut clause_then = self.build(&clause.then, module_name);
// handles clause guard if it exists // handles clause guard if it exists
if let Some(guard) = &clause.guard { if let Some(guard) = &clause.guard {
@ -1693,6 +1764,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
) )
} else if let Some(data_type) = data_type { } else if let Some(data_type) = data_type {
@ -1707,6 +1779,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
complex_clause, complex_clause,
) )
@ -1718,6 +1791,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
) )
} }
@ -1735,6 +1809,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
complex_clause, complex_clause,
) )
@ -1782,6 +1857,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
); );
}; };
@ -1884,6 +1960,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
next_tail_name, next_tail_name,
complex_clause, complex_clause,
@ -1896,6 +1973,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
) )
} }
@ -1943,6 +2021,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
) )
} else { } else {
@ -1957,6 +2036,7 @@ impl<'a> CodeGenerator<'a> {
final_clause, final_clause,
subject_tipo, subject_tipo,
&mut next_clause_props, &mut next_clause_props,
module_name,
), ),
props.complex_clause, props.complex_clause,
) )
@ -1969,7 +2049,7 @@ impl<'a> CodeGenerator<'a> {
assert!(final_clause.guard.is_none()); assert!(final_clause.guard.is_none());
let clause_then = self.build(&final_clause.then); let clause_then = self.build(&final_clause.then, module_name);
let (condition, assignments) = let (condition, assignments) =
self.clause_pattern(&final_clause.pattern, subject_tipo, props); self.clause_pattern(&final_clause.pattern, subject_tipo, props);
@ -2604,6 +2684,8 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::Expect, kind: AssignmentKind::Expect,
remove_unused: false, remove_unused: false,
full_check: true, full_check: true,
location: Span::empty(),
module_name: String::new(),
}, },
); );
@ -3466,7 +3548,8 @@ impl<'a> CodeGenerator<'a> {
}) })
.collect_vec(); .collect_vec();
let mut function_air_tree_body = self.build(&function_def.body); let mut function_air_tree_body =
self.build(&function_def.body, &generic_function_key.module_name);
function_air_tree_body.traverse_tree_with( function_air_tree_body.traverse_tree_with(
&mut |air_tree, _| { &mut |air_tree, _| {
@ -3495,7 +3578,8 @@ impl<'a> CodeGenerator<'a> {
.map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string())
.collect_vec(); .collect_vec();
let mut function_air_tree_body = self.build(&function_def.body); let mut function_air_tree_body =
self.build(&function_def.body, &generic_function_key.module_name);
function_air_tree_body.traverse_tree_with( function_air_tree_body.traverse_tree_with(
&mut |air_tree, _| { &mut |air_tree, _| {
@ -4381,14 +4465,12 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term); arg_stack.push(term);
} }
Air::AssertBool { is_true } => { Air::AssertBool { is_true, msg } => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let trace_term = if self.tracing { let trace_term = if self.tracing {
Term::Error.delayed_trace(Term::var( Term::Error.delayed_trace(Term::string(msg))
self.special_functions.use_function(INCORRECT_BOOLEAN),
))
} else { } else {
Term::Error Term::Error
}; };

View File

@ -92,6 +92,7 @@ pub enum Air {
}, },
AssertBool { AssertBool {
is_true: bool, is_true: bool,
msg: String,
}, },
// When // When
When { When {

View File

@ -82,6 +82,8 @@ pub struct AssignmentProperties {
pub kind: AssignmentKind, pub kind: AssignmentKind,
pub remove_unused: bool, pub remove_unused: bool,
pub full_check: bool, pub full_check: bool,
pub location: Span,
pub module_name: String,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -1861,3 +1863,17 @@ pub fn extract_constant(term: &Term<Name>) -> Option<Rc<UplcConstant>> {
} }
constant constant
} }
pub fn get_src_code_by_span(
module_name: &String,
span: &Span,
module_src: &IndexMap<String, String>,
) -> String {
let src = module_src
.get(module_name)
.unwrap_or_else(|| panic!("Missing module {module_name}"));
src.get(span.start..span.end)
.expect("Out of bounds span")
.to_string()
}

View File

@ -131,6 +131,7 @@ pub enum AirStatement {
AssertBool { AssertBool {
is_true: bool, is_true: bool,
value: Box<AirTree>, value: Box<AirTree>,
msg: String,
}, },
// Clause Guards // Clause Guards
ClauseGuard { ClauseGuard {
@ -501,11 +502,12 @@ impl AirTree {
hoisted_over: None, hoisted_over: None,
} }
} }
pub fn assert_bool(is_true: bool, value: AirTree) -> AirTree { pub fn assert_bool(is_true: bool, value: AirTree, msg: String) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::AssertBool { statement: AirStatement::AssertBool {
is_true, is_true,
value: value.into(), value: value.into(),
msg,
}, },
hoisted_over: None, hoisted_over: None,
} }
@ -950,8 +952,15 @@ impl AirTree {
}); });
constr.create_air_vec(air_vec); constr.create_air_vec(air_vec);
} }
AirStatement::AssertBool { is_true, value } => { AirStatement::AssertBool {
air_vec.push(Air::AssertBool { is_true: *is_true }); is_true,
value,
msg,
} => {
air_vec.push(Air::AssertBool {
is_true: *is_true,
msg: msg.clone(),
});
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
} }
AirStatement::ClauseGuard { AirStatement::ClauseGuard {

View File

@ -49,7 +49,7 @@ impl Validator {
module: &CheckedModule, module: &CheckedModule,
def: &TypedValidator, def: &TypedValidator,
) -> Vec<Result<Validator, Error>> { ) -> Vec<Result<Validator, Error>> {
let program = generator.generate(def).try_into().unwrap(); let program = generator.generate(def, &module.name).try_into().unwrap();
let is_multi_validator = def.other_fun.is_some(); let is_multi_validator = def.other_fun.is_some();

View File

@ -785,7 +785,6 @@ where
generator.insert_function( generator.insert_function(
module_name.to_string(), module_name.to_string(),
testable_validator.name.clone(), testable_validator.name.clone(),
String::new(),
testable_validator, testable_validator,
); );
} }
@ -808,13 +807,13 @@ where
let evaluation_hint = func_def.test_hint().map(|(bin_op, left_src, right_src)| { let evaluation_hint = func_def.test_hint().map(|(bin_op, left_src, right_src)| {
let left = generator let left = generator
.clone() .clone()
.generate_test(&left_src) .generate_test(&left_src, &module_name)
.try_into() .try_into()
.unwrap(); .unwrap();
let right = generator let right = generator
.clone() .clone()
.generate_test(&right_src) .generate_test(&right_src, &module_name)
.try_into() .try_into()
.unwrap(); .unwrap();
@ -825,7 +824,7 @@ where
} }
}); });
let program = generator.generate_test(body); let program = generator.generate_test(body, &module_name);
let script = Script::new( let script = Script::new(
input_path, input_path,

View File

@ -370,6 +370,8 @@ impl CheckedModules {
data_types.insert(k.clone(), v); data_types.insert(k.clone(), v);
} }
let mut module_src = IndexMap::new();
for module in self.values() { for module in self.values() {
for def in module.ast.definitions() { for def in module.ast.definitions() {
match def { match def {
@ -399,12 +401,19 @@ impl CheckedModules {
| Definition::Use(_) => {} | Definition::Use(_) => {}
} }
} }
module_src.insert(module.name.clone(), module.code.clone());
} }
let mut module_types_index = IndexMap::new(); let mut module_types_index = IndexMap::new();
module_types_index.extend(module_types); module_types_index.extend(module_types);
CodeGenerator::new(functions, data_types, module_types_index, tracing) CodeGenerator::new(
functions,
data_types,
module_types_index,
module_src,
tracing,
)
} }
} }

View File

@ -60,7 +60,7 @@ fn assert_uplc(source_code: &str, expected: Term<Name>, should_fail: bool) {
match &script.2 { match &script.2 {
TestType::Func(Function { body: func, .. }) => { TestType::Func(Function { body: func, .. }) => {
let program = generator.generate_test(func); let program = generator.generate_test(func, &script.1);
let debruijn_program: Program<DeBruijn> = program.try_into().unwrap(); let debruijn_program: Program<DeBruijn> = program.try_into().unwrap();
@ -89,7 +89,7 @@ fn assert_uplc(source_code: &str, expected: Term<Name>, should_fail: bool) {
} }
} }
TestType::Validator(func) => { TestType::Validator(func) => {
let program = generator.generate(func); let program = generator.generate(func, &script.1);
let debruijn_program: Program<DeBruijn> = program.try_into().unwrap(); let debruijn_program: Program<DeBruijn> = program.try_into().unwrap();