Finishing up codegen changes for pair

This commit is contained in:
microproofs 2024-03-28 13:59:56 -04:00 committed by Kasey
parent 963d275bb8
commit a8c8cf41cf
1 changed files with 867 additions and 622 deletions

View File

@ -1085,33 +1085,161 @@ impl<'a> CodeGenerator<'a> {
) )
} }
} }
// Pairs overlap by using the Constructor pattern type
// The logic is slightly different for pairs
Pattern::Constructor {
arguments,
constructor: PatternConstructor::Record { name, field_map },
tipo: constr_tipo,
..
} if tipo.is_pair() => {
// Constr and Pair execution branch
let field_map = field_map.clone();
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
for (index, arg) in constr_tipo.get_inner_types().iter().enumerate() {
let field_type = arg.clone();
type_map.insert(index, field_type);
}
assert!(type_map.len() >= arguments.len());
let mut fields = vec![];
let then = arguments
.iter()
.enumerate()
.rfold(then, |then, (index, arg)| {
let label = arg.label.clone().unwrap_or_default();
let field_index = if let Some(field_map) = &field_map {
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
} else {
index
};
let field_name = match &arg.value {
Pattern::Var { name, .. } => name.to_string(),
Pattern::Assign { name, .. } => name.to_string(),
Pattern::Discard { name, .. } => {
if props.full_check {
format!("__discard_{}_{}", name, index)
} else {
"_".to_string()
}
}
_ => format!(
"field_{}_span_{}_{}",
field_index,
arg.value.location().start,
arg.value.location().end
),
};
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
unreachable!(
"Missing type for field {} of constr {}",
field_index, name
)
});
let val = AirTree::local_var(&field_name, arg_type.clone());
let then = if field_name != "_" {
self.assignment(
&arg.value,
val,
then,
arg_type,
AssignmentProperties {
value_type: arg_type.clone(),
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
},
)
} else {
then
};
fields.push((field_index, field_name, arg_type.clone()));
then
});
fields.reverse();
// This `value` is either value param that was passed in or
// local var
let constructor_name = format!(
"__constructor_{}_span_{}_{}",
name,
pattern.location().start,
pattern.location().end
);
let local_value = AirTree::local_var(&constructor_name, tipo.clone());
let then = {
let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone())
} else {
(false, None)
};
assert!(fields.len() == 2);
AirTree::pair_access(
fields
.first()
.map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) })
.unwrap(),
fields
.last()
.map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) })
.unwrap(),
tipo.clone(),
local_value,
msg,
is_expect,
then,
)
};
AirTree::let_assignment(constructor_name, value, then)
}
Pattern::Constructor {
constructor: PatternConstructor::Record { name, .. },
..
} if tipo.is_bool() => {
assert!(props.kind.is_expect());
AirTree::assert_bool(name == "True", value, props.msg_func, then)
}
Pattern::Constructor { .. } if tipo.is_void() => {
// Void type is checked when casting from data
// So we just assign the value and move on
AirTree::let_assignment("_", value, then)
}
Pattern::Constructor { Pattern::Constructor {
arguments, arguments,
constructor: PatternConstructor::Record { name, field_map }, constructor: PatternConstructor::Record { name, field_map },
tipo: constr_tipo, tipo: constr_tipo,
.. ..
} => { } => {
// TODO: Implement Pair pattern // Constr execution branch
if tipo.is_bool() {
assert!(props.kind.is_expect());
AirTree::assert_bool(name == "True", value, props.msg_func, then)
} else if tipo.is_void() {
AirTree::let_assignment("_", value, then)
} else {
// Constr and Pair execution branch
let field_map = field_map.clone(); let field_map = field_map.clone();
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new(); let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
for (index, arg) in constr_tipo for (index, arg) in constr_tipo
.arg_types() .arg_types()
.unwrap_or_else(|| { .expect("Mismatched type")
// TODO refactor out pair from constr branch later
assert!(constr_tipo.is_pair());
constr_tipo.get_inner_types()
})
.iter() .iter()
.enumerate() .enumerate()
{ {
@ -1201,29 +1329,6 @@ impl<'a> CodeGenerator<'a> {
let then = if check_replaceable_opaque_type(tipo, &self.data_types) { let then = if check_replaceable_opaque_type(tipo, &self.data_types) {
AirTree::let_assignment(&fields[0].1, local_value, then) AirTree::let_assignment(&fields[0].1, local_value, then)
} else if tipo.is_pair() {
let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone())
} else {
(false, None)
};
assert!(fields.len() == 2);
AirTree::pair_access(
fields
.first()
.map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) })
.unwrap(),
fields
.last()
.map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) })
.unwrap(),
tipo.clone(),
local_value,
msg,
is_expect,
then,
)
} else { } else {
let (is_expect, msg) = if props.full_check { let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone()) (true, props.msg_func.clone())
@ -1233,17 +1338,12 @@ impl<'a> CodeGenerator<'a> {
AirTree::fields_expose(fields, local_value, msg, is_expect, then) AirTree::fields_expose(fields, local_value, msg, is_expect, then)
}; };
// TODO: See if we can combine these two if-conditions;
//
// i.e. can we lift data_type assignment out of the first if?
let then = if props.kind.is_expect() {
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
.unwrap_or_else(|| { .unwrap_or_else(|| unreachable!("Failed to find definition for {}", name));
unreachable!("Failed to find definition for {}", name)
});
if data_type.constructors.len() > 1 || props.full_check { let then = if props.kind.is_expect()
&& (data_type.constructors.len() > 1 || props.full_check)
{
let (index, _) = data_type let (index, _) = data_type
.constructors .constructors
.iter() .iter()
@ -1261,14 +1361,10 @@ impl<'a> CodeGenerator<'a> {
) )
} else { } else {
then then
}
} else {
then
}; };
AirTree::let_assignment(constructor_name, value, then) AirTree::let_assignment(constructor_name, value, then)
} }
}
Pattern::Tuple { Pattern::Tuple {
elems, location, .. elems, location, ..
} => { } => {
@ -1361,11 +1457,22 @@ impl<'a> CodeGenerator<'a> {
// Shouldn't be needed but still here just in case // Shouldn't be needed but still here just in case
// this function is called from anywhere else besides assignment // this function is called from anywhere else besides assignment
let tipo = &convert_opaque_type(tipo, &self.data_types, true); let tipo = &convert_opaque_type(tipo, &self.data_types, true);
let uplc_type = tipo.get_uplc_type();
if tipo.is_primitive() { match uplc_type {
// Since we would return void anyway and ignore then we can just return value here and ignore UplcType::Integer
value | UplcType::String
} else if tipo.is_map() { | UplcType::Bool
| UplcType::ByteString
| UplcType::Unit
| UplcType::Bls12_381G1Element
| UplcType::Bls12_381G2Element
| UplcType::Bls12_381MlResult => value,
// Untyped Data
UplcType::Data if tipo.is_data() => value,
// Map type
UplcType::List(_) if tipo.is_map() => {
assert!(!tipo.get_inner_types().is_empty()); assert!(!tipo.get_inner_types().is_empty());
let inner_list_type = &tipo.get_inner_types()[0]; let inner_list_type = &tipo.get_inner_types()[0];
@ -1446,115 +1553,9 @@ impl<'a> CodeGenerator<'a> {
); );
AirTree::let_assignment(&map_name, value, func_call) AirTree::let_assignment(&map_name, value, func_call)
} else if tipo.is_list() {
assert!(!tipo.get_inner_types().is_empty());
let inner_list_type = &tipo.get_inner_types()[0];
if inner_list_type.is_data() {
value
} else {
let list_name = format!("__list_span_{}_{}", location.start, location.end);
let item_name = format!("__item_span_{}_{}", location.start, location.end);
let expect_item = self.expect_type_assign(
inner_list_type,
AirTree::cast_from_data(
AirTree::local_var(&item_name, data()),
inner_list_type.clone(),
msg_func.clone(),
),
defined_data_types,
location,
msg_func,
);
let anon_func_body = expect_item;
let unwrap_function = AirTree::anon_func(vec![item_name], anon_func_body);
let function = self.code_gen_functions.get(EXPECT_ON_LIST);
if function.is_none() {
let expect_list_func = AirTree::expect_on_list();
self.code_gen_functions.insert(
EXPECT_ON_LIST.to_string(),
CodeGenFunction::Function {
body: expect_list_func,
params: vec!["__list_to_check".to_string(), "__check_with".to_string()],
},
);
} }
// Tuple type
if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) { UplcType::List(_) if tipo.is_tuple() => {
*counter += 1
} else {
defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1);
}
let func_call = AirTree::call(
AirTree::var(
ValueConstructor::public(
void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
module: "".to_string(),
arity: 1,
location,
builtin: None,
},
),
EXPECT_ON_LIST,
"",
),
void(),
vec![
AirTree::local_var(&list_name, tipo.clone()),
unwrap_function,
],
);
AirTree::let_assignment(&list_name, value, func_call)
}
} else if tipo.is_pair() {
let tuple_inner_types = tipo.get_inner_types();
assert!(tuple_inner_types.len() == 2);
let pair_name = format!("__pair_span_{}_{}", location.start, location.end);
let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end);
let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end);
let expect_fst = self.expect_type_assign(
&tuple_inner_types[0],
AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()),
defined_data_types,
location,
msg_func.clone(),
);
let expect_snd = self.expect_type_assign(
&tuple_inner_types[1],
AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()),
defined_data_types,
location,
msg_func.clone(),
);
let pair_access = AirTree::pair_access(
Some(fst_name.clone()),
Some(snd_name.clone()),
tipo.clone(),
AirTree::local_var(&pair_name, tipo.clone()),
msg_func.clone(),
true,
AirTree::let_assignment("_", expect_fst, expect_snd),
);
AirTree::let_assignment(&pair_name, value, pair_access)
} else if tipo.is_tuple() {
let tuple_inner_types = tipo.get_inner_types(); let tuple_inner_types = tipo.get_inner_types();
assert!(!tuple_inner_types.is_empty()); assert!(!tuple_inner_types.is_empty());
@ -1597,10 +1598,127 @@ impl<'a> CodeGenerator<'a> {
); );
AirTree::let_assignment(&tuple_name, value, tuple_access) AirTree::let_assignment(&tuple_name, value, tuple_access)
}
// Regular List type
UplcType::List(_) => {
assert!(!tipo.get_inner_types().is_empty());
// Constructor let inner_list_type = &tipo.get_inner_types()[0];
if inner_list_type.is_data() {
value
} else { } else {
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo).unwrap_or_else(|| { let list_name = format!("__list_span_{}_{}", location.start, location.end);
let item_name = format!("__item_span_{}_{}", location.start, location.end);
let expect_item = self.expect_type_assign(
inner_list_type,
AirTree::cast_from_data(
AirTree::local_var(&item_name, data()),
inner_list_type.clone(),
msg_func.clone(),
),
defined_data_types,
location,
msg_func,
);
let anon_func_body = expect_item;
let unwrap_function = AirTree::anon_func(vec![item_name], anon_func_body);
let function = self.code_gen_functions.get(EXPECT_ON_LIST);
if function.is_none() {
let expect_list_func = AirTree::expect_on_list();
self.code_gen_functions.insert(
EXPECT_ON_LIST.to_string(),
CodeGenFunction::Function {
body: expect_list_func,
params: vec![
"__list_to_check".to_string(),
"__check_with".to_string(),
],
},
);
}
if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) {
*counter += 1
} else {
defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1);
}
let func_call = AirTree::call(
AirTree::var(
ValueConstructor::public(
void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
module: "".to_string(),
arity: 1,
location,
builtin: None,
},
),
EXPECT_ON_LIST,
"",
),
void(),
vec![
AirTree::local_var(&list_name, tipo.clone()),
unwrap_function,
],
);
AirTree::let_assignment(&list_name, value, func_call)
}
}
// Pair type
UplcType::Pair(_, _) => {
let tuple_inner_types = tipo.get_inner_types();
assert!(tuple_inner_types.len() == 2);
let pair_name = format!("__pair_span_{}_{}", location.start, location.end);
let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end);
let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end);
let expect_fst = self.expect_type_assign(
&tuple_inner_types[0],
AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()),
defined_data_types,
location,
msg_func.clone(),
);
let expect_snd = self.expect_type_assign(
&tuple_inner_types[1],
AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()),
defined_data_types,
location,
msg_func.clone(),
);
let pair_access = AirTree::pair_access(
Some(fst_name.clone()),
Some(snd_name.clone()),
tipo.clone(),
AirTree::local_var(&pair_name, tipo.clone()),
msg_func.clone(),
true,
AirTree::let_assignment("_", expect_fst, expect_snd),
);
AirTree::let_assignment(&pair_name, value, pair_access)
}
// Constr type
UplcType::Data => {
let data_type =
lookup_data_type_by_tipo(&self.data_types, tipo).unwrap_or_else(|| {
unreachable!("We need a data type definition for type {:#?}", tipo) unreachable!("We need a data type definition for type {:#?}", tipo)
}); });
@ -1612,7 +1730,8 @@ impl<'a> CodeGenerator<'a> {
assert!(data_type.typed_parameters.len() == tipo.arg_types().unwrap().len()); assert!(data_type.typed_parameters.len() == tipo.arg_types().unwrap().len());
let mono_types: IndexMap<u64, Rc<Type>> = if !data_type.typed_parameters.is_empty() { let mono_types: IndexMap<u64, Rc<Type>> = if !data_type.typed_parameters.is_empty()
{
data_type data_type
.typed_parameters .typed_parameters
.iter() .iter()
@ -1659,7 +1778,8 @@ impl<'a> CodeGenerator<'a> {
let arg_name = let arg_name =
arg.label.clone().unwrap_or(format!("__field_{index}")); arg.label.clone().unwrap_or(format!("__field_{index}"));
let arg_tipo = find_and_replace_generics(&arg.tipo, &mono_types); let arg_tipo =
find_and_replace_generics(&arg.tipo, &mono_types);
constr_args.push((index, arg_name.clone(), arg_tipo.clone())); constr_args.push((index, arg_name.clone(), arg_tipo.clone()));
@ -1791,6 +1911,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::call(func_var, void(), args) AirTree::call(func_var, void(), args)
} }
} }
}
pub fn handle_each_clause( pub fn handle_each_clause(
&mut self, &mut self,
@ -2131,7 +2252,27 @@ impl<'a> CodeGenerator<'a> {
) )
} }
} }
SpecificClause::PairClause => todo!(), SpecificClause::PairClause => {
let (_, pattern_assigns) =
self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then);
let mut next_clause_props = ClauseProperties::init(
subject_tipo,
props.clause_var_name.clone(),
props.original_subject_name.clone(),
);
AirTree::wrap_clause(
pattern_assigns,
self.handle_each_clause(
rest_clauses,
final_clause,
subject_tipo,
&mut next_clause_props,
module_name,
),
)
}
} }
} else { } else {
// handle final_clause // handle final_clause
@ -2304,7 +2445,7 @@ impl<'a> CodeGenerator<'a> {
elems, elems,
subject_tipo.clone(), subject_tipo.clone(),
tail.is_some(), tail.is_some(),
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()), AirTree::local_var(&props.clause_var_name, subject_tipo.clone()),
// One special usage of list access here // One special usage of list access here
// So for the msg we pass in empty string if tracing is on // 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 // Since check_last_item is false this will never get added to the final uplc anyway
@ -2330,6 +2471,113 @@ impl<'a> CodeGenerator<'a> {
(AirTree::void(), list_assign) (AirTree::void(), list_assign)
} }
Pattern::Constructor {
name,
arguments,
constructor,
tipo: function_tipo,
..
} if subject_tipo.is_pair() => {
assert!(
matches!(function_tipo.as_ref().clone(), Type::Fn { .. })
|| matches!(function_tipo.as_ref().clone(), Type::App { .. })
);
let field_map = match constructor {
PatternConstructor::Record { field_map, .. } => field_map.clone(),
};
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
for (index, arg) in function_tipo.arg_types().unwrap().iter().enumerate() {
let field_type = arg.clone();
type_map.insert(index, field_type);
}
let mut fields = vec![];
let next_then =
arguments
.iter()
.enumerate()
.rfold(then, |inner_then, (index, arg)| {
let label = arg.label.clone().unwrap_or_default();
let field_index = if let Some(field_map) = &field_map {
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
} else {
index
};
let field_name = match &arg.value {
Pattern::Var { name, .. } => Some(name.to_string()),
Pattern::Assign { name, .. } => Some(name.to_string()),
Pattern::Discard { .. } => None,
_ => Some(format!(
"field_{}_span_{}_{}",
field_index,
arg.value.location().start,
arg.value.location().end
)),
};
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
unreachable!(
"Missing type for field {} of constr {}",
field_index, name
)
});
let mut field_props = ClauseProperties::init_inner(
arg_type,
field_name.clone().unwrap_or_else(|| "_".to_string()),
field_name.clone().unwrap_or_else(|| "_".to_string()),
props.final_clause,
);
let statement = if field_name.is_some() {
self.nested_clause_condition(
&arg.value,
arg_type,
&mut field_props,
inner_then,
)
} else {
inner_then
};
props.complex_clause =
props.complex_clause || field_props.complex_clause;
fields.push((field_name, arg_type.clone()));
statement
});
fields.reverse();
let field_assign = if fields.iter().all(|s| s.0.is_none()) {
next_then
} else {
AirTree::pair_access(
fields[0].0.clone(),
fields[1].0.clone(),
subject_tipo.clone(),
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
None,
false,
next_then,
)
};
(AirTree::void(), field_assign)
}
Pattern::Constructor { name, .. } if subject_tipo.is_bool() => {
(AirTree::bool(name == "True"), then)
}
Pattern::Constructor { Pattern::Constructor {
name, name,
arguments, arguments,
@ -2337,9 +2585,6 @@ impl<'a> CodeGenerator<'a> {
tipo: function_tipo, tipo: function_tipo,
.. ..
} => { } => {
if subject_tipo.is_bool() {
(AirTree::bool(name == "True"), then)
} else {
assert!( assert!(
matches!(function_tipo.as_ref().clone(), Type::Fn { .. }) matches!(function_tipo.as_ref().clone(), Type::Fn { .. })
|| matches!(function_tipo.as_ref().clone(), Type::App { .. }) || matches!(function_tipo.as_ref().clone(), Type::App { .. })
@ -2434,14 +2679,11 @@ impl<'a> CodeGenerator<'a> {
fields.reverse(); fields.reverse();
let field_assign = let field_assign = if check_replaceable_opaque_type(subject_tipo, &self.data_types)
if check_replaceable_opaque_type(subject_tipo, &self.data_types) { {
AirTree::let_assignment( AirTree::let_assignment(
&fields[0].1, &fields[0].1,
AirTree::local_var( AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
props.clause_var_name.clone(),
subject_tipo.clone(),
),
next_then, next_then,
) )
} else if fields.iter().all(|s| s.1 == "_") { } else if fields.iter().all(|s| s.1 == "_") {
@ -2449,10 +2691,7 @@ impl<'a> CodeGenerator<'a> {
} else { } else {
AirTree::fields_expose( AirTree::fields_expose(
fields, fields,
AirTree::local_var( AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
props.clause_var_name.clone(),
subject_tipo.clone(),
),
None, None,
false, false,
next_then, next_then,
@ -2461,7 +2700,6 @@ impl<'a> CodeGenerator<'a> {
(AirTree::int(constr_index), field_assign) (AirTree::int(constr_index), field_assign)
} }
}
Pattern::Tuple { elems, .. } => { Pattern::Tuple { elems, .. } => {
let items_type = subject_tipo.get_inner_types(); let items_type = subject_tipo.get_inner_types();
@ -2582,7 +2820,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::tuple_access( AirTree::tuple_access(
names, names,
subject_tipo.clone(), subject_tipo.clone(),
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()), AirTree::local_var(&props.clause_var_name, subject_tipo.clone()),
None, None,
false, false,
tuple_name_assigns, tuple_name_assigns,
@ -2724,6 +2962,13 @@ impl<'a> CodeGenerator<'a> {
}) })
} }
} }
Pattern::Constructor { .. } if subject_tipo.is_pair() => {
let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then);
assign
}
Pattern::Constructor { Pattern::Constructor {
name: constr_name, .. name: constr_name, ..
} => { } => {