commit
f02b9b0f0c
|
@ -19,6 +19,7 @@
|
||||||
- **aiken-lang**: Strings can contain a nul byte using the escape sequence `\0`. @KtorZ
|
- **aiken-lang**: Strings can contain a nul byte using the escape sequence `\0`. @KtorZ
|
||||||
- **aiken**: The `check` command now accept an extra (optional) option `--max-success` to control the number of property-test iterations to perform. @KtorZ
|
- **aiken**: The `check` command now accept an extra (optional) option `--max-success` to control the number of property-test iterations to perform. @KtorZ
|
||||||
- **aiken**: The `docs` command now accept an optional flag `--include-dependencies` to include all dependencies in the generated documentation. @KtorZ
|
- **aiken**: The `docs` command now accept an optional flag `--include-dependencies` to include all dependencies in the generated documentation. @KtorZ
|
||||||
|
- **aiken-lang**: Implement [function backpassing](https://www.roc-lang.org/tutorial#backpassing) as a syntactic sugar. @KtorZ
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ use std::{
|
||||||
use uplc::machine::runtime::Compressable;
|
use uplc::machine::runtime::Compressable;
|
||||||
use vec1::Vec1;
|
use vec1::Vec1;
|
||||||
|
|
||||||
|
pub const BACKPASS_VARIABLE: &str = "_backpass";
|
||||||
pub const CAPTURE_VARIABLE: &str = "_capture";
|
pub const CAPTURE_VARIABLE: &str = "_capture";
|
||||||
pub const PIPE_VARIABLE: &str = "_pipe";
|
pub const PIPE_VARIABLE: &str = "_pipe";
|
||||||
|
|
||||||
|
@ -792,6 +793,19 @@ impl<A> Arg<A> {
|
||||||
self.arg_name.get_variable_name()
|
self.arg_name.get_variable_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_capture(&self) -> bool {
|
||||||
|
if let ArgName::Named {
|
||||||
|
ref name, location, ..
|
||||||
|
} = self.arg_name
|
||||||
|
{
|
||||||
|
return name.starts_with(CAPTURE_VARIABLE)
|
||||||
|
&& location == Span::empty()
|
||||||
|
&& self.location == Span::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn put_doc(&mut self, new_doc: String) {
|
pub fn put_doc(&mut self, new_doc: String) {
|
||||||
self.doc = Some(new_doc);
|
self.doc = Some(new_doc);
|
||||||
}
|
}
|
||||||
|
@ -1422,25 +1436,59 @@ impl Default for Bls12_381Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type UntypedAssignmentKind = AssignmentKind<bool>;
|
||||||
|
pub type TypedAssignmentKind = AssignmentKind<()>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum AssignmentKind {
|
pub enum AssignmentKind<T> {
|
||||||
Let,
|
Let { backpassing: T },
|
||||||
Expect,
|
Expect { backpassing: T },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssignmentKind {
|
impl From<UntypedAssignmentKind> for TypedAssignmentKind {
|
||||||
|
fn from(kind: UntypedAssignmentKind) -> TypedAssignmentKind {
|
||||||
|
match kind {
|
||||||
|
AssignmentKind::Let { .. } => AssignmentKind::Let { backpassing: () },
|
||||||
|
AssignmentKind::Expect { .. } => AssignmentKind::Expect { backpassing: () },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AssignmentKind<T> {
|
||||||
pub fn is_let(&self) -> bool {
|
pub fn is_let(&self) -> bool {
|
||||||
matches!(self, AssignmentKind::Let)
|
matches!(self, AssignmentKind::Let { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_expect(&self) -> bool {
|
pub fn is_expect(&self) -> bool {
|
||||||
matches!(self, AssignmentKind::Expect)
|
matches!(self, AssignmentKind::Expect { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn location_offset(&self) -> usize {
|
pub fn location_offset(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
AssignmentKind::Let => 3,
|
AssignmentKind::Let { .. } => 3,
|
||||||
AssignmentKind::Expect => 6,
|
AssignmentKind::Expect { .. } => 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssignmentKind<bool> {
|
||||||
|
pub fn is_backpassing(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Let { backpassing } | Self::Expect { backpassing } => *backpassing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> AssignmentKind<T> {
|
||||||
|
pub fn let_() -> Self {
|
||||||
|
AssignmentKind::Let {
|
||||||
|
backpassing: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect() -> Self {
|
||||||
|
AssignmentKind::Expect {
|
||||||
|
backpassing: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
self, Annotation, Arg, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference,
|
self, Annotation, Arg, ArgName, BinOp, Bls12_381Point, ByteArrayFormatPreference, CallArg,
|
||||||
CallArg, Curve, DataType, DataTypeKey, DefinitionLocation, IfBranch, Located,
|
Curve, DataType, DataTypeKey, DefinitionLocation, IfBranch, Located, LogicalOpChainKind,
|
||||||
LogicalOpChainKind, ParsedCallArg, Pattern, RecordConstructorArg, RecordUpdateSpread, Span,
|
ParsedCallArg, Pattern, RecordConstructorArg, RecordUpdateSpread, Span, TraceKind,
|
||||||
TraceKind, TypedClause, TypedDataType, TypedRecordUpdateArg, UnOp, UntypedClause,
|
TypedAssignmentKind, TypedClause, TypedDataType, TypedRecordUpdateArg, UnOp,
|
||||||
UntypedRecordUpdateArg,
|
UntypedAssignmentKind, UntypedClause, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
builtins::void,
|
builtins::void,
|
||||||
parser::token::Base,
|
parser::token::Base,
|
||||||
|
@ -106,7 +106,7 @@ pub enum TypedExpr {
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
value: Box<Self>,
|
value: Box<Self>,
|
||||||
pattern: Pattern<PatternConstructor, Rc<Type>>,
|
pattern: Pattern<PatternConstructor, Rc<Type>>,
|
||||||
kind: AssignmentKind,
|
kind: TypedAssignmentKind,
|
||||||
},
|
},
|
||||||
|
|
||||||
Trace {
|
Trace {
|
||||||
|
@ -519,7 +519,7 @@ pub enum UntypedExpr {
|
||||||
location: Span,
|
location: Span,
|
||||||
value: Box<Self>,
|
value: Box<Self>,
|
||||||
pattern: Pattern<(), ()>,
|
pattern: Pattern<(), ()>,
|
||||||
kind: AssignmentKind,
|
kind: UntypedAssignmentKind,
|
||||||
annotation: Option<Annotation>,
|
annotation: Option<Annotation>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1299,4 +1299,29 @@ impl UntypedExpr {
|
||||||
Self::String { .. } | Self::UInt { .. } | Self::ByteArray { .. }
|
Self::String { .. } | Self::UInt { .. } | Self::ByteArray { .. }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lambda(name: String, expressions: Vec<UntypedExpr>, location: Span) -> Self {
|
||||||
|
Self::Fn {
|
||||||
|
location,
|
||||||
|
fn_style: FnStyle::Plain,
|
||||||
|
arguments: vec![Arg {
|
||||||
|
location,
|
||||||
|
doc: None,
|
||||||
|
annotation: None,
|
||||||
|
tipo: (),
|
||||||
|
arg_name: ArgName::Named {
|
||||||
|
label: name.clone(),
|
||||||
|
name,
|
||||||
|
location,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
body: Self::Sequence {
|
||||||
|
location,
|
||||||
|
expressions,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
return_annotation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ use crate::{
|
||||||
CallArg, ClauseGuard, Constant, CurveType, DataType, Definition, Function, IfBranch,
|
CallArg, ClauseGuard, Constant, CurveType, DataType, Definition, Function, IfBranch,
|
||||||
LogicalOpChainKind, ModuleConstant, Pattern, RecordConstructor, RecordConstructorArg,
|
LogicalOpChainKind, ModuleConstant, Pattern, RecordConstructor, RecordConstructorArg,
|
||||||
RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp, UnqualifiedImport,
|
RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp, UnqualifiedImport,
|
||||||
UntypedArg, UntypedArgVia, UntypedClause, UntypedClauseGuard, UntypedDefinition,
|
UntypedArg, UntypedArgVia, UntypedAssignmentKind, UntypedClause, UntypedClauseGuard,
|
||||||
UntypedFunction, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
|
UntypedDefinition, UntypedFunction, UntypedModule, UntypedPattern, UntypedRecordUpdateArg,
|
||||||
CAPTURE_VARIABLE,
|
Use, Validator, CAPTURE_VARIABLE,
|
||||||
},
|
},
|
||||||
docvec,
|
docvec,
|
||||||
expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR},
|
expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR},
|
||||||
|
@ -681,14 +681,16 @@ impl<'comments> Formatter<'comments> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &'a UntypedPattern,
|
pattern: &'a UntypedPattern,
|
||||||
value: &'a UntypedExpr,
|
value: &'a UntypedExpr,
|
||||||
kind: AssignmentKind,
|
kind: UntypedAssignmentKind,
|
||||||
annotation: &'a Option<Annotation>,
|
annotation: &'a Option<Annotation>,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
let keyword = match kind {
|
let keyword = match kind {
|
||||||
AssignmentKind::Let => "let",
|
AssignmentKind::Let { .. } => "let",
|
||||||
AssignmentKind::Expect => "expect",
|
AssignmentKind::Expect { .. } => "expect",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let symbol = if kind.is_backpassing() { "<-" } else { "=" };
|
||||||
|
|
||||||
match pattern {
|
match pattern {
|
||||||
UntypedPattern::Constructor {
|
UntypedPattern::Constructor {
|
||||||
name, module: None, ..
|
name, module: None, ..
|
||||||
|
@ -708,7 +710,8 @@ impl<'comments> Formatter<'comments> {
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(pattern.append(annotation).group())
|
.append(pattern.append(annotation).group())
|
||||||
.append(" =")
|
.append(" ")
|
||||||
|
.append(symbol)
|
||||||
.append(self.case_clause_value(value))
|
.append(self.case_clause_value(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,7 +559,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
&subject_type,
|
&subject_type,
|
||||||
AssignmentProperties {
|
AssignmentProperties {
|
||||||
value_type: subject.tipo(),
|
value_type: subject.tipo(),
|
||||||
kind: AssignmentKind::Let,
|
kind: AssignmentKind::let_(),
|
||||||
remove_unused: false,
|
remove_unused: false,
|
||||||
full_check: false,
|
full_check: false,
|
||||||
msg_func: None,
|
msg_func: None,
|
||||||
|
@ -843,7 +843,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
assert!(
|
assert!(
|
||||||
match &value {
|
match &value {
|
||||||
AirTree::Var { name, .. } if props.kind == AssignmentKind::Let => {
|
AirTree::Var { name, .. } if props.kind.is_let() => {
|
||||||
name != "_"
|
name != "_"
|
||||||
}
|
}
|
||||||
_ => true,
|
_ => true,
|
||||||
|
@ -2812,7 +2812,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
&actual_type,
|
&actual_type,
|
||||||
AssignmentProperties {
|
AssignmentProperties {
|
||||||
value_type: data(),
|
value_type: data(),
|
||||||
kind: AssignmentKind::Expect,
|
kind: AssignmentKind::expect(),
|
||||||
remove_unused: false,
|
remove_unused: false,
|
||||||
full_check: true,
|
full_check: true,
|
||||||
msg_func,
|
msg_func,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
AssignmentKind, BinOp, ClauseGuard, Constant, DataTypeKey, FunctionAccessKey, Pattern,
|
BinOp, ClauseGuard, Constant, DataTypeKey, FunctionAccessKey, Pattern, Span, TraceLevel,
|
||||||
Span, TraceLevel, TypedArg, TypedClause, TypedClauseGuard, TypedDataType, TypedPattern,
|
TypedArg, TypedAssignmentKind, TypedClause, TypedClauseGuard, TypedDataType, TypedPattern,
|
||||||
UnOp,
|
UnOp,
|
||||||
},
|
},
|
||||||
builtins::{bool, data, function, int, list, void},
|
builtins::{bool, data, function, int, list, void},
|
||||||
|
@ -68,7 +68,7 @@ pub enum HoistableFunction {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AssignmentProperties {
|
pub struct AssignmentProperties {
|
||||||
pub value_type: Rc<Type>,
|
pub value_type: Rc<Type>,
|
||||||
pub kind: AssignmentKind,
|
pub kind: TypedAssignmentKind,
|
||||||
pub remove_unused: bool,
|
pub remove_unused: bool,
|
||||||
pub full_check: bool,
|
pub full_check: bool,
|
||||||
pub msg_func: Option<AirMsg>,
|
pub msg_func: Option<AirMsg>,
|
||||||
|
|
|
@ -24,7 +24,9 @@ Test(
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: (),
|
tipo: (),
|
||||||
},
|
},
|
||||||
kind: Expect,
|
kind: Expect {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Var {
|
Var {
|
||||||
|
|
|
@ -29,7 +29,9 @@ Fn(
|
||||||
location: 17..18,
|
location: 17..18,
|
||||||
name: "x",
|
name: "x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
doc: None,
|
doc: None,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use chumsky::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{annotation, error::ParseError, pattern, token::Token},
|
parser::{annotation, error::ParseError, pattern, token::Token},
|
||||||
};
|
};
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
pub fn let_(
|
pub fn let_(
|
||||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
|
@ -12,9 +11,9 @@ pub fn let_(
|
||||||
just(Token::Let)
|
just(Token::Let)
|
||||||
.ignore_then(pattern())
|
.ignore_then(pattern())
|
||||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||||
.then_ignore(just(Token::Equal))
|
.then(choice((just(Token::Equal), just(Token::LArrow))))
|
||||||
.then(r.clone())
|
.then(r.clone())
|
||||||
.validate(move |((pattern, annotation), value), span, emit| {
|
.validate(move |(((pattern, annotation), kind), value), span, emit| {
|
||||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||||
}
|
}
|
||||||
|
@ -23,7 +22,9 @@ pub fn let_(
|
||||||
location: span,
|
location: span,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
pattern,
|
pattern,
|
||||||
kind: ast::AssignmentKind::Let,
|
kind: ast::AssignmentKind::Let {
|
||||||
|
backpassing: kind == Token::LArrow,
|
||||||
|
},
|
||||||
annotation,
|
annotation,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -36,24 +37,27 @@ pub fn expect(
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
pattern()
|
pattern()
|
||||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||||
.then_ignore(just(Token::Equal))
|
.then(choice((just(Token::Equal), just(Token::LArrow))))
|
||||||
.or_not(),
|
.or_not(),
|
||||||
)
|
)
|
||||||
.then(r.clone())
|
.then(r.clone())
|
||||||
.validate(move |(opt_pattern, value), span, emit| {
|
.validate(move |(opt_pattern, value), span, emit| {
|
||||||
let (pattern, annotation) = opt_pattern.unwrap_or_else(|| {
|
let ((pattern, annotation), kind) = opt_pattern.unwrap_or_else(|| {
|
||||||
(
|
(
|
||||||
ast::UntypedPattern::Constructor {
|
(
|
||||||
is_record: false,
|
ast::UntypedPattern::Constructor {
|
||||||
location: span,
|
is_record: false,
|
||||||
name: "True".to_string(),
|
location: span,
|
||||||
arguments: vec![],
|
name: "True".to_string(),
|
||||||
module: None,
|
arguments: vec![],
|
||||||
constructor: (),
|
module: None,
|
||||||
with_spread: false,
|
constructor: (),
|
||||||
tipo: (),
|
with_spread: false,
|
||||||
},
|
tipo: (),
|
||||||
None,
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Token::Equal,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,7 +69,9 @@ pub fn expect(
|
||||||
location: span,
|
location: span,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
pattern,
|
pattern,
|
||||||
kind: ast::AssignmentKind::Expect,
|
kind: ast::AssignmentKind::Expect {
|
||||||
|
backpassing: kind == Token::LArrow,
|
||||||
|
},
|
||||||
annotation,
|
annotation,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
||||||
location: 16..17,
|
location: 16..17,
|
||||||
name: "x",
|
name: "x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
BinOp {
|
BinOp {
|
||||||
|
@ -44,6 +46,8 @@ Assignment {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "b",
|
name: "b",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ Assignment {
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: (),
|
tipo: (),
|
||||||
},
|
},
|
||||||
kind: Expect,
|
kind: Expect {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ Assignment {
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: (),
|
tipo: (),
|
||||||
},
|
},
|
||||||
kind: Expect,
|
kind: Expect {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
||||||
location: 13..14,
|
location: 13..14,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -35,6 +37,8 @@ Assignment {
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: (),
|
tipo: (),
|
||||||
},
|
},
|
||||||
kind: Expect,
|
kind: Expect {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
||||||
location: 14..15,
|
location: 14..15,
|
||||||
name: "b",
|
name: "b",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -29,6 +31,8 @@ Assignment {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
||||||
location: 14..15,
|
location: 14..15,
|
||||||
name: "b",
|
name: "b",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -29,6 +31,8 @@ Assignment {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
||||||
location: 16..17,
|
location: 16..17,
|
||||||
name: "b",
|
name: "b",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Var {
|
Var {
|
||||||
|
@ -33,6 +35,8 @@ Assignment {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ Assignment {
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: (),
|
tipo: (),
|
||||||
},
|
},
|
||||||
kind: Expect,
|
kind: Expect {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@ Sequence {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "x",
|
name: "x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Assignment {
|
Assignment {
|
||||||
|
@ -115,7 +117,9 @@ Sequence {
|
||||||
location: 24..33,
|
location: 24..33,
|
||||||
name: "map_add_x",
|
name: "map_add_x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
|
|
|
@ -18,7 +18,9 @@ Sequence {
|
||||||
location: 8..9,
|
location: 8..9,
|
||||||
name: "i",
|
name: "i",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Assignment {
|
Assignment {
|
||||||
|
@ -34,7 +36,9 @@ Sequence {
|
||||||
location: 28..29,
|
location: 28..29,
|
||||||
name: "j",
|
name: "j",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Assignment {
|
Assignment {
|
||||||
|
@ -54,7 +58,9 @@ Sequence {
|
||||||
location: 48..49,
|
location: 48..49,
|
||||||
name: "k",
|
name: "k",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -32,6 +32,8 @@ Assignment {
|
||||||
location: 4..9,
|
location: 4..9,
|
||||||
name: "thing",
|
name: "thing",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,9 @@ Sequence {
|
||||||
location: 4..9,
|
location: 4..9,
|
||||||
name: "tuple",
|
name: "tuple",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
BinOp {
|
BinOp {
|
||||||
|
|
|
@ -31,7 +31,9 @@ Sequence {
|
||||||
location: 4..5,
|
location: 4..5,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Tuple {
|
Tuple {
|
||||||
|
|
|
@ -89,7 +89,9 @@ When {
|
||||||
location: 55..62,
|
location: 55..62,
|
||||||
name: "amazing",
|
name: "amazing",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Var {
|
Var {
|
||||||
|
|
|
@ -163,6 +163,8 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
just("!=").to(Token::NotEqual),
|
just("!=").to(Token::NotEqual),
|
||||||
just('!').to(Token::Bang),
|
just('!').to(Token::Bang),
|
||||||
just('?').to(Token::Question),
|
just('?').to(Token::Question),
|
||||||
|
just("<-").to(Token::LArrow),
|
||||||
|
just("->").to(Token::RArrow),
|
||||||
choice((
|
choice((
|
||||||
just("<=").to(Token::LessEqual),
|
just("<=").to(Token::LessEqual),
|
||||||
just('<').to(Token::Less),
|
just('<').to(Token::Less),
|
||||||
|
@ -170,7 +172,6 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
just('>').to(Token::Greater),
|
just('>').to(Token::Greater),
|
||||||
)),
|
)),
|
||||||
just('+').to(Token::Plus),
|
just('+').to(Token::Plus),
|
||||||
just("->").to(Token::RArrow),
|
|
||||||
just('-').to(Token::Minus),
|
just('-').to(Token::Minus),
|
||||||
just('*').to(Token::Star),
|
just('*').to(Token::Star),
|
||||||
just('/').to(Token::Slash),
|
just('/').to(Token::Slash),
|
||||||
|
|
|
@ -62,6 +62,7 @@ pub enum Token {
|
||||||
Pipe, // '|>'
|
Pipe, // '|>'
|
||||||
Dot, // '.'
|
Dot, // '.'
|
||||||
RArrow, // '->'
|
RArrow, // '->'
|
||||||
|
LArrow, // '<-'
|
||||||
DotDot, // '..'
|
DotDot, // '..'
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
// Docs/Extra
|
// Docs/Extra
|
||||||
|
@ -152,6 +153,7 @@ impl fmt::Display for Token {
|
||||||
Token::Pipe => "|>",
|
Token::Pipe => "|>",
|
||||||
Token::Dot => ".",
|
Token::Dot => ".",
|
||||||
Token::RArrow => "->",
|
Token::RArrow => "->",
|
||||||
|
Token::LArrow => "<-",
|
||||||
Token::DotDot => "..",
|
Token::DotDot => "..",
|
||||||
Token::EndOfFile => "EOF",
|
Token::EndOfFile => "EOF",
|
||||||
Token::Comment => "//",
|
Token::Comment => "//",
|
||||||
|
|
|
@ -23,7 +23,9 @@ Module {
|
||||||
location: 19..20,
|
location: 19..20,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
UInt {
|
UInt {
|
||||||
|
@ -61,7 +63,9 @@ Module {
|
||||||
location: 56..57,
|
location: 56..57,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
UInt {
|
UInt {
|
||||||
|
@ -110,7 +114,9 @@ Module {
|
||||||
location: 93..94,
|
location: 93..94,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
doc: None,
|
doc: None,
|
||||||
|
@ -155,7 +161,9 @@ Module {
|
||||||
location: 126..127,
|
location: 126..127,
|
||||||
name: "a",
|
name: "a",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
BinOp {
|
BinOp {
|
||||||
|
|
|
@ -28,7 +28,9 @@ Module {
|
||||||
location: 17..18,
|
location: 17..18,
|
||||||
name: "x",
|
name: "x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Var {
|
Var {
|
||||||
|
|
|
@ -26,7 +26,9 @@ Module {
|
||||||
location: 17..18,
|
location: 17..18,
|
||||||
name: "x",
|
name: "x",
|
||||||
},
|
},
|
||||||
kind: Let,
|
kind: Let {
|
||||||
|
backpassing: false,
|
||||||
|
},
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
Var {
|
Var {
|
||||||
|
|
|
@ -1185,6 +1185,204 @@ fn trace_if_false_ok() {
|
||||||
assert!(check(parse(source_code)).is_ok())
|
assert!(check(parse(source_code)).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_basic() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
let i <- and_then(opt_i)
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_expect_simple() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
expect 42 <- and_then(opt_i)
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(j + 42)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_expect_nested() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(Option<a>) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(Some(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
expect Some(i) <- and_then(opt_i)
|
||||||
|
expect Some(j) <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_interleaved_capture() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
let f = and_then(opt_i, _)
|
||||||
|
let i <- f
|
||||||
|
let g = and_then(opt_j, _)
|
||||||
|
let j <- g
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_patterns() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo {
|
||||||
|
foo: Int,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Foo>, opt_j: Option<Foo>) -> Option<Int> {
|
||||||
|
let Foo { foo: i } <- and_then(opt_i)
|
||||||
|
let Foo { foo: j } <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_not_a_function() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
let i <- opt_i
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::NotFn { .. }))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_non_exhaustive_pattern() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
let 42 <- and_then(opt_i)
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::NotExhaustivePatternMatch { .. }))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_unsaturated_fn() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
let i <- and_then
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::IncorrectFieldsArity { .. }))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backpassing_expect_type_mismatch() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
|
||||||
|
when opt is {
|
||||||
|
None -> None
|
||||||
|
Some(a) -> then(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
|
||||||
|
expect Some(i) <- and_then(opt_i)
|
||||||
|
let j <- and_then(opt_j)
|
||||||
|
Some(i + j)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
check(parse(source_code)),
|
||||||
|
Err((_, Error::CouldNotUnify { .. }))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trace_if_false_ko() {
|
fn trace_if_false_ko() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
|
|
@ -8,12 +8,12 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference,
|
self, Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point,
|
||||||
CallArg, ClauseGuard, Constant, Curve, IfBranch, LogicalOpChainKind, Pattern,
|
ByteArrayFormatPreference, CallArg, ClauseGuard, Constant, Curve, IfBranch,
|
||||||
RecordUpdateSpread, Span, TraceKind, TraceLevel, Tracing, TypedArg, TypedCallArg,
|
LogicalOpChainKind, Pattern, RecordUpdateSpread, Span, TraceKind, TraceLevel, Tracing,
|
||||||
TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp,
|
TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern,
|
||||||
UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch, UntypedPattern,
|
TypedRecordUpdateArg, UnOp, UntypedArg, UntypedAssignmentKind, UntypedClause,
|
||||||
UntypedRecordUpdateArg,
|
UntypedClauseGuard, UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
builtins::{
|
builtins::{
|
||||||
bool, byte_array, function, g1_element, g2_element, int, list, string, tuple, void,
|
bool, byte_array, function, g1_element, g2_element, int, list, string, tuple, void,
|
||||||
|
@ -916,7 +916,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
&mut self,
|
&mut self,
|
||||||
untyped_pattern: UntypedPattern,
|
untyped_pattern: UntypedPattern,
|
||||||
untyped_value: UntypedExpr,
|
untyped_value: UntypedExpr,
|
||||||
kind: AssignmentKind,
|
kind: UntypedAssignmentKind,
|
||||||
annotation: &Option<Annotation>,
|
annotation: &Option<Annotation>,
|
||||||
location: Span,
|
location: Span,
|
||||||
) -> Result<TypedExpr, Error> {
|
) -> Result<TypedExpr, Error> {
|
||||||
|
@ -978,12 +978,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
|
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
|
||||||
// error we emit a warning which explains that using `expect` is unnecessary.
|
// error we emit a warning which explains that using `expect` is unnecessary.
|
||||||
match kind {
|
match kind {
|
||||||
AssignmentKind::Let => {
|
AssignmentKind::Let { .. } => {
|
||||||
self.environment
|
self.environment
|
||||||
.check_exhaustiveness(&[&pattern], location, true)?
|
.check_exhaustiveness(&[&pattern], location, true)?
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignmentKind::Expect => {
|
AssignmentKind::Expect { .. } => {
|
||||||
let is_exaustive_pattern = self
|
let is_exaustive_pattern = self
|
||||||
.environment
|
.environment
|
||||||
.check_exhaustiveness(&[&pattern], location, false)
|
.check_exhaustiveness(&[&pattern], location, false)
|
||||||
|
@ -999,12 +999,26 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
},
|
},
|
||||||
pattern_location: untyped_pattern.location(),
|
pattern_location: untyped_pattern.location(),
|
||||||
value_location: untyped_value.location(),
|
value_location: untyped_value.location(),
|
||||||
sample: UntypedExpr::Assignment {
|
sample: match untyped_value {
|
||||||
location: Span::empty(),
|
UntypedExpr::Var { name, .. } if name == ast::BACKPASS_VARIABLE => {
|
||||||
value: Box::new(untyped_value),
|
UntypedExpr::Assignment {
|
||||||
pattern: untyped_pattern,
|
location: Span::empty(),
|
||||||
kind: AssignmentKind::Let,
|
value: Box::new(UntypedExpr::Var {
|
||||||
annotation: None,
|
name: "...".to_string(),
|
||||||
|
location: Span::empty(),
|
||||||
|
}),
|
||||||
|
pattern: untyped_pattern,
|
||||||
|
kind: AssignmentKind::Let { backpassing: true },
|
||||||
|
annotation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => UntypedExpr::Assignment {
|
||||||
|
location: Span::empty(),
|
||||||
|
value: Box::new(untyped_value),
|
||||||
|
pattern: untyped_pattern,
|
||||||
|
kind: AssignmentKind::let_(),
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1014,7 +1028,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
Ok(TypedExpr::Assignment {
|
Ok(TypedExpr::Assignment {
|
||||||
location,
|
location,
|
||||||
tipo: value_typ,
|
tipo: value_typ,
|
||||||
kind,
|
kind: kind.into(),
|
||||||
pattern,
|
pattern,
|
||||||
value: Box::new(typed_value),
|
value: Box::new(typed_value),
|
||||||
})
|
})
|
||||||
|
@ -1707,13 +1721,157 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
PipeTyper::infer(self, expressions)
|
PipeTyper::infer(self, expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn backpass(&mut self, breakpoint: UntypedExpr, continuation: Vec<UntypedExpr>) -> UntypedExpr {
|
||||||
|
let (value, pattern, annotation, kind, assign_location) = match breakpoint {
|
||||||
|
UntypedExpr::Assignment {
|
||||||
|
location,
|
||||||
|
value,
|
||||||
|
pattern,
|
||||||
|
annotation,
|
||||||
|
kind,
|
||||||
|
..
|
||||||
|
} => (value, pattern, annotation, kind, location),
|
||||||
|
_ => unreachable!("backpass misuse: breakpoint isn't an Assignment ?!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let value_location = value.location();
|
||||||
|
|
||||||
|
let call_location = Span {
|
||||||
|
start: value_location.end,
|
||||||
|
end: continuation
|
||||||
|
.last()
|
||||||
|
.map(|expr| expr.location().end)
|
||||||
|
.unwrap_or_else(|| value_location.end),
|
||||||
|
};
|
||||||
|
|
||||||
|
// In case where we have a Pattern that isn't simply a let-binding to a name, we do insert an extra let-binding
|
||||||
|
// in front of the continuation sequence. This is because we do not support patterns in function argument
|
||||||
|
// (which is perhaps something we should support?).
|
||||||
|
let (name, continuation) = match pattern {
|
||||||
|
Pattern::Var { name, .. } | Pattern::Discard { name, .. } if kind.is_let() => {
|
||||||
|
(name.clone(), continuation)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut with_assignment = vec![UntypedExpr::Assignment {
|
||||||
|
location: assign_location,
|
||||||
|
value: UntypedExpr::Var {
|
||||||
|
location: value_location,
|
||||||
|
name: ast::BACKPASS_VARIABLE.to_string(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
pattern,
|
||||||
|
// Erase backpassing while preserving assignment kind.
|
||||||
|
kind: match kind {
|
||||||
|
AssignmentKind::Let { .. } => AssignmentKind::let_(),
|
||||||
|
AssignmentKind::Expect { .. } => AssignmentKind::expect(),
|
||||||
|
},
|
||||||
|
annotation,
|
||||||
|
}];
|
||||||
|
with_assignment.extend(continuation);
|
||||||
|
(ast::BACKPASS_VARIABLE.to_string(), with_assignment)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match *value {
|
||||||
|
UntypedExpr::Call { fun, arguments, .. } => {
|
||||||
|
let mut new_arguments = Vec::new();
|
||||||
|
new_arguments.extend(arguments);
|
||||||
|
new_arguments.push(CallArg {
|
||||||
|
location: call_location,
|
||||||
|
label: None,
|
||||||
|
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||||
|
});
|
||||||
|
|
||||||
|
UntypedExpr::Call {
|
||||||
|
location: call_location,
|
||||||
|
fun,
|
||||||
|
arguments: new_arguments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This typically occurs on function captures. We do not try to assert anything on the
|
||||||
|
// length of the arguments here. We defer that to the rest of the type-checker. The
|
||||||
|
// only thing we have to do is rewrite the AST as-if someone had passed a callback.
|
||||||
|
//
|
||||||
|
// Now, whether this leads to an invalid call usage, that's not *our* immediate
|
||||||
|
// problem.
|
||||||
|
UntypedExpr::Fn {
|
||||||
|
fn_style,
|
||||||
|
ref arguments,
|
||||||
|
ref return_annotation,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let return_annotation = return_annotation.clone();
|
||||||
|
|
||||||
|
let arguments = arguments.iter().skip(1).cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let call = UntypedExpr::Call {
|
||||||
|
location: call_location,
|
||||||
|
fun: value,
|
||||||
|
arguments: vec![CallArg {
|
||||||
|
location: call_location,
|
||||||
|
label: None,
|
||||||
|
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
if arguments.is_empty() {
|
||||||
|
call
|
||||||
|
} else {
|
||||||
|
UntypedExpr::Fn {
|
||||||
|
location: call_location,
|
||||||
|
fn_style,
|
||||||
|
arguments,
|
||||||
|
body: call.into(),
|
||||||
|
return_annotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly to function captures, if we have any other expression we simply call it
|
||||||
|
// with our continuation. If the expression isn't callable? No problem, the
|
||||||
|
// type-checker will catch that eventually in exactly the same way as if the code was
|
||||||
|
// written like that to begin with.
|
||||||
|
_ => UntypedExpr::Call {
|
||||||
|
location: call_location,
|
||||||
|
fun: value,
|
||||||
|
arguments: vec![CallArg {
|
||||||
|
location: call_location,
|
||||||
|
label: None,
|
||||||
|
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_seq(&mut self, location: Span, untyped: Vec<UntypedExpr>) -> Result<TypedExpr, Error> {
|
fn infer_seq(&mut self, location: Span, untyped: Vec<UntypedExpr>) -> Result<TypedExpr, Error> {
|
||||||
|
// Search for backpassing.
|
||||||
|
let mut breakpoint = None;
|
||||||
|
let mut prefix = Vec::with_capacity(untyped.len());
|
||||||
|
let mut suffix = Vec::with_capacity(untyped.len());
|
||||||
|
for expression in untyped.into_iter() {
|
||||||
|
if breakpoint.is_some() {
|
||||||
|
suffix.push(expression);
|
||||||
|
} else {
|
||||||
|
match expression {
|
||||||
|
UntypedExpr::Assignment { kind, .. } if kind.is_backpassing() => {
|
||||||
|
breakpoint = Some(expression);
|
||||||
|
}
|
||||||
|
_ => prefix.push(expression),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(breakpoint) = breakpoint {
|
||||||
|
prefix.push(self.backpass(breakpoint, suffix));
|
||||||
|
return self.infer_seq(location, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
let sequence = self.in_new_scope(|scope| {
|
let sequence = self.in_new_scope(|scope| {
|
||||||
let count = untyped.len();
|
let count = prefix.len();
|
||||||
|
|
||||||
let mut expressions = Vec::with_capacity(count);
|
let mut expressions = Vec::with_capacity(count);
|
||||||
|
|
||||||
for (i, expression) in untyped.into_iter().enumerate() {
|
for (i, expression) in prefix.into_iter().enumerate() {
|
||||||
let no_assignment = assert_no_assignment(&expression);
|
let no_assignment = assert_no_assignment(&expression);
|
||||||
|
|
||||||
let typed_expression = scope.infer(expression)?;
|
let typed_expression = scope.infer(expression)?;
|
||||||
|
@ -1992,7 +2150,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
value: Box::new(subject.clone()),
|
value: Box::new(subject.clone()),
|
||||||
pattern: clauses[0].patterns[0].clone(),
|
pattern: clauses[0].patterns[0].clone(),
|
||||||
kind: AssignmentKind::Let,
|
kind: AssignmentKind::let_(),
|
||||||
annotation: None,
|
annotation: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -2120,7 +2278,7 @@ fn assert_assignment(expr: TypedExpr) -> Result<TypedExpr, Error> {
|
||||||
with_spread: false,
|
with_spread: false,
|
||||||
tipo: void(),
|
tipo: void(),
|
||||||
},
|
},
|
||||||
kind: AssignmentKind::Let,
|
kind: AssignmentKind::let_(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
use std::{ops::Deref, rc::Rc};
|
|
||||||
|
|
||||||
use vec1::Vec1;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ast::{AssignmentKind, CallArg, Pattern, Span, PIPE_VARIABLE},
|
|
||||||
builtins::function,
|
|
||||||
expr::{TypedExpr, UntypedExpr},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{Error, UnifyErrorSituation},
|
error::{Error, UnifyErrorSituation},
|
||||||
expr::ExprTyper,
|
expr::ExprTyper,
|
||||||
Type, ValueConstructor, ValueConstructorVariant,
|
Type, ValueConstructor, ValueConstructorVariant,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
ast::{AssignmentKind, CallArg, Pattern, Span, PIPE_VARIABLE},
|
||||||
|
builtins::function,
|
||||||
|
expr::{TypedExpr, UntypedExpr},
|
||||||
|
};
|
||||||
|
use std::{ops::Deref, rc::Rc};
|
||||||
|
use vec1::Vec1;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct PipeTyper<'a, 'b, 'c> {
|
pub(crate) struct PipeTyper<'a, 'b, 'c> {
|
||||||
|
@ -184,7 +181,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
|
||||||
let assignment = TypedExpr::Assignment {
|
let assignment = TypedExpr::Assignment {
|
||||||
location,
|
location,
|
||||||
tipo: expression.tipo(),
|
tipo: expression.tipo(),
|
||||||
kind: AssignmentKind::Let,
|
kind: AssignmentKind::let_(),
|
||||||
value: Box::new(expression),
|
value: Box::new(expression),
|
||||||
pattern: Pattern::Var {
|
pattern: Pattern::Var {
|
||||||
location,
|
location,
|
||||||
|
|
Loading…
Reference in New Issue