Fix: issue crash in code gen with incorrect column length in decision trees (#1069)
* Fix: Deeply nested assignments would offset the new columns count calculation. Now we track relevant columns and their path to ensure each row has wildcards if they don't contain the relevant column * Add test plus clippy fix * Clippy fix * New version clippy fix
This commit is contained in:
parent
a9675fedc6
commit
86ec3b2924
|
@ -881,7 +881,7 @@ pub enum Located<'a> {
|
|||
Annotation(&'a Annotation),
|
||||
}
|
||||
|
||||
impl<'a> Located<'a> {
|
||||
impl Located<'_> {
|
||||
pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
|
||||
match self {
|
||||
Self::Expression(expression) => expression.definition_location(),
|
||||
|
@ -1996,7 +1996,7 @@ impl<'de> serde::Deserialize<'de> for Bls12_381Point {
|
|||
{
|
||||
struct FieldVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
|
||||
impl serde::de::Visitor<'_> for FieldVisitor {
|
||||
type Value = Field;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -3,7 +3,7 @@ use pretty::RcDoc;
|
|||
use std::{cmp::Ordering, fmt::Display, rc::Rc};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use itertools::{Itertools, Position};
|
||||
use itertools::{Either, Itertools, Position};
|
||||
|
||||
use crate::{
|
||||
ast::{DataTypeKey, Pattern, TypedClause, TypedDataType, TypedPattern},
|
||||
|
@ -12,10 +12,6 @@ use crate::{
|
|||
|
||||
use super::{interner::AirInterner, tree::AirTree};
|
||||
|
||||
const PAIR_NEW_COLUMNS: usize = 2;
|
||||
|
||||
const MIN_NEW_COLUMNS: usize = 1;
|
||||
|
||||
#[derive(Clone, Default, Copy)]
|
||||
struct Occurrence {
|
||||
passed_wild_card: bool,
|
||||
|
@ -71,6 +67,26 @@ impl PartialEq for Path {
|
|||
|
||||
impl Eq for Path {}
|
||||
|
||||
impl PartialOrd for Path {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Path {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(Path::Pair(a), Path::Pair(b))
|
||||
| (Path::Tuple(a), Path::Tuple(b))
|
||||
| (Path::List(a), Path::List(b))
|
||||
| (Path::ListTail(a), Path::ListTail(b))
|
||||
| (Path::Constr(_, a), Path::Constr(_, b)) => a.cmp(b),
|
||||
(Path::OpaqueConstr(_), Path::OpaqueConstr(_)) => Ordering::Equal,
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Assigned {
|
||||
pub path: Vec<Path>,
|
||||
|
@ -568,7 +584,7 @@ impl<'a> DecisionTree<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for DecisionTree<'a> {
|
||||
impl Display for DecisionTree<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.to_pretty())
|
||||
}
|
||||
|
@ -603,33 +619,86 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
) -> DecisionTree<'a> {
|
||||
let mut hoistables = IndexMap::new();
|
||||
|
||||
let rows = clauses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, clause)| {
|
||||
// Assigns are split out from patterns so they can be handled
|
||||
// outside of the tree algorithm
|
||||
let (assign, row_items) =
|
||||
self.map_pattern_to_row(&clause.pattern, subject_tipo, vec![]);
|
||||
let mut columns_added = vec![];
|
||||
|
||||
self.interner.intern(format!("__clause_then_{}", index));
|
||||
let clause_then_name = self
|
||||
.interner
|
||||
.lookup_interned(&format!("__clause_then_{}", index));
|
||||
let rows = {
|
||||
let rows_initial = clauses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, clause)| {
|
||||
// Assigns are split out from patterns so they can be handled
|
||||
// outside of the tree algorithm
|
||||
let (assign, row_items) =
|
||||
self.map_pattern_to_row(&clause.pattern, subject_tipo, vec![]);
|
||||
|
||||
hoistables.insert(clause_then_name.clone(), (vec![], &clause.then));
|
||||
self.interner.intern(format!("__clause_then_{}", index));
|
||||
let clause_then_name = self
|
||||
.interner
|
||||
.lookup_interned(&format!("__clause_then_{}", index));
|
||||
|
||||
let row = Row {
|
||||
assigns: assign.into_iter().collect_vec(),
|
||||
columns: row_items,
|
||||
then: clause_then_name,
|
||||
};
|
||||
hoistables.insert(clause_then_name.clone(), (vec![], &clause.then));
|
||||
|
||||
self.interner.pop_text(format!("__clause_then_{}", index));
|
||||
// Some good ol' mutation to track added columns per relevant path
|
||||
// relevant path indicating a column that has a pattern to test at some point in
|
||||
// one of the rows
|
||||
row_items.iter().for_each(|col| {
|
||||
if !columns_added.contains(&col.path) {
|
||||
columns_added.push(col.path.clone());
|
||||
}
|
||||
});
|
||||
|
||||
row
|
||||
})
|
||||
.collect_vec();
|
||||
let row = Row {
|
||||
assigns: assign.into_iter().collect_vec(),
|
||||
columns: row_items,
|
||||
then: clause_then_name,
|
||||
};
|
||||
|
||||
self.interner.pop_text(format!("__clause_then_{}", index));
|
||||
|
||||
row
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
columns_added = columns_added
|
||||
.into_iter()
|
||||
.sorted_by(|a, b| {
|
||||
let mut a = a.clone();
|
||||
let mut b = b.clone();
|
||||
|
||||
// It's impossible for duplicates since we check before insertion
|
||||
while let Ordering::Equal = a.first().cmp(&b.first()) {
|
||||
a.remove(0);
|
||||
b.remove(0);
|
||||
}
|
||||
|
||||
a.first().cmp(&b.first())
|
||||
})
|
||||
.map(|col| {
|
||||
// remove opaqueconstr paths since they are just type information
|
||||
col.into_iter()
|
||||
.filter(|a| !matches!(a, Path::OpaqueConstr(_)))
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
rows_initial
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
for (index, path) in columns_added.iter().enumerate() {
|
||||
if !row
|
||||
.columns
|
||||
.get(index)
|
||||
.map(|col| col.path == *path)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
row.columns.insert(index, self.wild_card_pattern.clone());
|
||||
}
|
||||
}
|
||||
|
||||
row
|
||||
})
|
||||
.collect_vec()
|
||||
};
|
||||
|
||||
let mut tree = self.do_build_tree(subject_tipo, PatternMatrix { rows }, &mut hoistables);
|
||||
|
||||
|
@ -654,6 +723,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
then_map: &mut IndexMap<String, (Vec<Assigned>, &'a TypedExpr)>,
|
||||
) -> DecisionTree<'a> {
|
||||
let column_length = matrix.rows[0].columns.len();
|
||||
|
||||
// First step make sure all rows have same number of columns
|
||||
// or something went wrong
|
||||
assert!(matrix
|
||||
|
@ -734,6 +804,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
.unwrap();
|
||||
|
||||
let specialized_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
|
||||
let mut relevant_columns: Vec<(CaseTest, Vec<Vec<Path>>)> = vec![];
|
||||
|
||||
// Time to split on the matrices based on case to test for or lack of
|
||||
let (default_matrix, specialized_matrices) = matrix.rows.into_iter().fold(
|
||||
|
@ -841,37 +912,37 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
// Add inner patterns to existing row
|
||||
let mut new_cols = remaining_patts.into_iter().flat_map(|x| x.1).collect_vec();
|
||||
|
||||
// To align number of columns we pop off the tail since it can
|
||||
// never include a pattern besides wild card
|
||||
if matches!(case, CaseTest::ListWithTail(_)) {
|
||||
new_cols.pop();
|
||||
}
|
||||
let new_paths = new_cols.iter().map(|col| col.path.clone()).collect_vec();
|
||||
|
||||
let added_columns = new_cols.len();
|
||||
|
||||
// Pop off tail so that it aligns more easily with other list patterns
|
||||
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
|
||||
new_paths.iter().for_each(|col| {
|
||||
if !a.1.contains(col) {
|
||||
a.1.push(col.clone());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
relevant_columns.push((case.clone(), new_paths.clone()));
|
||||
};
|
||||
|
||||
new_cols.extend(row.columns);
|
||||
|
||||
row.columns = new_cols;
|
||||
|
||||
if let CaseTest::Wild = case {
|
||||
let current_wild_cols = row.columns.len();
|
||||
default_matrix.push(row.clone());
|
||||
|
||||
case_matrices.iter_mut().for_each(|(_, matrix)| {
|
||||
let mut row = row.clone();
|
||||
let total_cols = matrix[0].columns.len();
|
||||
case_matrices.iter_mut().for_each(|(case, matrix)| {
|
||||
let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == *case) else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if total_cols != 0 {
|
||||
let added_columns = total_cols - current_wild_cols;
|
||||
|
||||
for _ in 0..added_columns {
|
||||
row.columns.insert(0, self.wild_card_pattern.clone());
|
||||
new_paths.iter().for_each(|col| {
|
||||
if !a.1.contains(col) {
|
||||
a.1.push(col.clone());
|
||||
}
|
||||
});
|
||||
|
||||
matrix.push(row);
|
||||
}
|
||||
matrix.push(row.clone());
|
||||
});
|
||||
} else if let CaseTest::ListWithTail(tail_case_length) = case {
|
||||
// For lists with tail it's a special case where we also add it to existing patterns
|
||||
|
@ -886,20 +957,30 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
for elem_count in tail_case_length..=longest_elems_no_tail {
|
||||
let case = CaseTest::List(elem_count);
|
||||
|
||||
let mut row = row.clone();
|
||||
// paths first
|
||||
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
|
||||
new_paths.iter().for_each(|col| {
|
||||
if !a.1.contains(col) {
|
||||
a.1.push(col.clone());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
relevant_columns.push((case.clone(), new_paths.clone()));
|
||||
};
|
||||
|
||||
for _ in 0..(elem_count - tail_case_length) {
|
||||
row.columns
|
||||
.insert(tail_case_length, self.wild_card_pattern.clone());
|
||||
let row = row.clone();
|
||||
|
||||
// now insertion into the appropriate matrix
|
||||
if let Some(entry) =
|
||||
case_matrices.iter_mut().find(|item| item.0 == case)
|
||||
{
|
||||
entry.1.push(row);
|
||||
} else {
|
||||
let mut rows = default_matrix.to_vec();
|
||||
|
||||
rows.push(row);
|
||||
case_matrices.push((case, rows));
|
||||
}
|
||||
|
||||
self.insert_case(
|
||||
&mut case_matrices,
|
||||
case,
|
||||
&default_matrix,
|
||||
row,
|
||||
added_columns,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -907,35 +988,123 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
for elem_count in tail_case_length..=longest_elems_with_tail {
|
||||
let case = CaseTest::ListWithTail(elem_count);
|
||||
|
||||
let mut row = row.clone();
|
||||
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
|
||||
new_paths.iter().for_each(|col| {
|
||||
if !a.1.contains(col) {
|
||||
a.1.push(col.clone());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
relevant_columns.push((case.clone(), new_paths.clone()));
|
||||
};
|
||||
|
||||
for _ in 0..(elem_count - tail_case_length) {
|
||||
row.columns
|
||||
.insert(tail_case_length, self.wild_card_pattern.clone());
|
||||
let row = row.clone();
|
||||
|
||||
if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
|
||||
entry.1.push(row);
|
||||
} else {
|
||||
let mut rows = default_matrix.clone();
|
||||
|
||||
rows.push(row);
|
||||
case_matrices.push((case, rows));
|
||||
}
|
||||
|
||||
self.insert_case(
|
||||
&mut case_matrices,
|
||||
case,
|
||||
&default_matrix,
|
||||
row,
|
||||
added_columns,
|
||||
);
|
||||
}
|
||||
} else if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
|
||||
entry.1.push(row);
|
||||
} else {
|
||||
self.insert_case(
|
||||
&mut case_matrices,
|
||||
case,
|
||||
&default_matrix,
|
||||
row,
|
||||
added_columns,
|
||||
);
|
||||
let mut rows = default_matrix.clone();
|
||||
|
||||
rows.push(row);
|
||||
case_matrices.push((case, rows));
|
||||
}
|
||||
|
||||
(default_matrix, case_matrices)
|
||||
},
|
||||
);
|
||||
|
||||
let (default_relevant_cols, relevant_columns): (Vec<_>, Vec<_>) = relevant_columns
|
||||
.into_iter()
|
||||
.map(|(case, paths)| {
|
||||
(
|
||||
case,
|
||||
paths
|
||||
.into_iter()
|
||||
.sorted_by(|a, b| {
|
||||
let mut a = a.clone();
|
||||
let mut b = b.clone();
|
||||
|
||||
// It's impossible for duplicates since we check before insertion
|
||||
while let Ordering::Equal = a.first().cmp(&b.first()) {
|
||||
a.remove(0);
|
||||
b.remove(0);
|
||||
}
|
||||
|
||||
a.first().cmp(&b.first())
|
||||
})
|
||||
.map(|col| {
|
||||
// remove opaqueconstr paths since they are just type information
|
||||
col.into_iter()
|
||||
.filter(|a| !matches!(a, Path::OpaqueConstr(_)))
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec(),
|
||||
)
|
||||
})
|
||||
.partition_map(|(case, paths)| match case {
|
||||
CaseTest::Wild => Either::Left(paths),
|
||||
_ => Either::Right((case, paths)),
|
||||
});
|
||||
|
||||
let default_matrix = default_matrix
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
for (index, path) in default_relevant_cols[0].iter().enumerate() {
|
||||
if !row
|
||||
.columns
|
||||
.get(index)
|
||||
.map(|col| col.path == *path)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
row.columns.insert(index, self.wild_card_pattern.clone());
|
||||
}
|
||||
}
|
||||
|
||||
row
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let specialized_matrices = specialized_matrices
|
||||
.into_iter()
|
||||
.map(|(case, matrix)| {
|
||||
let Some((_, relevant_cols)) = relevant_columns
|
||||
.iter()
|
||||
.find(|(relevant_case, _)| relevant_case == &case)
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
(
|
||||
case,
|
||||
matrix
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
for (index, path) in relevant_cols.iter().enumerate() {
|
||||
if !row
|
||||
.columns
|
||||
.get(index)
|
||||
.map(|col| col.path == *path)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
row.columns.insert(index, self.wild_card_pattern.clone());
|
||||
}
|
||||
}
|
||||
|
||||
row
|
||||
})
|
||||
.collect_vec(),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let default_matrix = PatternMatrix {
|
||||
rows: default_matrix,
|
||||
};
|
||||
|
@ -1001,39 +1170,13 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
) -> (Vec<Assigned>, Vec<RowItem<'a>>) {
|
||||
let current_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
|
||||
|
||||
let new_columns_added = if current_tipo.is_pair() {
|
||||
PAIR_NEW_COLUMNS
|
||||
} else if current_tipo.is_tuple() {
|
||||
let Type::Tuple { elems, .. } = current_tipo.as_ref() else {
|
||||
unreachable!("{:#?}", current_tipo)
|
||||
};
|
||||
elems.len()
|
||||
} else if let Some(data) = lookup_data_type_by_tipo(self.data_types, ¤t_tipo) {
|
||||
if data.constructors.len() == 1 {
|
||||
data.constructors[0].arguments.len()
|
||||
} else if data.is_never() {
|
||||
0
|
||||
} else {
|
||||
MIN_NEW_COLUMNS
|
||||
}
|
||||
} else {
|
||||
MIN_NEW_COLUMNS
|
||||
};
|
||||
|
||||
match pattern {
|
||||
Pattern::Var { name, .. } => (
|
||||
vec![Assigned {
|
||||
path: path.clone(),
|
||||
assigned: name.clone(),
|
||||
}],
|
||||
vec![RowItem {
|
||||
pattern,
|
||||
path: path.clone(),
|
||||
}]
|
||||
.into_iter()
|
||||
.cycle()
|
||||
.take(new_columns_added)
|
||||
.collect_vec(),
|
||||
vec![],
|
||||
),
|
||||
|
||||
Pattern::Assign { name, pattern, .. } => {
|
||||
|
@ -1049,19 +1192,14 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
);
|
||||
(assigns, patts)
|
||||
}
|
||||
Pattern::Int { .. }
|
||||
| Pattern::ByteArray { .. }
|
||||
| Pattern::Discard { .. }
|
||||
| Pattern::List { .. } => (
|
||||
Pattern::Discard { .. } => (vec![], vec![]),
|
||||
|
||||
Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::List { .. } => (
|
||||
vec![],
|
||||
vec![RowItem {
|
||||
pattern,
|
||||
path: path.clone(),
|
||||
}]
|
||||
.into_iter()
|
||||
.cycle()
|
||||
.take(new_columns_added)
|
||||
.collect_vec(),
|
||||
}],
|
||||
),
|
||||
|
||||
Pattern::Constructor {
|
||||
|
@ -1101,11 +1239,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
vec![RowItem {
|
||||
pattern,
|
||||
path: path.clone(),
|
||||
}]
|
||||
.into_iter()
|
||||
.cycle()
|
||||
.take(new_columns_added)
|
||||
.collect_vec(),
|
||||
}],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1126,6 +1260,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
|
||||
(assigns, patts)
|
||||
}
|
||||
|
||||
Pattern::Tuple { elems, .. } => {
|
||||
elems
|
||||
.iter()
|
||||
|
@ -1146,30 +1281,6 @@ impl<'a, 'b> TreeGen<'a, 'b> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_case(
|
||||
&self,
|
||||
case_matrices: &mut Vec<(CaseTest, Vec<Row<'a>>)>,
|
||||
case: CaseTest,
|
||||
default_matrix: &[Row<'a>],
|
||||
new_row: Row<'a>,
|
||||
added_columns: usize,
|
||||
) {
|
||||
if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
|
||||
entry.1.push(new_row);
|
||||
} else {
|
||||
let mut rows = default_matrix.to_vec();
|
||||
|
||||
for _ in 0..added_columns {
|
||||
for row in &mut rows {
|
||||
row.columns.insert(0, self.wild_card_pattern.clone());
|
||||
}
|
||||
}
|
||||
|
||||
rows.push(new_row);
|
||||
case_matrices.push((case, rows));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tipo_by_path(mut subject_tipo: Rc<Type>, mut path: &[Path]) -> Rc<Type> {
|
||||
|
|
|
@ -622,7 +622,7 @@ pub struct Counterexample<'a> {
|
|||
pub cache: Cache<'a, PlutusData>,
|
||||
}
|
||||
|
||||
impl<'a> Counterexample<'a> {
|
||||
impl Counterexample<'_> {
|
||||
fn consider(&mut self, choices: &[u8]) -> bool {
|
||||
if choices == self.choices {
|
||||
return true;
|
||||
|
|
|
@ -1284,7 +1284,7 @@ fn suggest_pattern(
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_generic(name: &String, expected: usize) -> String {
|
||||
fn suggest_generic(name: &str, expected: usize) -> String {
|
||||
if expected == 0 {
|
||||
return name.to_doc().to_pretty_string(70);
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ impl Printer {
|
|||
}
|
||||
}
|
||||
|
||||
fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> {
|
||||
fn qualify_type_name(module: &str, typ_name: &str) -> Document<'static> {
|
||||
if module.is_empty() {
|
||||
docvec!["aiken.", Document::String(typ_name.to_string())]
|
||||
} else {
|
||||
|
|
|
@ -52,7 +52,7 @@ struct ModuleTemplate<'a> {
|
|||
timestamp: String,
|
||||
}
|
||||
|
||||
impl<'a> ModuleTemplate<'a> {
|
||||
impl ModuleTemplate<'_> {
|
||||
pub fn is_current_module(&self, module: &DocLink) -> bool {
|
||||
match module.path.split(".html").next() {
|
||||
None => false,
|
||||
|
@ -75,7 +75,7 @@ struct PageTemplate<'a> {
|
|||
timestamp: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> PageTemplate<'a> {
|
||||
impl PageTemplate<'_> {
|
||||
pub fn is_current_module(&self, _module: &DocLink) -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -720,7 +720,7 @@ struct DisplayWarning<'a> {
|
|||
help: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> Diagnostic for DisplayWarning<'a> {
|
||||
impl Diagnostic for DisplayWarning<'_> {
|
||||
fn severity(&self) -> Option<miette::Severity> {
|
||||
Some(miette::Severity::Warning)
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ impl<'a> Diagnostic for DisplayWarning<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug for DisplayWarning<'a> {
|
||||
impl Debug for DisplayWarning<'_> {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
unreachable!("Display warning are never shown directly.");
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ impl<'de> Deserialize<'de> for PackageName {
|
|||
{
|
||||
struct PackageNameVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PackageNameVisitor {
|
||||
impl Visitor<'_> for PackageNameVisitor {
|
||||
type Value = PackageName;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
|
|
|
@ -191,7 +191,7 @@ enum Log<'a> {
|
|||
Done(&'a Shell),
|
||||
}
|
||||
|
||||
impl<'a> Display for Log<'a> {
|
||||
impl Display for Log<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
|
||||
match self {
|
||||
Log::Detecting(what) => pretty::fmt_step(f, "Detecting", what),
|
||||
|
|
|
@ -632,7 +632,7 @@ fn encode_type(typ: &Type, bytes: &mut Vec<u8>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for Constant {
|
||||
impl Decode<'_> for Constant {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
match &decode_constant(d)?[..] {
|
||||
[0] => Ok(Constant::Integer(BigInt::decode(d)?)),
|
||||
|
@ -806,7 +806,7 @@ impl Encode for Unique {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for Unique {
|
||||
impl Decode<'_> for Unique {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
Ok(isize::decode(d)?.into())
|
||||
}
|
||||
|
@ -821,7 +821,7 @@ impl Encode for Name {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for Name {
|
||||
impl Decode<'_> for Name {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
Ok(Name {
|
||||
text: String::decode(d)?,
|
||||
|
@ -830,7 +830,7 @@ impl<'b> Decode<'b> for Name {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Binder<'b> for Name {
|
||||
impl Binder<'_> for Name {
|
||||
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error> {
|
||||
self.encode(e)?;
|
||||
|
||||
|
@ -855,7 +855,7 @@ impl Encode for NamedDeBruijn {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for NamedDeBruijn {
|
||||
impl Decode<'_> for NamedDeBruijn {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
Ok(NamedDeBruijn {
|
||||
text: String::decode(d)?,
|
||||
|
@ -864,7 +864,7 @@ impl<'b> Decode<'b> for NamedDeBruijn {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Binder<'b> for NamedDeBruijn {
|
||||
impl Binder<'_> for NamedDeBruijn {
|
||||
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error> {
|
||||
self.text.encode(e)?;
|
||||
self.index.encode(e)?;
|
||||
|
@ -892,13 +892,13 @@ impl Encode for DeBruijn {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for DeBruijn {
|
||||
impl Decode<'_> for DeBruijn {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
Ok(usize::decode(d)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Binder<'b> for DeBruijn {
|
||||
impl Binder<'_> for DeBruijn {
|
||||
fn binder_encode(&self, _: &mut Encoder) -> Result<(), en::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -922,7 +922,7 @@ impl Encode for FakeNamedDeBruijn {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for FakeNamedDeBruijn {
|
||||
impl Decode<'_> for FakeNamedDeBruijn {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
let index = DeBruijn::decode(d)?;
|
||||
|
||||
|
@ -930,7 +930,7 @@ impl<'b> Decode<'b> for FakeNamedDeBruijn {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Binder<'b> for FakeNamedDeBruijn {
|
||||
impl Binder<'_> for FakeNamedDeBruijn {
|
||||
fn binder_encode(&self, _: &mut Encoder) -> Result<(), en::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -954,7 +954,7 @@ impl Encode for DefaultFunction {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b> Decode<'b> for DefaultFunction {
|
||||
impl Decode<'_> for DefaultFunction {
|
||||
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||
let builtin_tag = d.bits8(BUILTIN_TAG_WIDTH as usize)?;
|
||||
builtin_tag.try_into()
|
||||
|
|
|
@ -132,7 +132,7 @@ impl ToPlutusData for Address {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, TransactionInput> {
|
||||
impl ToPlutusData for WithWrappedTransactionId<'_, TransactionInput> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
wrap_multiple_with_constr(
|
||||
0,
|
||||
|
@ -197,7 +197,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
|
||||
impl ToPlutusData for WithWrappedTransactionId<'_, KeyValuePairs<ScriptPurpose, Redeemer>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
|
||||
for (key, value) in self.0.iter() {
|
||||
|
@ -210,7 +210,7 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpo
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Vec<Certificate>> {
|
||||
impl ToPlutusData for WithNeverRegistrationDeposit<'_, Vec<Certificate>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
self.0
|
||||
.iter()
|
||||
|
@ -220,7 +220,7 @@ impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Vec<Certificate>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
|
||||
impl ToPlutusData for WithNeverRegistrationDeposit<'_, KeyValuePairs<ScriptPurpose, Redeemer>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
|
||||
for (key, value) in self.0.iter() {
|
||||
|
@ -309,7 +309,7 @@ impl ToPlutusData for PositiveCoin {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Value> {
|
||||
impl ToPlutusData for WithZeroAdaAsset<'_, Value> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
Value::Coin(coin) => {
|
||||
|
@ -345,7 +345,7 @@ impl ToPlutusData for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, MintValue> {
|
||||
impl ToPlutusData for WithZeroAdaAsset<'_, MintValue> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
value_to_plutus_data(
|
||||
self.0.mint_value.iter(),
|
||||
|
@ -429,7 +429,7 @@ impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, Vec<Transacti
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Vec<TransactionOutput>> {
|
||||
impl ToPlutusData for WithZeroAdaAsset<'_, Vec<TransactionOutput>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
Data::list(
|
||||
self.0
|
||||
|
@ -465,7 +465,7 @@ impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, TransactionOu
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, TransactionOutput> {
|
||||
impl ToPlutusData for WithZeroAdaAsset<'_, TransactionOutput> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
TransactionOutput::Legacy(legacy_output) => {
|
||||
|
@ -528,7 +528,7 @@ impl ToPlutusData for StakeCredential {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithPartialCertificates<'a, Vec<Certificate>> {
|
||||
impl ToPlutusData for WithPartialCertificates<'_, Vec<Certificate>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
self.0
|
||||
.iter()
|
||||
|
@ -538,7 +538,7 @@ impl<'a> ToPlutusData for WithPartialCertificates<'a, Vec<Certificate>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithPartialCertificates<'a, Certificate> {
|
||||
impl ToPlutusData for WithPartialCertificates<'_, Certificate> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
Certificate::StakeRegistration(stake_credential) => {
|
||||
|
@ -586,7 +586,7 @@ impl<'a> ToPlutusData for WithPartialCertificates<'a, Certificate> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Certificate> {
|
||||
impl ToPlutusData for WithNeverRegistrationDeposit<'_, Certificate> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
Certificate::StakeRegistration(stake_credential) => wrap_multiple_with_constr(
|
||||
|
@ -870,7 +870,7 @@ impl ToPlutusData for TxInInfo {
|
|||
// NOTE: This is a _small_ abuse of the 'WithWrappedTransactionId'. We know the wrapped
|
||||
// is needed for V1 and V2, and it also appears that for V1 and V2, the certifying
|
||||
// purpose mustn't include the certificate index. So, we also short-circuit it here.
|
||||
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
|
||||
impl ToPlutusData for WithWrappedTransactionId<'_, ScriptPurpose> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
|
||||
|
@ -891,7 +891,7 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptPurpose> {
|
||||
impl ToPlutusData for WithNeverRegistrationDeposit<'_, ScriptPurpose> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self.0 {
|
||||
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
|
||||
|
@ -1210,14 +1210,14 @@ impl ToPlutusData for RationalNumber {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithArrayRational<'a, RationalNumber> {
|
||||
impl ToPlutusData for WithArrayRational<'_, RationalNumber> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
let gcd = self.0.numerator.gcd(&self.0.denominator);
|
||||
vec![self.0.numerator / gcd, self.0.denominator / gcd].to_plutus_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, Vec<(Address, Coin)>> {
|
||||
impl ToPlutusData for WithWrappedStakeCredential<'_, Vec<(Address, Coin)>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
self.0
|
||||
.iter()
|
||||
|
@ -1232,7 +1232,7 @@ impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, Vec<(Address, Coin)>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, KeyValuePairs<Address, Coin>> {
|
||||
impl ToPlutusData for WithWrappedStakeCredential<'_, KeyValuePairs<Address, Coin>> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
KeyValuePairs::from(
|
||||
self.0
|
||||
|
@ -1249,7 +1249,7 @@ impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, KeyValuePairs<Address,
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, StakeCredential> {
|
||||
impl ToPlutusData for WithWrappedStakeCredential<'_, StakeCredential> {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
wrap_with_constr(0, self.0.to_plutus_data())
|
||||
}
|
||||
|
@ -1291,7 +1291,7 @@ impl ToPlutusData for Vote {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptInfo<T>>
|
||||
impl<T> ToPlutusData for WithNeverRegistrationDeposit<'_, ScriptInfo<T>>
|
||||
where
|
||||
T: ToPlutusData,
|
||||
{
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
name = "aiken-lang/acceptance_test_006"
|
||||
version = "0.0.0"
|
||||
compiler = "v1.1.7"
|
||||
plutus = "v3"
|
||||
description = ""
|
||||
|
||||
[[dependencies]]
|
||||
name = "aiken-lang/stdlib"
|
||||
version = "v2.1.0"
|
||||
source = "github"
|
||||
|
||||
[config]
|
|
@ -0,0 +1,86 @@
|
|||
use aiken/collection/list
|
||||
use cardano/assets.{PolicyId}
|
||||
use cardano/transaction.{InlineDatum, Output, OutputReference, Transaction}
|
||||
|
||||
pub type StateMachineDatum {
|
||||
state: Int,
|
||||
buyer: ByteArray,
|
||||
seller: ByteArray,
|
||||
collateral: Int,
|
||||
price: Int,
|
||||
accept_range: Int,
|
||||
}
|
||||
|
||||
pub type StateMachineInput {
|
||||
Return
|
||||
Other
|
||||
}
|
||||
|
||||
validator statemachine(threadtoken: PolicyId) {
|
||||
spend(
|
||||
datum_opt: Option<StateMachineDatum>,
|
||||
redeemer: StateMachineInput,
|
||||
own_ref: OutputReference,
|
||||
transaction: Transaction,
|
||||
) {
|
||||
expect Some(datum) = datum_opt
|
||||
when (datum, redeemer) is {
|
||||
(
|
||||
StateMachineDatum {
|
||||
state,
|
||||
buyer,
|
||||
seller,
|
||||
price,
|
||||
collateral,
|
||||
accept_range,
|
||||
},
|
||||
Return,
|
||||
) -> {
|
||||
let must_be_state = state == 0
|
||||
|
||||
let must_be_signed = list.has(transaction.extra_signatories, buyer)
|
||||
//One of the transaction inputs belongs to the statemachine.
|
||||
expect Some(sm_input) =
|
||||
list.find(
|
||||
transaction.inputs,
|
||||
fn(input) { input.output_reference == own_ref },
|
||||
)
|
||||
//One of the transaction outputs contains the threadtoken addressed to the statemachine itself - 1.
|
||||
expect Some(sm_output) =
|
||||
list.find(
|
||||
transaction.outputs,
|
||||
fn(output) { output.address == sm_input.output.address },
|
||||
)
|
||||
//One of the transaction outputs contains the threadtoken addressed to the statemachine itself - 2.
|
||||
let must_be_policy =
|
||||
list.has(assets.policies(sm_output.value), threadtoken)
|
||||
|
||||
//verification of the new datum - 1.
|
||||
let new_data: Data =
|
||||
StateMachineDatum {
|
||||
state: -1,
|
||||
buyer,
|
||||
seller,
|
||||
collateral,
|
||||
price,
|
||||
accept_range,
|
||||
}
|
||||
//verification of the new datum - 2.
|
||||
let must_be_datum = InlineDatum(new_data) == sm_output.datum
|
||||
|
||||
and {
|
||||
must_be_state?,
|
||||
must_be_signed?,
|
||||
must_be_policy?,
|
||||
must_be_datum?,
|
||||
}
|
||||
}
|
||||
|
||||
_ -> False
|
||||
}
|
||||
}
|
||||
|
||||
else(_) {
|
||||
fail
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue