Merge pull request #315 from aiken-lang/assert-data-type
Assert data type to type cast
This commit is contained in:
commit
f7421032bd
|
@ -10,6 +10,7 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Air {
|
pub enum Air {
|
||||||
|
// Primitives
|
||||||
Int {
|
Int {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -30,17 +31,6 @@ pub enum Air {
|
||||||
value: bool,
|
value: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
Var {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
constructor: ValueConstructor,
|
|
||||||
name: String,
|
|
||||||
variant_name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
Fn {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
params: Vec<String>,
|
|
||||||
},
|
|
||||||
List {
|
List {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
@ -48,48 +38,30 @@ pub enum Air {
|
||||||
tail: bool,
|
tail: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
ListAccessor {
|
Tuple {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
names: Vec<String>,
|
count: usize,
|
||||||
tail: bool,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ListExpose {
|
Void {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
tipo: Arc<Type>,
|
|
||||||
tail_head_names: Vec<(String, String)>,
|
|
||||||
tail: Option<(String, String)>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
constructor: ValueConstructor,
|
||||||
|
name: String,
|
||||||
|
variant_name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Functions
|
||||||
Call {
|
Call {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
count: usize,
|
||||||
},
|
|
||||||
|
|
||||||
Builtin {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
func: DefaultFunction,
|
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
BinOp {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
name: BinOp,
|
|
||||||
count: usize,
|
|
||||||
tipo: Arc<Type>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Assignment {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
Assert {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
constr_index: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
DefineFunc {
|
DefineFunc {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
func_name: String,
|
func_name: String,
|
||||||
|
@ -99,11 +71,47 @@ pub enum Air {
|
||||||
variant_name: String,
|
variant_name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Lam {
|
Fn {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
params: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Builtin {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
func: DefaultFunction,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
BinOp {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
name: BinOp,
|
||||||
|
count: usize,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
UnOp {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
op: UnOp,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Assignment
|
||||||
|
Let {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UnWrapData {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
AssertConstr {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
constr_index: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
// When
|
||||||
When {
|
When {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
|
@ -153,18 +161,17 @@ pub enum Air {
|
||||||
inverse: bool,
|
inverse: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
Discard {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Finally {
|
Finally {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// If
|
||||||
If {
|
If {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Record Creation
|
||||||
Record {
|
Record {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
constr_index: usize,
|
constr_index: usize,
|
||||||
|
@ -172,6 +179,14 @@ pub enum Air {
|
||||||
count: usize,
|
count: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
RecordUpdate {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
highest_index: usize,
|
||||||
|
indices: Vec<(usize, Arc<Type>)>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Field Access
|
||||||
RecordAccess {
|
RecordAccess {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
record_index: u64,
|
record_index: u64,
|
||||||
|
@ -180,14 +195,32 @@ pub enum Air {
|
||||||
|
|
||||||
FieldsExpose {
|
FieldsExpose {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
|
||||||
indices: Vec<(usize, String, Arc<Type>)>,
|
indices: Vec<(usize, String, Arc<Type>)>,
|
||||||
|
check_last_item: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
Tuple {
|
// ListAccess
|
||||||
|
ListAccessor {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
count: usize,
|
names: Vec<String>,
|
||||||
|
tail: bool,
|
||||||
|
check_last_item: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
ListExpose {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
tail_head_names: Vec<(String, String)>,
|
||||||
|
tail: Option<(String, String)>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tuple Access
|
||||||
|
TupleAccessor {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
names: Vec<String>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
check_last_item: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
TupleIndex {
|
TupleIndex {
|
||||||
|
@ -196,6 +229,7 @@ pub enum Air {
|
||||||
tuple_index: usize,
|
tuple_index: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Misc.
|
||||||
Todo {
|
Todo {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
label: Option<String>,
|
label: Option<String>,
|
||||||
|
@ -213,23 +247,6 @@ pub enum Air {
|
||||||
text: Option<String>,
|
text: Option<String>,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
RecordUpdate {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
highest_index: usize,
|
|
||||||
indices: Vec<(usize, Arc<Type>)>,
|
|
||||||
},
|
|
||||||
|
|
||||||
UnOp {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
op: UnOp,
|
|
||||||
},
|
|
||||||
|
|
||||||
TupleAccessor {
|
|
||||||
scope: Vec<u64>,
|
|
||||||
names: Vec<String>,
|
|
||||||
tipo: Arc<Type>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Air {
|
impl Air {
|
||||||
|
@ -239,68 +256,142 @@ impl Air {
|
||||||
| Air::String { scope, .. }
|
| Air::String { scope, .. }
|
||||||
| Air::ByteArray { scope, .. }
|
| Air::ByteArray { scope, .. }
|
||||||
| Air::Bool { scope, .. }
|
| Air::Bool { scope, .. }
|
||||||
| Air::Var { scope, .. }
|
|
||||||
| Air::List { scope, .. }
|
| Air::List { scope, .. }
|
||||||
| Air::ListAccessor { scope, .. }
|
| Air::Tuple { scope, .. }
|
||||||
| Air::ListExpose { scope, .. }
|
| Air::Void { scope }
|
||||||
| Air::Fn { scope, .. }
|
| Air::Var { scope, .. }
|
||||||
| Air::Call { scope, .. }
|
| Air::Call { scope, .. }
|
||||||
|
| Air::DefineFunc { scope, .. }
|
||||||
|
| Air::Fn { scope, .. }
|
||||||
| Air::Builtin { scope, .. }
|
| Air::Builtin { scope, .. }
|
||||||
| Air::BinOp { scope, .. }
|
| Air::BinOp { scope, .. }
|
||||||
| Air::Assignment { scope, .. }
|
| Air::UnOp { scope, .. }
|
||||||
| Air::Assert { scope, .. }
|
| Air::Let { scope, .. }
|
||||||
| Air::DefineFunc { scope, .. }
|
| Air::UnWrapData { scope, .. }
|
||||||
| Air::Lam { scope, .. }
|
| Air::AssertConstr { scope, .. }
|
||||||
| Air::When { scope, .. }
|
| Air::When { scope, .. }
|
||||||
| Air::Clause { scope, .. }
|
| Air::Clause { scope, .. }
|
||||||
| Air::ListClause { scope, .. }
|
| Air::ListClause { scope, .. }
|
||||||
| Air::TupleClause { scope, .. }
|
|
||||||
| Air::WrapClause { scope }
|
| Air::WrapClause { scope }
|
||||||
|
| Air::TupleClause { scope, .. }
|
||||||
| Air::ClauseGuard { scope, .. }
|
| Air::ClauseGuard { scope, .. }
|
||||||
| Air::ListClauseGuard { scope, .. }
|
| Air::ListClauseGuard { scope, .. }
|
||||||
| Air::Discard { scope }
|
|
||||||
| Air::Finally { scope }
|
| Air::Finally { scope }
|
||||||
| Air::If { scope, .. }
|
| Air::If { scope, .. }
|
||||||
| Air::Record { scope, .. }
|
| Air::Record { scope, .. }
|
||||||
|
| Air::RecordUpdate { scope, .. }
|
||||||
| Air::RecordAccess { scope, .. }
|
| Air::RecordAccess { scope, .. }
|
||||||
| Air::FieldsExpose { scope, .. }
|
| Air::FieldsExpose { scope, .. }
|
||||||
| Air::Tuple { scope, .. }
|
| Air::ListAccessor { scope, .. }
|
||||||
|
| Air::ListExpose { scope, .. }
|
||||||
|
| Air::TupleAccessor { scope, .. }
|
||||||
|
| Air::TupleIndex { scope, .. }
|
||||||
| Air::Todo { scope, .. }
|
| Air::Todo { scope, .. }
|
||||||
| Air::ErrorTerm { scope, .. }
|
| Air::ErrorTerm { scope, .. }
|
||||||
| Air::RecordUpdate { scope, .. }
|
| Air::Trace { scope, .. } => scope.clone(),
|
||||||
| Air::UnOp { scope, .. }
|
|
||||||
| Air::Trace { scope, .. }
|
|
||||||
| Air::TupleAccessor { scope, .. }
|
|
||||||
| Air::TupleIndex { scope, .. } => scope.to_vec(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tipo(&self) -> Option<Arc<Type>> {
|
pub fn tipo(&self) -> Option<Arc<Type>> {
|
||||||
match self {
|
match self {
|
||||||
|
Air::Int { .. } => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Air::String { .. } => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "String".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Air::ByteArray { .. } => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "ByteArray".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Air::Bool { .. } => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "Bool".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Air::Void { .. } => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "Void".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Air::Var { constructor, .. } => Some(constructor.tipo.clone()),
|
||||||
Air::List { tipo, .. }
|
Air::List { tipo, .. }
|
||||||
| Air::ListAccessor { tipo, .. }
|
| Air::Tuple { tipo, .. }
|
||||||
| Air::ListExpose { tipo, .. }
|
| Air::Call { tipo, .. }
|
||||||
| Air::Builtin { tipo, .. }
|
| Air::Builtin { tipo, .. }
|
||||||
| Air::BinOp { tipo, .. }
|
| Air::BinOp { tipo, .. }
|
||||||
|
| Air::UnWrapData { tipo, .. }
|
||||||
| Air::When { tipo, .. }
|
| Air::When { tipo, .. }
|
||||||
| Air::Clause { tipo, .. }
|
| Air::Clause { tipo, .. }
|
||||||
| Air::ListClause { tipo, .. }
|
| Air::ListClause { tipo, .. }
|
||||||
| Air::TupleClause { tipo, .. }
|
| Air::TupleClause { tipo, .. }
|
||||||
| Air::ClauseGuard { tipo, .. }
|
| Air::ClauseGuard { tipo, .. }
|
||||||
|
| Air::If { tipo, .. }
|
||||||
| Air::ListClauseGuard { tipo, .. }
|
| Air::ListClauseGuard { tipo, .. }
|
||||||
| Air::Record { tipo, .. }
|
| Air::Record { tipo, .. }
|
||||||
|
| Air::RecordUpdate { tipo, .. }
|
||||||
| Air::RecordAccess { tipo, .. }
|
| Air::RecordAccess { tipo, .. }
|
||||||
| Air::Tuple { tipo, .. }
|
| Air::ListAccessor { tipo, .. }
|
||||||
|
| Air::ListExpose { tipo, .. }
|
||||||
|
| Air::TupleAccessor { tipo, .. }
|
||||||
| Air::TupleIndex { tipo, .. }
|
| Air::TupleIndex { tipo, .. }
|
||||||
| Air::Todo { tipo, .. }
|
| Air::Todo { tipo, .. }
|
||||||
| Air::ErrorTerm { tipo, .. }
|
| Air::ErrorTerm { tipo, .. }
|
||||||
| Air::Trace { tipo, .. }
|
| Air::Trace { tipo, .. } => Some(tipo.clone()),
|
||||||
| Air::TupleAccessor { tipo, .. }
|
|
||||||
| Air::Var {
|
Air::DefineFunc { .. }
|
||||||
constructor: ValueConstructor { tipo, .. },
|
| Air::Fn { .. }
|
||||||
..
|
| Air::Let { .. }
|
||||||
} => Some(tipo.clone()),
|
| Air::WrapClause { .. }
|
||||||
_ => None,
|
| Air::AssertConstr { .. }
|
||||||
|
| Air::Finally { .. }
|
||||||
|
| Air::FieldsExpose { .. } => None,
|
||||||
|
|
||||||
|
Air::UnOp { op, .. } => match op {
|
||||||
|
UnOp::Not => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "Bool".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
UnOp::Negate => Some(
|
||||||
|
Type::App {
|
||||||
|
public: true,
|
||||||
|
module: String::new(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use uplc::{
|
use uplc::{
|
||||||
ast::{
|
ast::{
|
||||||
builder::{apply_wrap, if_else},
|
builder::{apply_wrap, delayed_choose_list, if_else},
|
||||||
Constant as UplcConstant, Name, Term, Type as UplcType,
|
Constant as UplcConstant, Name, Term, Type as UplcType,
|
||||||
},
|
},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
|
@ -14,7 +14,7 @@ use uplc::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
air::Air,
|
air::Air,
|
||||||
ast::{Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
|
ast::{AssignmentKind, Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
|
||||||
expr::TypedExpr,
|
expr::TypedExpr,
|
||||||
tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant},
|
tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant},
|
||||||
};
|
};
|
||||||
|
@ -48,6 +48,12 @@ pub struct FunctionAccessKey {
|
||||||
pub variant_name: String,
|
pub variant_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AssignmentProperties {
|
||||||
|
pub value_is_data: bool,
|
||||||
|
pub kind: AssignmentKind,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ClauseProperties {
|
pub enum ClauseProperties {
|
||||||
ConstrClause {
|
ConstrClause {
|
||||||
|
@ -475,11 +481,13 @@ pub fn list_access_to_uplc(
|
||||||
tail: bool,
|
tail: bool,
|
||||||
current_index: usize,
|
current_index: usize,
|
||||||
term: Term<Name>,
|
term: Term<Name>,
|
||||||
tipo: &Type,
|
tipos: Vec<Arc<Type>>,
|
||||||
|
check_last_item: bool,
|
||||||
) -> Term<Name> {
|
) -> Term<Name> {
|
||||||
let (first, names) = names.split_first().unwrap();
|
if let Some((first, names)) = names.split_first() {
|
||||||
|
let (current_tipo, tipos) = tipos.split_first().unwrap();
|
||||||
|
|
||||||
let head_list = if tipo.is_map() {
|
let head_list = if current_tipo.is_map() {
|
||||||
apply_wrap(
|
apply_wrap(
|
||||||
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
|
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
|
||||||
Term::Var(Name {
|
Term::Var(Name {
|
||||||
|
@ -496,7 +504,7 @@ pub fn list_access_to_uplc(
|
||||||
unique: 0.into(),
|
unique: 0.into(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
&tipo.clone().get_inner_types()[0],
|
¤t_tipo.to_owned(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -538,32 +546,6 @@ pub fn list_access_to_uplc(
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
} else if names.is_empty() {
|
} else if names.is_empty() {
|
||||||
// Maybe check list is actually empty or should we leave that to when .. is only
|
|
||||||
// this would replace term.into() if we decide to
|
|
||||||
// body: choose_list(
|
|
||||||
// apply_wrap(
|
|
||||||
// Term::Builtin(DefaultFunction::TailList).force_wrap(),
|
|
||||||
// Term::Var(Name {
|
|
||||||
// text: format!(
|
|
||||||
// "tail_index_{}_{}",
|
|
||||||
// current_index, id_list[current_index]
|
|
||||||
// ),
|
|
||||||
// unique: 0.into(),
|
|
||||||
// }),
|
|
||||||
// ),
|
|
||||||
// term,
|
|
||||||
// apply_wrap(
|
|
||||||
// apply_wrap(
|
|
||||||
// Term::Builtin(DefaultFunction::Trace).force_wrap(),
|
|
||||||
// Term::Constant(UplcConstant::String(
|
|
||||||
// "List contains more items".to_string(),
|
|
||||||
// )),
|
|
||||||
// ),
|
|
||||||
// Term::Delay(Term::Error.into()),
|
|
||||||
// )
|
|
||||||
// .force_wrap(),
|
|
||||||
// )
|
|
||||||
// .into(),
|
|
||||||
Term::Lambda {
|
Term::Lambda {
|
||||||
parameter_name: Name {
|
parameter_name: Name {
|
||||||
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
|
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
|
||||||
|
@ -575,15 +557,37 @@ pub fn list_access_to_uplc(
|
||||||
text: first.clone(),
|
text: first.clone(),
|
||||||
unique: 0.into(),
|
unique: 0.into(),
|
||||||
},
|
},
|
||||||
body: term.into(),
|
body: if check_last_item {
|
||||||
},
|
delayed_choose_list(
|
||||||
apply_wrap(
|
apply_wrap(
|
||||||
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
|
Term::Builtin(DefaultFunction::TailList).force_wrap(),
|
||||||
Term::Var(Name {
|
Term::Var(Name {
|
||||||
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
|
text: format!(
|
||||||
|
"tail_index_{}_{}",
|
||||||
|
current_index, id_list[current_index]
|
||||||
|
),
|
||||||
unique: 0.into(),
|
unique: 0.into(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
term,
|
||||||
|
apply_wrap(
|
||||||
|
apply_wrap(
|
||||||
|
Term::Builtin(DefaultFunction::Trace).force_wrap(),
|
||||||
|
Term::Constant(UplcConstant::String(
|
||||||
|
"List/Tuple contains more items than it should"
|
||||||
|
.to_string(),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Term::Delay(Term::Error.into()),
|
||||||
|
)
|
||||||
|
.force_wrap(),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
term.into()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
head_list,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
@ -600,7 +604,15 @@ pub fn list_access_to_uplc(
|
||||||
unique: 0.into(),
|
unique: 0.into(),
|
||||||
},
|
},
|
||||||
body: apply_wrap(
|
body: apply_wrap(
|
||||||
list_access_to_uplc(names, id_list, tail, current_index + 1, term, tipo),
|
list_access_to_uplc(
|
||||||
|
names,
|
||||||
|
id_list,
|
||||||
|
tail,
|
||||||
|
current_index + 1,
|
||||||
|
term,
|
||||||
|
tipos.to_owned(),
|
||||||
|
check_last_item,
|
||||||
|
),
|
||||||
apply_wrap(
|
apply_wrap(
|
||||||
Term::Builtin(DefaultFunction::TailList).force_wrap(),
|
Term::Builtin(DefaultFunction::TailList).force_wrap(),
|
||||||
Term::Var(Name {
|
Term::Var(Name {
|
||||||
|
@ -619,6 +631,9 @@ pub fn list_access_to_uplc(
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
term
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec<u64> {
|
pub fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec<u64> {
|
||||||
|
@ -1072,6 +1087,7 @@ pub fn monomorphize(
|
||||||
tipo,
|
tipo,
|
||||||
names,
|
names,
|
||||||
tail,
|
tail,
|
||||||
|
check_last_item,
|
||||||
} => {
|
} => {
|
||||||
if tipo.is_generic() {
|
if tipo.is_generic() {
|
||||||
let mut tipo = tipo.clone();
|
let mut tipo = tipo.clone();
|
||||||
|
@ -1082,6 +1098,7 @@ pub fn monomorphize(
|
||||||
names,
|
names,
|
||||||
tipo,
|
tipo,
|
||||||
tail,
|
tail,
|
||||||
|
check_last_item,
|
||||||
};
|
};
|
||||||
needs_variant = true;
|
needs_variant = true;
|
||||||
}
|
}
|
||||||
|
@ -1133,8 +1150,15 @@ pub fn monomorphize(
|
||||||
needs_variant = true;
|
needs_variant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO check on assignment if type is needed
|
Air::UnWrapData { scope, tipo } => {
|
||||||
Air::Assignment { .. } => {}
|
if tipo.is_generic() {
|
||||||
|
let mut tipo = tipo.clone();
|
||||||
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
|
||||||
|
new_air[index] = Air::UnWrapData { scope, tipo };
|
||||||
|
needs_variant = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Air::When {
|
Air::When {
|
||||||
scope,
|
scope,
|
||||||
tipo,
|
tipo,
|
||||||
|
@ -1346,8 +1370,8 @@ pub fn monomorphize(
|
||||||
}
|
}
|
||||||
Air::FieldsExpose {
|
Air::FieldsExpose {
|
||||||
scope,
|
scope,
|
||||||
count,
|
|
||||||
indices,
|
indices,
|
||||||
|
check_last_item,
|
||||||
} => {
|
} => {
|
||||||
let mut new_indices = vec![];
|
let mut new_indices = vec![];
|
||||||
for (ind, name, tipo) in indices {
|
for (ind, name, tipo) in indices {
|
||||||
|
@ -1362,16 +1386,18 @@ pub fn monomorphize(
|
||||||
}
|
}
|
||||||
new_air[index] = Air::FieldsExpose {
|
new_air[index] = Air::FieldsExpose {
|
||||||
scope,
|
scope,
|
||||||
count,
|
|
||||||
indices: new_indices,
|
indices: new_indices,
|
||||||
|
check_last_item,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Air::RecordUpdate {
|
Air::RecordUpdate {
|
||||||
scope,
|
scope,
|
||||||
highest_index,
|
highest_index,
|
||||||
indices,
|
indices,
|
||||||
|
tipo,
|
||||||
} => {
|
} => {
|
||||||
let mut new_indices = vec![];
|
let mut new_indices = vec![];
|
||||||
|
let mut tipo = tipo.clone();
|
||||||
for (ind, tipo) in indices {
|
for (ind, tipo) in indices {
|
||||||
if tipo.is_generic() {
|
if tipo.is_generic() {
|
||||||
let mut tipo = tipo.clone();
|
let mut tipo = tipo.clone();
|
||||||
|
@ -1382,18 +1408,51 @@ pub fn monomorphize(
|
||||||
new_indices.push((ind, tipo));
|
new_indices.push((ind, tipo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if tipo.is_generic() {
|
||||||
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
}
|
||||||
new_air[index] = Air::RecordUpdate {
|
new_air[index] = Air::RecordUpdate {
|
||||||
scope,
|
scope,
|
||||||
highest_index,
|
highest_index,
|
||||||
indices: new_indices,
|
indices: new_indices,
|
||||||
|
tipo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Air::TupleAccessor { scope, names, tipo } => {
|
Air::TupleAccessor {
|
||||||
|
scope,
|
||||||
|
names,
|
||||||
|
tipo,
|
||||||
|
check_last_item,
|
||||||
|
} => {
|
||||||
if tipo.is_generic() {
|
if tipo.is_generic() {
|
||||||
let mut tipo = tipo.clone();
|
let mut tipo = tipo.clone();
|
||||||
find_generics_to_replace(&mut tipo, &generic_types);
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
|
||||||
new_air[index] = Air::TupleAccessor { scope, names, tipo };
|
new_air[index] = Air::TupleAccessor {
|
||||||
|
scope,
|
||||||
|
names,
|
||||||
|
tipo,
|
||||||
|
check_last_item,
|
||||||
|
};
|
||||||
|
needs_variant = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Air::Call { scope, count, tipo } => {
|
||||||
|
if tipo.is_generic() {
|
||||||
|
let mut tipo = tipo.clone();
|
||||||
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
|
||||||
|
new_air[index] = Air::Call { scope, count, tipo };
|
||||||
|
needs_variant = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Air::If { scope, tipo } => {
|
||||||
|
if tipo.is_generic() {
|
||||||
|
let mut tipo = tipo.clone();
|
||||||
|
find_generics_to_replace(&mut tipo, &generic_types);
|
||||||
|
|
||||||
|
new_air[index] = Air::If { scope, tipo };
|
||||||
needs_variant = true;
|
needs_variant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1511,10 +1570,11 @@ pub fn handle_recursion_ir(
|
||||||
let current_call = recursion_ir[index - 1].clone();
|
let current_call = recursion_ir[index - 1].clone();
|
||||||
|
|
||||||
match current_call {
|
match current_call {
|
||||||
Air::Call { scope, count } => {
|
Air::Call { scope, count, tipo } => {
|
||||||
recursion_ir[index - 1] = Air::Call {
|
recursion_ir[index - 1] = Air::Call {
|
||||||
scope,
|
scope,
|
||||||
count: count + 1,
|
count: count + 1,
|
||||||
|
tipo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|
|
@ -173,6 +173,14 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_data(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::App { module, name, .. } => "Data" == name && module.is_empty(),
|
||||||
|
Self::Var { tipo } => tipo.borrow().is_data(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_generic(&self) -> bool {
|
pub fn is_generic(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Type::App { args, .. } => {
|
Type::App { args, .. } => {
|
||||||
|
@ -205,6 +213,7 @@ impl Type {
|
||||||
match self {
|
match self {
|
||||||
Self::Fn { args, .. } => Some(args.clone()),
|
Self::Fn { args, .. } => Some(args.clone()),
|
||||||
Self::App { args, .. } => Some(args.clone()),
|
Self::App { args, .. } => Some(args.clone()),
|
||||||
|
Self::Var { tipo } => tipo.borrow().arg_types(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,6 +476,13 @@ impl TypeVar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_data(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Link { tipo } => tipo.is_data(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_generic(&self) -> bool {
|
pub fn is_generic(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
TypeVar::Generic { .. } => true,
|
TypeVar::Generic { .. } => true,
|
||||||
|
@ -483,6 +499,13 @@ impl TypeVar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn arg_types(&self) -> Option<Vec<Arc<Type>>> {
|
||||||
|
match self {
|
||||||
|
Self::Link { tipo } => tipo.arg_types(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_inner_type(&self) -> Vec<Arc<Type>> {
|
pub fn get_inner_type(&self) -> Vec<Arc<Type>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Link { tipo } => tipo.get_inner_types(),
|
Self::Link { tipo } => tipo.get_inner_types(),
|
||||||
|
|
|
@ -724,6 +724,15 @@ The best thing to do from here is to remove it."#))]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I discovered a type cast from Data without an annotation")]
|
||||||
|
#[diagnostic(code("illegal::type_cast"))]
|
||||||
|
#[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
|
||||||
|
CastDataNoAnn {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
value: UntypedExpr,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
|
|
@ -807,8 +807,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
annotation: &Option<Annotation>,
|
annotation: &Option<Annotation>,
|
||||||
location: Span,
|
location: Span,
|
||||||
) -> Result<TypedExpr, Error> {
|
) -> Result<TypedExpr, Error> {
|
||||||
let value = self.in_new_scope(|value_typer| value_typer.infer(value))?;
|
let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?;
|
||||||
let mut value_typ = value.tipo();
|
let mut value_typ = typed_value.tipo();
|
||||||
|
|
||||||
// Check that any type annotation is accurate.
|
// Check that any type annotation is accurate.
|
||||||
let pattern = if let Some(ann) = annotation {
|
let pattern = if let Some(ann) = annotation {
|
||||||
|
@ -819,7 +819,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
self.unify(
|
self.unify(
|
||||||
ann_typ.clone(),
|
ann_typ.clone(),
|
||||||
value_typ.clone(),
|
value_typ.clone(),
|
||||||
value.type_defining_location(),
|
typed_value.type_defining_location(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
value_typ = ann_typ.clone();
|
value_typ = ann_typ.clone();
|
||||||
|
@ -831,6 +831,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
Some(ann_typ),
|
Some(ann_typ),
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
|
if value_typ.is_data() {
|
||||||
|
return Err(Error::CastDataNoAnn {
|
||||||
|
location,
|
||||||
|
value: UntypedExpr::Assignment {
|
||||||
|
location,
|
||||||
|
value: value.into(),
|
||||||
|
pattern,
|
||||||
|
kind,
|
||||||
|
annotation: Some(Annotation::Constructor {
|
||||||
|
location: Span::empty(),
|
||||||
|
module: None,
|
||||||
|
name: "Type".to_string(),
|
||||||
|
arguments: vec![],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the pattern matches the type of the value
|
// Ensure the pattern matches the type of the value
|
||||||
PatternTyper::new(self.environment, &self.hydrator).unify(
|
PatternTyper::new(self.environment, &self.hydrator).unify(
|
||||||
pattern,
|
pattern,
|
||||||
|
@ -860,7 +878,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
tipo: value_typ,
|
tipo: value_typ,
|
||||||
kind,
|
kind,
|
||||||
pattern,
|
pattern,
|
||||||
value: Box::new(value),
|
value: Box::new(typed_value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,7 @@ use super::{Constant, Name, Term};
|
||||||
pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer";
|
pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer";
|
||||||
pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer";
|
pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer";
|
||||||
pub const CONSTR_GET_FIELD: &str = "__constr_get_field";
|
pub const CONSTR_GET_FIELD: &str = "__constr_get_field";
|
||||||
|
pub const ASSERT_ON_LIST: &str = "__assert_on_list";
|
||||||
|
|
||||||
pub fn apply_wrap(function: Term<Name>, arg: Term<Name>) -> Term<Name> {
|
pub fn apply_wrap(function: Term<Name>, arg: Term<Name>) -> Term<Name> {
|
||||||
Term::Apply {
|
Term::Apply {
|
||||||
|
@ -31,6 +32,108 @@ pub fn final_wrapper(term: Term<Name>) -> Term<Name> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assert_on_list(term: Term<Name>) -> Term<Name> {
|
||||||
|
apply_wrap(
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: apply_wrap(
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: term.into(),
|
||||||
|
},
|
||||||
|
apply_wrap(
|
||||||
|
Term::Var(Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
Term::Var(Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
},
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: "list_to_check".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: "check_with".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: delayed_choose_list(
|
||||||
|
Term::Var(Name {
|
||||||
|
text: "list_to_check".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
Term::Constant(Constant::Unit),
|
||||||
|
apply_wrap(
|
||||||
|
apply_wrap(
|
||||||
|
Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(),
|
||||||
|
apply_wrap(
|
||||||
|
Term::Var(Name {
|
||||||
|
text: "check_with".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
apply_wrap(
|
||||||
|
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
|
||||||
|
Term::Var(Name {
|
||||||
|
text: "list_to_check".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
apply_wrap(
|
||||||
|
apply_wrap(
|
||||||
|
apply_wrap(
|
||||||
|
Term::Var(Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
Term::Var(Name {
|
||||||
|
text: ASSERT_ON_LIST.to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
apply_wrap(
|
||||||
|
Term::Builtin(DefaultFunction::TailList).force_wrap(),
|
||||||
|
Term::Var(Name {
|
||||||
|
text: "list_to_check".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Term::Var(Name {
|
||||||
|
text: "check_with".to_string(),
|
||||||
|
unique: 0.into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn constr_fields_exposer(term: Term<Name>) -> Term<Name> {
|
pub fn constr_fields_exposer(term: Term<Name>) -> Term<Name> {
|
||||||
Term::Apply {
|
Term::Apply {
|
||||||
function: Term::Lambda {
|
function: Term::Lambda {
|
||||||
|
|
|
@ -343,7 +343,7 @@ impl Machine {
|
||||||
Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone()))
|
Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rest => Err(Error::NonFunctionalApplication(rest)),
|
rest => Err(Error::NonFunctionalApplication(rest, argument)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ pub enum Error {
|
||||||
EvaluationFailure,
|
EvaluationFailure,
|
||||||
#[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")]
|
#[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")]
|
||||||
NonPolymorphicInstantiation(Value),
|
NonPolymorphicInstantiation(Value),
|
||||||
#[error("Attempted to apply a non-function:\n\n{0:#?}")]
|
#[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")]
|
||||||
NonFunctionalApplication(Value),
|
NonFunctionalApplication(Value, Value),
|
||||||
#[error("Type mismatch expected '{0}' got '{1}'")]
|
#[error("Type mismatch expected '{0}' got '{1}'")]
|
||||||
TypeMismatch(Type, Type),
|
TypeMismatch(Type, Type),
|
||||||
#[error("Type mismatch expected '(list a)' got '{0}'")]
|
#[error("Type mismatch expected '(list a)' got '{0}'")]
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
pub type Door{
|
||||||
|
angle: Int,
|
||||||
|
locked: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub type Car {
|
pub type Car {
|
||||||
Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int }
|
Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int }
|
||||||
Ford {
|
Ford {
|
||||||
|
@ -5,18 +11,19 @@ pub type Car {
|
||||||
owner: ByteArray,
|
owner: ByteArray,
|
||||||
wheels: Int,
|
wheels: Int,
|
||||||
truck_bed_limit: Int,
|
truck_bed_limit: Int,
|
||||||
|
car_doors: List<Door>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test update_owner2_should_fail(){
|
// test update_owner2_should_fail(){
|
||||||
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000}
|
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: []}
|
||||||
// assert Honda{ owner, ..} = initial_car
|
// assert Honda{ owner, ..}: Car = initial_car
|
||||||
// owner == #[]
|
// owner == #[]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
test update_owner1() {
|
test update_owner1() {
|
||||||
let initial_car: Data =
|
let initial_car: Data =
|
||||||
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 }
|
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] }
|
||||||
assert Ford { owner, .. } = initial_car
|
assert Ford { owner, .. }: Car = initial_car
|
||||||
owner == #[]
|
owner == #[]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue