Merge pull request #315 from aiken-lang/assert-data-type

Assert data type to type cast
This commit is contained in:
Lucas 2023-01-30 11:34:47 -05:00 committed by GitHub
commit f7421032bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1834 additions and 894 deletions

View File

@ -10,6 +10,7 @@ use crate::{
#[derive(Debug, Clone)]
pub enum Air {
// Primitives
Int {
scope: Vec<u64>,
value: String,
@ -30,17 +31,6 @@ pub enum Air {
value: bool,
},
Var {
scope: Vec<u64>,
constructor: ValueConstructor,
name: String,
variant_name: String,
},
Fn {
scope: Vec<u64>,
params: Vec<String>,
},
List {
scope: Vec<u64>,
count: usize,
@ -48,48 +38,30 @@ pub enum Air {
tail: bool,
},
ListAccessor {
Tuple {
scope: Vec<u64>,
tipo: Arc<Type>,
names: Vec<String>,
tail: bool,
count: usize,
},
ListExpose {
Void {
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 {
scope: Vec<u64>,
count: usize,
},
Builtin {
scope: Vec<u64>,
func: DefaultFunction,
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 {
scope: Vec<u64>,
func_name: String,
@ -99,11 +71,47 @@ pub enum Air {
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>,
name: String,
},
UnWrapData {
scope: Vec<u64>,
tipo: Arc<Type>,
},
AssertConstr {
scope: Vec<u64>,
constr_index: usize,
},
// When
When {
scope: Vec<u64>,
tipo: Arc<Type>,
@ -153,18 +161,17 @@ pub enum Air {
inverse: bool,
},
Discard {
scope: Vec<u64>,
},
Finally {
scope: Vec<u64>,
},
// If
If {
scope: Vec<u64>,
tipo: Arc<Type>,
},
// Record Creation
Record {
scope: Vec<u64>,
constr_index: usize,
@ -172,6 +179,14 @@ pub enum Air {
count: usize,
},
RecordUpdate {
scope: Vec<u64>,
highest_index: usize,
indices: Vec<(usize, Arc<Type>)>,
tipo: Arc<Type>,
},
// Field Access
RecordAccess {
scope: Vec<u64>,
record_index: u64,
@ -180,14 +195,32 @@ pub enum Air {
FieldsExpose {
scope: Vec<u64>,
count: usize,
indices: Vec<(usize, String, Arc<Type>)>,
check_last_item: bool,
},
Tuple {
// ListAccess
ListAccessor {
scope: Vec<u64>,
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 {
@ -196,6 +229,7 @@ pub enum Air {
tuple_index: usize,
},
// Misc.
Todo {
scope: Vec<u64>,
label: Option<String>,
@ -213,23 +247,6 @@ pub enum Air {
text: Option<String>,
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 {
@ -239,68 +256,142 @@ impl Air {
| Air::String { scope, .. }
| Air::ByteArray { scope, .. }
| Air::Bool { scope, .. }
| Air::Var { scope, .. }
| Air::List { scope, .. }
| Air::ListAccessor { scope, .. }
| Air::ListExpose { scope, .. }
| Air::Fn { scope, .. }
| Air::Tuple { scope, .. }
| Air::Void { scope }
| Air::Var { scope, .. }
| Air::Call { scope, .. }
| Air::DefineFunc { scope, .. }
| Air::Fn { scope, .. }
| Air::Builtin { scope, .. }
| Air::BinOp { scope, .. }
| Air::Assignment { scope, .. }
| Air::Assert { scope, .. }
| Air::DefineFunc { scope, .. }
| Air::Lam { scope, .. }
| Air::UnOp { scope, .. }
| Air::Let { scope, .. }
| Air::UnWrapData { scope, .. }
| Air::AssertConstr { scope, .. }
| Air::When { scope, .. }
| Air::Clause { scope, .. }
| Air::ListClause { scope, .. }
| Air::TupleClause { scope, .. }
| Air::WrapClause { scope }
| Air::TupleClause { scope, .. }
| Air::ClauseGuard { scope, .. }
| Air::ListClauseGuard { scope, .. }
| Air::Discard { scope }
| Air::Finally { scope }
| Air::If { scope, .. }
| Air::Record { scope, .. }
| Air::RecordUpdate { scope, .. }
| Air::RecordAccess { scope, .. }
| Air::FieldsExpose { scope, .. }
| Air::Tuple { scope, .. }
| Air::ListAccessor { scope, .. }
| Air::ListExpose { scope, .. }
| Air::TupleAccessor { scope, .. }
| Air::TupleIndex { scope, .. }
| Air::Todo { scope, .. }
| Air::ErrorTerm { scope, .. }
| Air::RecordUpdate { scope, .. }
| Air::UnOp { scope, .. }
| Air::Trace { scope, .. }
| Air::TupleAccessor { scope, .. }
| Air::TupleIndex { scope, .. } => scope.to_vec(),
| Air::Trace { scope, .. } => scope.clone(),
}
}
pub fn tipo(&self) -> Option<Arc<Type>> {
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::ListAccessor { tipo, .. }
| Air::ListExpose { tipo, .. }
| Air::Tuple { tipo, .. }
| Air::Call { tipo, .. }
| Air::Builtin { tipo, .. }
| Air::BinOp { tipo, .. }
| Air::UnWrapData { tipo, .. }
| Air::When { tipo, .. }
| Air::Clause { tipo, .. }
| Air::ListClause { tipo, .. }
| Air::TupleClause { tipo, .. }
| Air::ClauseGuard { tipo, .. }
| Air::If { tipo, .. }
| Air::ListClauseGuard { tipo, .. }
| Air::Record { tipo, .. }
| Air::RecordUpdate { tipo, .. }
| Air::RecordAccess { tipo, .. }
| Air::Tuple { tipo, .. }
| Air::ListAccessor { tipo, .. }
| Air::ListExpose { tipo, .. }
| Air::TupleAccessor { tipo, .. }
| Air::TupleIndex { tipo, .. }
| Air::Todo { tipo, .. }
| Air::ErrorTerm { tipo, .. }
| Air::Trace { tipo, .. }
| Air::TupleAccessor { tipo, .. }
| Air::Var {
constructor: ValueConstructor { tipo, .. },
..
} => Some(tipo.clone()),
_ => None,
| Air::Trace { tipo, .. } => Some(tipo.clone()),
Air::DefineFunc { .. }
| Air::Fn { .. }
| Air::Let { .. }
| Air::WrapClause { .. }
| 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(),
),
},
}
}
}

View File

@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use uplc::{
ast::{
builder::{apply_wrap, if_else},
builder::{apply_wrap, delayed_choose_list, if_else},
Constant as UplcConstant, Name, Term, Type as UplcType,
},
builtins::DefaultFunction,
@ -14,7 +14,7 @@ use uplc::{
use crate::{
air::Air,
ast::{Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
ast::{AssignmentKind, Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
expr::TypedExpr,
tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant},
};
@ -48,6 +48,12 @@ pub struct FunctionAccessKey {
pub variant_name: String,
}
#[derive(Clone, Debug)]
pub struct AssignmentProperties {
pub value_is_data: bool,
pub kind: AssignmentKind,
}
#[derive(Clone, Debug)]
pub enum ClauseProperties {
ConstrClause {
@ -475,108 +481,22 @@ pub fn list_access_to_uplc(
tail: bool,
current_index: usize,
term: Term<Name>,
tipo: &Type,
tipos: Vec<Arc<Type>>,
check_last_item: bool,
) -> 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() {
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
}),
)
} else {
convert_data_to_type(
let head_list = if current_tipo.is_map() {
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
}),
),
&tipo.clone().get_inner_types()[0],
)
};
if names.len() == 1 && tail {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: names[0].clone(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
} 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 {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: term.into(),
},
} else {
convert_data_to_type(
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
@ -584,40 +504,135 @@ pub fn list_access_to_uplc(
unique: 0.into(),
}),
),
&current_tipo.to_owned(),
)
.into(),
};
if names.len() == 1 && tail {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: names[0].clone(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
} else if names.is_empty() {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: if check_last_item {
delayed_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/Tuple contains more items than it should"
.to_string(),
)),
),
Term::Delay(Term::Error.into()),
)
.force_wrap(),
)
.into()
} else {
term.into()
},
},
head_list,
)
.into(),
}
} else {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
list_access_to_uplc(
names,
id_list,
tail,
current_index + 1,
term,
tipos.to_owned(),
check_last_item,
),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
}
} else {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
list_access_to_uplc(names, id_list, tail, current_index + 1, term, tipo),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
term
}
}
@ -1072,6 +1087,7 @@ pub fn monomorphize(
tipo,
names,
tail,
check_last_item,
} => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
@ -1082,6 +1098,7 @@ pub fn monomorphize(
names,
tipo,
tail,
check_last_item,
};
needs_variant = true;
}
@ -1133,8 +1150,15 @@ pub fn monomorphize(
needs_variant = true;
}
}
// TODO check on assignment if type is needed
Air::Assignment { .. } => {}
Air::UnWrapData { scope, tipo } => {
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 {
scope,
tipo,
@ -1346,8 +1370,8 @@ pub fn monomorphize(
}
Air::FieldsExpose {
scope,
count,
indices,
check_last_item,
} => {
let mut new_indices = vec![];
for (ind, name, tipo) in indices {
@ -1362,16 +1386,18 @@ pub fn monomorphize(
}
new_air[index] = Air::FieldsExpose {
scope,
count,
indices: new_indices,
check_last_item,
};
}
Air::RecordUpdate {
scope,
highest_index,
indices,
tipo,
} => {
let mut new_indices = vec![];
let mut tipo = tipo.clone();
for (ind, tipo) in indices {
if tipo.is_generic() {
let mut tipo = tipo.clone();
@ -1382,18 +1408,51 @@ pub fn monomorphize(
new_indices.push((ind, tipo));
}
}
if tipo.is_generic() {
find_generics_to_replace(&mut tipo, &generic_types);
}
new_air[index] = Air::RecordUpdate {
scope,
highest_index,
indices: new_indices,
tipo,
};
}
Air::TupleAccessor { scope, names, tipo } => {
Air::TupleAccessor {
scope,
names,
tipo,
check_last_item,
} => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
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;
}
}
@ -1511,10 +1570,11 @@ pub fn handle_recursion_ir(
let current_call = recursion_ir[index - 1].clone();
match current_call {
Air::Call { scope, count } => {
Air::Call { scope, count, tipo } => {
recursion_ir[index - 1] = Air::Call {
scope,
count: count + 1,
tipo,
}
}
_ => unreachable!(),

View File

@ -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 {
match self {
Type::App { args, .. } => {
@ -205,6 +213,7 @@ impl Type {
match self {
Self::Fn { args, .. } => Some(args.clone()),
Self::App { args, .. } => Some(args.clone()),
Self::Var { tipo } => tipo.borrow().arg_types(),
_ => 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 {
match self {
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>> {
match self {
Self::Link { tipo } => tipo.get_inner_types(),

View File

@ -724,6 +724,15 @@ The best thing to do from here is to remove it."#))]
location: Span,
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 {

View File

@ -807,8 +807,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
annotation: &Option<Annotation>,
location: Span,
) -> Result<TypedExpr, Error> {
let value = self.in_new_scope(|value_typer| value_typer.infer(value))?;
let mut value_typ = value.tipo();
let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?;
let mut value_typ = typed_value.tipo();
// Check that any type annotation is accurate.
let pattern = if let Some(ann) = annotation {
@ -819,7 +819,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
self.unify(
ann_typ.clone(),
value_typ.clone(),
value.type_defining_location(),
typed_value.type_defining_location(),
)?;
value_typ = ann_typ.clone();
@ -831,6 +831,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Some(ann_typ),
)?
} 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
PatternTyper::new(self.environment, &self.hydrator).unify(
pattern,
@ -860,7 +878,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
tipo: value_typ,
kind,
pattern,
value: Box::new(value),
value: Box::new(typed_value),
})
}

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ use super::{Constant, Name, Term};
pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer";
pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer";
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> {
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> {
Term::Apply {
function: Term::Lambda {

View File

@ -343,7 +343,7 @@ impl Machine {
Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone()))
}
}
rest => Err(Error::NonFunctionalApplication(rest)),
rest => Err(Error::NonFunctionalApplication(rest, argument)),
}
}

View File

@ -16,8 +16,8 @@ pub enum Error {
EvaluationFailure,
#[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")]
NonPolymorphicInstantiation(Value),
#[error("Attempted to apply a non-function:\n\n{0:#?}")]
NonFunctionalApplication(Value),
#[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")]
NonFunctionalApplication(Value, Value),
#[error("Type mismatch expected '{0}' got '{1}'")]
TypeMismatch(Type, Type),
#[error("Type mismatch expected '(list a)' got '{0}'")]

View File

@ -1,3 +1,9 @@
pub type Door{
angle: Int,
locked: Bool
}
pub type Car {
Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int }
Ford {
@ -5,18 +11,19 @@ pub type Car {
owner: ByteArray,
wheels: Int,
truck_bed_limit: Int,
car_doors: List<Door>
}
}
// test update_owner2_should_fail(){
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000}
// assert Honda{ owner, ..} = initial_car
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: []}
// assert Honda{ owner, ..}: Car = initial_car
// owner == #[]
// }
test update_owner1() {
let initial_car: Data =
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 }
assert Ford { owner, .. } = initial_car
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] }
assert Ford { owner, .. }: Car = initial_car
owner == #[]
}