feat: typechecking is working
This commit is contained in:
parent
cabc653167
commit
5244e58c9f
|
@ -5,7 +5,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::{error::ParseError, tipo};
|
use aiken_lang::{error::ParseError, tipo};
|
||||||
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
use miette::{Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(thiserror::Error)]
|
#[derive(thiserror::Error)]
|
||||||
|
@ -27,7 +27,7 @@ pub enum Error {
|
||||||
#[error("a list of errors")]
|
#[error("a list of errors")]
|
||||||
List(Vec<Self>),
|
List(Vec<Self>),
|
||||||
|
|
||||||
#[error("failed to parse")]
|
#[error("parsing")]
|
||||||
Parse {
|
Parse {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
||||||
|
@ -37,10 +37,11 @@ pub enum Error {
|
||||||
error: Box<ParseError>,
|
error: Box<ParseError>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("type checking failed")]
|
#[error("type checking")]
|
||||||
Type {
|
Type {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
src: String,
|
src: String,
|
||||||
|
#[source]
|
||||||
error: tipo::error::Error,
|
error: tipo::error::Error,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -52,6 +53,10 @@ impl Error {
|
||||||
_ => 1,
|
_ => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report(&self) {
|
||||||
|
eprintln!("Error: {:?}", self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Error {
|
impl Debug for Error {
|
||||||
|
@ -73,7 +78,7 @@ impl Debug for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl miette::Diagnostic for Error {
|
impl Diagnostic for Error {
|
||||||
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
match self {
|
match self {
|
||||||
Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")),
|
Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")),
|
||||||
|
@ -99,7 +104,7 @@ impl miette::Diagnostic for Error {
|
||||||
))),
|
))),
|
||||||
Error::List(_) => None,
|
Error::List(_) => None,
|
||||||
Error::Parse { error, .. } => error.kind.help(),
|
Error::Parse { error, .. } => error.kind.help(),
|
||||||
Error::Type { .. } => None,
|
Error::Type { error, .. } => error.help(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,3 +130,62 @@ impl miette::Diagnostic for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, thiserror::Error)]
|
||||||
|
pub enum Warning {
|
||||||
|
#[error("type checking")]
|
||||||
|
Type {
|
||||||
|
path: PathBuf,
|
||||||
|
src: String,
|
||||||
|
#[source]
|
||||||
|
warning: tipo::error::Warning,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for Warning {
|
||||||
|
fn source_code(&self) -> Option<&dyn SourceCode> {
|
||||||
|
match self {
|
||||||
|
Warning::Type { src, .. } => Some(src),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
|
||||||
|
match self {
|
||||||
|
Warning::Type { warning, .. } => warning.labels(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
match self {
|
||||||
|
Warning::Type { .. } => Some(Box::new("aiken::typecheck")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Warning {
|
||||||
|
pub fn from_type_warning(warning: tipo::error::Warning, path: PathBuf, src: String) -> Warning {
|
||||||
|
Warning::Type { path, warning, src }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report(&self) {
|
||||||
|
eprintln!("Warning: {:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Warning {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let miette_handler = MietteHandlerOpts::new()
|
||||||
|
// For better support of terminal themes use the ANSI coloring
|
||||||
|
.rgb_colors(RgbColors::Never)
|
||||||
|
// If ansi support is disabled in the config disable the eye-candy
|
||||||
|
.color(true)
|
||||||
|
.unicode(true)
|
||||||
|
.terminal_links(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Ignore error to prevent format! panics. This can happen if span points at some
|
||||||
|
// inaccessible location, for example by calling `report_error()` with wrong working set.
|
||||||
|
let _ = miette_handler.debug(self, f);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ fn main() -> miette::Result<()> {
|
||||||
let build_result = project.build();
|
let build_result = project.build();
|
||||||
|
|
||||||
for warning in project.warnings {
|
for warning in project.warnings {
|
||||||
eprintln!("Warning: {:?}", warning)
|
warning.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = build_result {
|
if let Err(err) = build_result {
|
||||||
|
@ -65,7 +65,7 @@ fn main() -> miette::Result<()> {
|
||||||
rest => eprintln!("Error: {:?}", rest),
|
rest => eprintln!("Error: {:?}", rest),
|
||||||
}
|
}
|
||||||
|
|
||||||
miette::bail!("failed: {} errors", err.total());
|
// miette::bail!("failed: {} errors", err.total());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,11 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::{
|
use aiken_lang::{ast::ModuleKind, builtins, tipo::TypeInfo, IdGenerator};
|
||||||
ast::ModuleKind,
|
|
||||||
builtins,
|
|
||||||
tipo::{self, TypeInfo},
|
|
||||||
IdGenerator,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
error::Error,
|
error::{Error, Warning},
|
||||||
module::{CheckedModule, ParsedModule, ParsedModules},
|
module::{CheckedModule, ParsedModule, ParsedModules},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,21 +20,6 @@ pub struct Source {
|
||||||
pub kind: ModuleKind,
|
pub kind: ModuleKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Warning {
|
|
||||||
Type {
|
|
||||||
path: PathBuf,
|
|
||||||
src: String,
|
|
||||||
warning: tipo::error::Warning,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Warning {
|
|
||||||
pub fn from_type_warning(warning: tipo::error::Warning, path: PathBuf, src: String) -> Warning {
|
|
||||||
Warning::Type { path, warning, src }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
config: Config,
|
config: Config,
|
||||||
defined_modules: HashMap<String, PathBuf>,
|
defined_modules: HashMap<String, PathBuf>,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{fmt, ops::Range, sync::Arc};
|
||||||
use internment::Intern;
|
use internment::Intern;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builtins,
|
builtins::{self, bool},
|
||||||
expr::{TypedExpr, UntypedExpr},
|
expr::{TypedExpr, UntypedExpr},
|
||||||
tipo::{fields::FieldMap, PatternConstructor, Type, TypeInfo, ValueConstructor},
|
tipo::{fields::FieldMap, PatternConstructor, Type, TypeInfo, ValueConstructor},
|
||||||
};
|
};
|
||||||
|
@ -122,6 +122,12 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct DefinitionLocation<'module> {
|
||||||
|
pub module: Option<&'module str>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
pub type TypedConstant = Constant<Arc<Type>, String>;
|
pub type TypedConstant = Constant<Arc<Type>, String>;
|
||||||
pub type UntypedConstant = Constant<(), ()>;
|
pub type UntypedConstant = Constant<(), ()>;
|
||||||
|
|
||||||
|
@ -200,6 +206,8 @@ impl<A, B> Constant<A, B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type TypedCallArg = CallArg<TypedExpr>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CallArg<A> {
|
pub struct CallArg<A> {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
@ -453,11 +461,6 @@ pub enum Pattern<Constructor, Type> {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Float {
|
|
||||||
location: Span,
|
|
||||||
value: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
String {
|
String {
|
||||||
location: Span,
|
location: Span,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -509,11 +512,34 @@ pub enum Pattern<Constructor, Type> {
|
||||||
with_spread: bool,
|
with_spread: bool,
|
||||||
tipo: Type,
|
tipo: Type,
|
||||||
},
|
},
|
||||||
|
// Tuple {
|
||||||
|
// location: Span,
|
||||||
|
// elems: Vec<Self>,
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
|
||||||
Tuple {
|
impl<A, B> Pattern<A, B> {
|
||||||
location: Span,
|
pub fn location(&self) -> Span {
|
||||||
elems: Vec<Self>,
|
match self {
|
||||||
},
|
Pattern::Assign { pattern, .. } => pattern.location(),
|
||||||
|
Pattern::Int { location, .. }
|
||||||
|
| Pattern::Var { location, .. }
|
||||||
|
| Pattern::VarUsage { location, .. }
|
||||||
|
| Pattern::List { location, .. }
|
||||||
|
| Pattern::Discard { location, .. }
|
||||||
|
| Pattern::String { location, .. }
|
||||||
|
// | Pattern::Tuple { location, .. }
|
||||||
|
// | Pattern::Concatenate { location, .. }
|
||||||
|
| Pattern::Constructor { location, .. } => *location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the pattern is [`Discard`].
|
||||||
|
///
|
||||||
|
/// [`Discard`]: Pattern::Discard
|
||||||
|
pub fn is_discard(&self) -> bool {
|
||||||
|
matches!(self, Self::Discard { .. })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -528,7 +554,6 @@ pub type UntypedMultiPattern = MultiPattern<(), ()>;
|
||||||
pub type TypedMultiPattern = MultiPattern<PatternConstructor, Arc<Type>>;
|
pub type TypedMultiPattern = MultiPattern<PatternConstructor, Arc<Type>>;
|
||||||
|
|
||||||
pub type TypedClause = Clause<TypedExpr, PatternConstructor, Arc<Type>, String>;
|
pub type TypedClause = Clause<TypedExpr, PatternConstructor, Arc<Type>, String>;
|
||||||
|
|
||||||
pub type UntypedClause = Clause<UntypedExpr, (), (), ()>;
|
pub type UntypedClause = Clause<UntypedExpr, (), (), ()>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -540,6 +565,23 @@ pub struct Clause<Expr, PatternConstructor, Type, RecordTag> {
|
||||||
pub then: Expr,
|
pub then: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TypedClause {
|
||||||
|
pub fn location(&self) -> Span {
|
||||||
|
Span {
|
||||||
|
src: SrcId::empty(),
|
||||||
|
start: self
|
||||||
|
.pattern
|
||||||
|
.get(0)
|
||||||
|
.map(|p| p.location().start)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
end: self.then.location().end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type UntypedClauseGuard = ClauseGuard<(), ()>;
|
||||||
|
pub type TypedClauseGuard = ClauseGuard<Arc<Type>, String>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ClauseGuard<Type, RecordTag> {
|
pub enum ClauseGuard<Type, RecordTag> {
|
||||||
Equals {
|
Equals {
|
||||||
|
@ -596,16 +638,52 @@ pub enum ClauseGuard<Type, RecordTag> {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
TupleIndex {
|
// TupleIndex {
|
||||||
location: Span,
|
// location: Span,
|
||||||
index: u64,
|
// index: u64,
|
||||||
tipo: Type,
|
// tipo: Type,
|
||||||
tuple: Box<Self>,
|
// tuple: Box<Self>,
|
||||||
},
|
// },
|
||||||
|
|
||||||
Constant(Constant<Type, RecordTag>),
|
Constant(Constant<Type, RecordTag>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A, B> ClauseGuard<A, B> {
|
||||||
|
pub fn location(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
ClauseGuard::Constant(constant) => constant.location(),
|
||||||
|
ClauseGuard::Or { location, .. }
|
||||||
|
| ClauseGuard::And { location, .. }
|
||||||
|
| ClauseGuard::Var { location, .. }
|
||||||
|
// | ClauseGuard::TupleIndex { location, .. }
|
||||||
|
| ClauseGuard::Equals { location, .. }
|
||||||
|
| ClauseGuard::NotEquals { location, .. }
|
||||||
|
| ClauseGuard::GtInt { location, .. }
|
||||||
|
| ClauseGuard::GtEqInt { location, .. }
|
||||||
|
| ClauseGuard::LtInt { location, .. }
|
||||||
|
| ClauseGuard::LtEqInt { location, .. } => *location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedClauseGuard {
|
||||||
|
pub fn tipo(&self) -> Arc<Type> {
|
||||||
|
match self {
|
||||||
|
ClauseGuard::Var { tipo, .. } => tipo.clone(),
|
||||||
|
// ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
|
||||||
|
ClauseGuard::Constant(constant) => constant.tipo(),
|
||||||
|
|
||||||
|
ClauseGuard::Or { .. }
|
||||||
|
| ClauseGuard::And { .. }
|
||||||
|
| ClauseGuard::Equals { .. }
|
||||||
|
| ClauseGuard::NotEquals { .. }
|
||||||
|
| ClauseGuard::GtInt { .. }
|
||||||
|
| ClauseGuard::GtEqInt { .. }
|
||||||
|
| ClauseGuard::LtInt { .. }
|
||||||
|
| ClauseGuard::LtEqInt { .. } => bool(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type TypedIfBranch = IfBranch<TypedExpr>;
|
pub type TypedIfBranch = IfBranch<TypedExpr>;
|
||||||
pub type UntypedIfBranch = IfBranch<UntypedExpr>;
|
pub type UntypedIfBranch = IfBranch<UntypedExpr>;
|
||||||
|
|
||||||
|
@ -616,7 +694,7 @@ pub struct IfBranch<Expr> {
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TypedRecordUpdateArg {
|
pub struct TypedRecordUpdateArg {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
|
|
|
@ -4,14 +4,14 @@ use vec1::Vec1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, IfBranch, Pattern,
|
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, DefinitionLocation, IfBranch,
|
||||||
RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg,
|
Pattern, RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
builtins::{bool, nil},
|
builtins::{bool, nil},
|
||||||
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypedExpr {
|
pub enum TypedExpr {
|
||||||
Int {
|
Int {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -130,19 +130,18 @@ pub enum TypedExpr {
|
||||||
constructor: ModuleValueConstructor,
|
constructor: ModuleValueConstructor,
|
||||||
},
|
},
|
||||||
|
|
||||||
Tuple {
|
// Tuple {
|
||||||
location: Span,
|
// location: Span,
|
||||||
tipo: Arc<Type>,
|
// tipo: Arc<Type>,
|
||||||
elems: Vec<Self>,
|
// elems: Vec<Self>,
|
||||||
},
|
// },
|
||||||
|
|
||||||
TupleIndex {
|
|
||||||
location: Span,
|
|
||||||
tipo: Arc<Type>,
|
|
||||||
index: u64,
|
|
||||||
tuple: Box<Self>,
|
|
||||||
},
|
|
||||||
|
|
||||||
|
// TupleIndex {
|
||||||
|
// location: Span,
|
||||||
|
// tipo: Arc<Type>,
|
||||||
|
// index: u64,
|
||||||
|
// tuple: Box<Self>,
|
||||||
|
// },
|
||||||
Todo {
|
Todo {
|
||||||
location: Span,
|
location: Span,
|
||||||
label: Option<String>,
|
label: Option<String>,
|
||||||
|
@ -176,10 +175,10 @@ impl TypedExpr {
|
||||||
| Self::Call { tipo, .. }
|
| Self::Call { tipo, .. }
|
||||||
| Self::If { tipo, .. }
|
| Self::If { tipo, .. }
|
||||||
| Self::BinOp { tipo, .. }
|
| Self::BinOp { tipo, .. }
|
||||||
| Self::Tuple { tipo, .. }
|
// | Self::Tuple { tipo, .. }
|
||||||
| Self::String { tipo, .. }
|
| Self::String { tipo, .. }
|
||||||
| Self::ByteArray { tipo, .. }
|
| Self::ByteArray { tipo, .. }
|
||||||
| Self::TupleIndex { tipo, .. }
|
// | Self::TupleIndex { tipo, .. }
|
||||||
| Self::Assignment { tipo, .. }
|
| Self::Assignment { tipo, .. }
|
||||||
| Self::ModuleSelect { tipo, .. }
|
| Self::ModuleSelect { tipo, .. }
|
||||||
| Self::RecordAccess { tipo, .. }
|
| Self::RecordAccess { tipo, .. }
|
||||||
|
@ -190,6 +189,62 @@ impl TypedExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_literal(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Int { .. }
|
||||||
|
| Self::List { .. }
|
||||||
|
// | Self::Tuple { .. }
|
||||||
|
| Self::String { .. }
|
||||||
|
| Self::ByteArray { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the typed expr is [`Assignment`].
|
||||||
|
pub fn is_assignment(&self) -> bool {
|
||||||
|
matches!(self, Self::Assignment { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
|
||||||
|
match self {
|
||||||
|
TypedExpr::Fn { .. }
|
||||||
|
| TypedExpr::Int { .. }
|
||||||
|
| TypedExpr::Try { .. }
|
||||||
|
| TypedExpr::List { .. }
|
||||||
|
| TypedExpr::Call { .. }
|
||||||
|
| TypedExpr::When { .. }
|
||||||
|
| TypedExpr::Todo { .. }
|
||||||
|
| TypedExpr::BinOp { .. }
|
||||||
|
// | TypedExpr::Tuple { .. }
|
||||||
|
| TypedExpr::Negate { .. }
|
||||||
|
| TypedExpr::String { .. }
|
||||||
|
| TypedExpr::Sequence { .. }
|
||||||
|
| TypedExpr::Pipeline { .. }
|
||||||
|
| TypedExpr::ByteArray { .. }
|
||||||
|
| TypedExpr::Assignment { .. }
|
||||||
|
// | TypedExpr::TupleIndex { .. }
|
||||||
|
| TypedExpr::RecordAccess { .. } => None,
|
||||||
|
| TypedExpr::If { .. } => None,
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
// TODO: definition
|
||||||
|
TypedExpr::RecordUpdate { .. } => None,
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
TypedExpr::ModuleSelect {
|
||||||
|
module_name,
|
||||||
|
constructor,
|
||||||
|
..
|
||||||
|
} => Some(DefinitionLocation {
|
||||||
|
module: Some(module_name.as_str()),
|
||||||
|
span: constructor.location(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
TypedExpr::Var { constructor, .. } => Some(constructor.definition_location()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn type_defining_location(&self) -> Span {
|
pub fn type_defining_location(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::Fn { location, .. }
|
Self::Fn { location, .. }
|
||||||
|
@ -201,22 +256,20 @@ impl TypedExpr {
|
||||||
| Self::Call { location, .. }
|
| Self::Call { location, .. }
|
||||||
| Self::List { location, .. }
|
| Self::List { location, .. }
|
||||||
| Self::BinOp { location, .. }
|
| Self::BinOp { location, .. }
|
||||||
| Self::Tuple { location, .. }
|
// | Self::Tuple { location, .. }
|
||||||
| Self::String { location, .. }
|
| Self::String { location, .. }
|
||||||
| Self::Negate { location, .. }
|
| Self::Negate { location, .. }
|
||||||
| Self::Pipeline { location, .. }
|
| Self::Pipeline { location, .. }
|
||||||
| Self::ByteArray { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
| Self::TupleIndex { location, .. }
|
// | Self::TupleIndex { location, .. }
|
||||||
| Self::ModuleSelect { location, .. }
|
| Self::ModuleSelect { location, .. }
|
||||||
| Self::RecordAccess { location, .. }
|
| Self::RecordAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. } => *location,
|
| Self::RecordUpdate { location, .. } => *location,
|
||||||
|
|
||||||
Self::If {
|
Self::If {
|
||||||
location,
|
|
||||||
branches,
|
branches,
|
||||||
final_else,
|
..
|
||||||
tipo,
|
|
||||||
} => branches.first().body.type_defining_location(),
|
} => branches.first().body.type_defining_location(),
|
||||||
|
|
||||||
Self::Sequence {
|
Self::Sequence {
|
||||||
|
@ -242,14 +295,14 @@ impl TypedExpr {
|
||||||
| Self::If { location, .. }
|
| Self::If { location, .. }
|
||||||
| Self::List { location, .. }
|
| Self::List { location, .. }
|
||||||
| Self::BinOp { location, .. }
|
| Self::BinOp { location, .. }
|
||||||
| Self::Tuple { location, .. }
|
// | Self::Tuple { location, .. }
|
||||||
| Self::String { location, .. }
|
| Self::String { location, .. }
|
||||||
| Self::Negate { location, .. }
|
| Self::Negate { location, .. }
|
||||||
| Self::Sequence { location, .. }
|
| Self::Sequence { location, .. }
|
||||||
| Self::Pipeline { location, .. }
|
| Self::Pipeline { location, .. }
|
||||||
| Self::ByteArray { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
| Self::TupleIndex { location, .. }
|
// | Self::TupleIndex { location, .. }
|
||||||
| Self::ModuleSelect { location, .. }
|
| Self::ModuleSelect { location, .. }
|
||||||
| Self::RecordAccess { location, .. }
|
| Self::RecordAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. } => *location,
|
| Self::RecordUpdate { location, .. } => *location,
|
||||||
|
@ -349,17 +402,15 @@ pub enum UntypedExpr {
|
||||||
container: Box<Self>,
|
container: Box<Self>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Tuple {
|
// Tuple {
|
||||||
location: Span,
|
// location: Span,
|
||||||
elems: Vec<Self>,
|
// elems: Vec<Self>,
|
||||||
},
|
// },
|
||||||
|
// TupleIndex {
|
||||||
TupleIndex {
|
// location: Span,
|
||||||
location: Span,
|
// index: u64,
|
||||||
index: u64,
|
// tuple: Box<Self>,
|
||||||
tuple: Box<Self>,
|
// },
|
||||||
},
|
|
||||||
|
|
||||||
Todo {
|
Todo {
|
||||||
kind: TodoKind,
|
kind: TodoKind,
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -442,10 +493,10 @@ impl UntypedExpr {
|
||||||
| Self::List { location, .. }
|
| Self::List { location, .. }
|
||||||
| Self::ByteArray { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::BinOp { location, .. }
|
| Self::BinOp { location, .. }
|
||||||
| Self::Tuple { location, .. }
|
// | Self::Tuple { location, .. }
|
||||||
| Self::String { location, .. }
|
| Self::String { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
| Self::TupleIndex { location, .. }
|
// | Self::TupleIndex { location, .. }
|
||||||
| Self::FieldAccess { location, .. }
|
| Self::FieldAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. }
|
| Self::RecordUpdate { location, .. }
|
||||||
| Self::Negate { location, .. }
|
| Self::Negate { location, .. }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc};
|
use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, ModuleKind, Span, TypedConstant},
|
ast::{Constant, DefinitionLocation, ModuleKind, Span, TypedConstant},
|
||||||
tipo::fields::FieldMap,
|
tipo::fields::FieldMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ mod expr;
|
||||||
pub mod fields;
|
pub mod fields;
|
||||||
mod hydrator;
|
mod hydrator;
|
||||||
mod infer;
|
mod infer;
|
||||||
|
mod pattern;
|
||||||
|
mod pipe;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
@ -125,7 +127,7 @@ impl Type {
|
||||||
pub fn get_app_args(
|
pub fn get_app_args(
|
||||||
&self,
|
&self,
|
||||||
public: bool,
|
public: bool,
|
||||||
module: &String,
|
module: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
environment: &mut Environment<'_>,
|
environment: &mut Environment<'_>,
|
||||||
|
@ -294,6 +296,30 @@ impl ValueConstructor {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_local_variable(&self) -> bool {
|
||||||
|
self.variant.is_local_variable()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn definition_location(&self) -> DefinitionLocation<'_> {
|
||||||
|
match &self.variant {
|
||||||
|
ValueConstructorVariant::Record {
|
||||||
|
module, location, ..
|
||||||
|
}
|
||||||
|
| ValueConstructorVariant::ModuleConstant {
|
||||||
|
location, module, ..
|
||||||
|
} => DefinitionLocation {
|
||||||
|
module: Some(module.as_str()),
|
||||||
|
span: *location,
|
||||||
|
},
|
||||||
|
|
||||||
|
ValueConstructorVariant::ModuleFn { location, .. }
|
||||||
|
| ValueConstructorVariant::LocalVariable { location } => DefinitionLocation {
|
||||||
|
module: None,
|
||||||
|
span: *location,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -329,6 +355,54 @@ pub enum ValueConstructorVariant {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueConstructorVariant {
|
impl ValueConstructorVariant {
|
||||||
|
fn to_module_value_constructor(
|
||||||
|
&self,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
module_name: &str,
|
||||||
|
function_name: &str,
|
||||||
|
) -> ModuleValueConstructor {
|
||||||
|
match self {
|
||||||
|
Self::Record {
|
||||||
|
name,
|
||||||
|
arity,
|
||||||
|
field_map,
|
||||||
|
location,
|
||||||
|
..
|
||||||
|
} => ModuleValueConstructor::Record {
|
||||||
|
name: name.clone(),
|
||||||
|
field_map: field_map.clone(),
|
||||||
|
arity: *arity,
|
||||||
|
tipo,
|
||||||
|
location: *location,
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: remove this clone with an rc clone
|
||||||
|
Self::ModuleConstant {
|
||||||
|
literal, location, ..
|
||||||
|
} => ModuleValueConstructor::Constant {
|
||||||
|
literal: literal.clone(),
|
||||||
|
location: *location,
|
||||||
|
},
|
||||||
|
|
||||||
|
Self::LocalVariable { location, .. } => ModuleValueConstructor::Fn {
|
||||||
|
name: function_name.to_string(),
|
||||||
|
module: module_name.to_string(),
|
||||||
|
location: *location,
|
||||||
|
},
|
||||||
|
|
||||||
|
Self::ModuleFn {
|
||||||
|
name,
|
||||||
|
module,
|
||||||
|
location,
|
||||||
|
..
|
||||||
|
} => ModuleValueConstructor::Fn {
|
||||||
|
name: name.clone(),
|
||||||
|
module: module.clone(),
|
||||||
|
location: *location,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn location(&self) -> Span {
|
pub fn location(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ValueConstructorVariant::LocalVariable { location }
|
ValueConstructorVariant::LocalVariable { location }
|
||||||
|
@ -337,6 +411,11 @@ impl ValueConstructorVariant {
|
||||||
| ValueConstructorVariant::Record { location, .. } => *location,
|
| ValueConstructorVariant::Record { location, .. } => *location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the variant is [`LocalVariable`].
|
||||||
|
pub fn is_local_variable(&self) -> bool {
|
||||||
|
matches!(self, Self::LocalVariable { .. })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -374,7 +453,7 @@ pub struct RecordAccessor {
|
||||||
pub tipo: Arc<Type>,
|
pub tipo: Arc<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum PatternConstructor {
|
pub enum PatternConstructor {
|
||||||
Record {
|
Record {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -382,7 +461,7 @@ pub enum PatternConstructor {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ModuleValueConstructor {
|
pub enum ModuleValueConstructor {
|
||||||
Record {
|
Record {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -394,6 +473,20 @@ pub enum ModuleValueConstructor {
|
||||||
|
|
||||||
Fn {
|
Fn {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
/// The name of the module and the function
|
||||||
|
/// Typically this will be the module that this constructor belongs to
|
||||||
|
/// and the name that was used for the function. However it could also
|
||||||
|
/// point to some other module and function when this is an `external fn`.
|
||||||
|
///
|
||||||
|
/// This function has module "themodule" and name "wibble"
|
||||||
|
/// pub fn wibble() { Nil }
|
||||||
|
///
|
||||||
|
/// This function has module "other" and name "whoop"
|
||||||
|
/// pub external fn wibble() -> Nil =
|
||||||
|
/// "other" "whoop"
|
||||||
|
///
|
||||||
|
module: String,
|
||||||
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Constant {
|
Constant {
|
||||||
|
@ -401,3 +494,13 @@ pub enum ModuleValueConstructor {
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModuleValueConstructor {
|
||||||
|
pub fn location(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
ModuleValueConstructor::Fn { location, .. }
|
||||||
|
| ModuleValueConstructor::Record { location, .. }
|
||||||
|
| ModuleValueConstructor::Constant { location, .. } => *location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, ArgName, CallArg, Definition, RecordConstructor, RecordConstructorArg, Span,
|
Annotation, ArgName, CallArg, Definition, Pattern, RecordConstructor, RecordConstructorArg,
|
||||||
TypedDefinition, UnqualifiedImport, UntypedDefinition, PIPE_VARIABLE,
|
Span, TypedDefinition, UnqualifiedImport, UntypedDefinition, PIPE_VARIABLE,
|
||||||
},
|
},
|
||||||
builtins::{function, generic_var, unbound_var},
|
builtins::{function, generic_var, unbound_var},
|
||||||
tipo::fields::FieldMap,
|
tipo::fields::FieldMap,
|
||||||
|
@ -17,8 +19,8 @@ use crate::{
|
||||||
use super::{
|
use super::{
|
||||||
error::{Error, Warning},
|
error::{Error, Warning},
|
||||||
hydrator::Hydrator,
|
hydrator::Hydrator,
|
||||||
AccessorsMap, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar, ValueConstructor,
|
AccessorsMap, PatternConstructor, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar,
|
||||||
ValueConstructorVariant,
|
ValueConstructor, ValueConstructorVariant,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1213,6 +1215,114 @@ impl<'a> Environment<'a> {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks that the given patterns are exhaustive for given type.
|
||||||
|
/// Currently only performs exhaustiveness checking for custom types,
|
||||||
|
/// only at the top level (without recursing into constructor arguments).
|
||||||
|
pub fn check_exhaustiveness(
|
||||||
|
&mut self,
|
||||||
|
patterns: Vec<Pattern<PatternConstructor, Arc<Type>>>,
|
||||||
|
value_typ: Arc<Type>,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<(), Vec<String>> {
|
||||||
|
match &*value_typ {
|
||||||
|
Type::App {
|
||||||
|
name: type_name,
|
||||||
|
module,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let m = if module.is_empty() || module == self.current_module {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(module.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(constructors) = self.get_constructors_for_type(&m, type_name, location) {
|
||||||
|
let mut unmatched_constructors: HashSet<String> =
|
||||||
|
constructors.iter().cloned().collect();
|
||||||
|
|
||||||
|
for p in &patterns {
|
||||||
|
// ignore Assign patterns
|
||||||
|
let mut pattern = p;
|
||||||
|
while let Pattern::Assign {
|
||||||
|
pattern: assign_pattern,
|
||||||
|
..
|
||||||
|
} = pattern
|
||||||
|
{
|
||||||
|
pattern = assign_pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
match pattern {
|
||||||
|
// If the pattern is a Discard or Var, all constructors are covered by it
|
||||||
|
Pattern::Discard { .. } => return Ok(()),
|
||||||
|
Pattern::Var { .. } => return Ok(()),
|
||||||
|
// If the pattern is a constructor, remove it from unmatched patterns
|
||||||
|
Pattern::Constructor {
|
||||||
|
constructor: PatternConstructor::Record { name, .. },
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
unmatched_constructors.remove(name);
|
||||||
|
}
|
||||||
|
_ => return Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !unmatched_constructors.is_empty() {
|
||||||
|
return Err(unmatched_constructors.into_iter().sorted().collect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup constructors for type in the current scope.
|
||||||
|
///
|
||||||
|
pub fn get_constructors_for_type(
|
||||||
|
&mut self,
|
||||||
|
full_module_name: &Option<String>,
|
||||||
|
name: &str,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<&Vec<String>, Error> {
|
||||||
|
match full_module_name {
|
||||||
|
None => self
|
||||||
|
.module_types_constructors
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| Error::UnknownType {
|
||||||
|
name: name.to_string(),
|
||||||
|
types: self.module_types.keys().map(|t| t.to_string()).collect(),
|
||||||
|
location,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Some(m) => {
|
||||||
|
let module =
|
||||||
|
self.importable_modules
|
||||||
|
.get(m)
|
||||||
|
.ok_or_else(|| Error::UnknownModule {
|
||||||
|
location,
|
||||||
|
name: name.to_string(),
|
||||||
|
imported_modules: self
|
||||||
|
.importable_modules
|
||||||
|
.keys()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.unused_modules.remove(m);
|
||||||
|
|
||||||
|
module
|
||||||
|
.types_constructors
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| Error::UnknownModuleType {
|
||||||
|
location,
|
||||||
|
name: name.to_string(),
|
||||||
|
module_name: module.name.clone(),
|
||||||
|
type_constructors: module.types.keys().map(|t| t.to_string()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For Keeping track of entity usages and knowing which error to display.
|
/// For Keeping track of entity usages and knowing which error to display.
|
||||||
|
@ -1359,6 +1469,15 @@ pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Result<(),
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn collapse_links(t: Arc<Type>) -> Arc<Type> {
|
||||||
|
if let Type::Var { tipo } = t.deref() {
|
||||||
|
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
|
||||||
|
return tipo.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the fields that have the same label and type across all variants of
|
/// Returns the fields that have the same label and type across all variants of
|
||||||
/// the given type.
|
/// the given type.
|
||||||
fn get_compatible_record_fields<A>(
|
fn get_compatible_record_fields<A>(
|
||||||
|
|
|
@ -69,6 +69,14 @@ pub enum Error {
|
||||||
labels: Vec<String>,
|
labels: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("incorrect number of clause patterns expected {expected} but given {given}")]
|
||||||
|
IncorrectNumClausePatterns {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
expected: usize,
|
||||||
|
given: usize,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("{name} has incorrect type arity expected {expected} but given {given}")]
|
#[error("{name} has incorrect type arity expected {expected} but given {given}")]
|
||||||
IncorrectTypeArity {
|
IncorrectTypeArity {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -77,6 +85,13 @@ pub enum Error {
|
||||||
given: usize,
|
given: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("non-exhaustive pattern match")]
|
||||||
|
NotExhaustivePatternMatch {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
unmatched: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("not a function")]
|
#[error("not a function")]
|
||||||
NotFn {
|
NotFn {
|
||||||
#[label]
|
#[label]
|
||||||
|
@ -107,6 +122,18 @@ pub enum Error {
|
||||||
leaked: Type,
|
leaked: Type,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("record access unknown type")]
|
||||||
|
RecordAccessUnknownType {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("record update invalid constructor")]
|
||||||
|
RecordUpdateInvalidConstructor {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("{name} is a reserved module name")]
|
#[error("{name} is a reserved module name")]
|
||||||
ReservedModuleName { name: String },
|
ReservedModuleName { name: String },
|
||||||
|
|
||||||
|
@ -132,6 +159,7 @@ pub enum Error {
|
||||||
|
|
||||||
#[error("unknown module {name}")]
|
#[error("unknown module {name}")]
|
||||||
UnknownModule {
|
UnknownModule {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
imported_modules: Vec<String>,
|
imported_modules: Vec<String>,
|
||||||
|
@ -164,6 +192,16 @@ pub enum Error {
|
||||||
type_constructors: Vec<String>,
|
type_constructors: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unknown record field {label}")]
|
||||||
|
UnknownRecordField {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
typ: Arc<Type>,
|
||||||
|
label: String,
|
||||||
|
fields: Vec<String>,
|
||||||
|
situation: Option<UnknownRecordFieldSituation>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("unknown type {name}")]
|
#[error("unknown type {name}")]
|
||||||
UnknownType {
|
UnknownType {
|
||||||
#[label]
|
#[label]
|
||||||
|
@ -180,6 +218,19 @@ pub enum Error {
|
||||||
variables: Vec<String>,
|
variables: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unnecessary spread operator")]
|
||||||
|
UnnecessarySpreadOperator {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
arity: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("cannot update a type with multiple constructors")]
|
||||||
|
UpdateMultiConstructorType {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
CouldNotUnify {
|
CouldNotUnify {
|
||||||
#[label]
|
#[label]
|
||||||
|
@ -219,6 +270,20 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
pub fn call_situation(mut self) -> Self {
|
||||||
|
if let Error::UnknownRecordField {
|
||||||
|
ref mut situation, ..
|
||||||
|
} = self
|
||||||
|
{
|
||||||
|
*situation = Some(UnknownRecordFieldSituation::FunctionCall);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn case_clause_mismatch(self) -> Self {
|
||||||
|
self.with_unify_error_situation(UnifyErrorSituation::CaseClauseMismatch)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn flip_unify(self) -> Error {
|
pub fn flip_unify(self) -> Error {
|
||||||
match self {
|
match self {
|
||||||
Error::CouldNotUnify {
|
Error::CouldNotUnify {
|
||||||
|
@ -238,6 +303,22 @@ impl Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inconsistent_try(self, return_value_is_result: bool) -> Self {
|
||||||
|
self.with_unify_error_situation(if return_value_is_result {
|
||||||
|
UnifyErrorSituation::TryErrorMismatch
|
||||||
|
} else {
|
||||||
|
UnifyErrorSituation::TryReturnResult
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn operator_situation(self, binop: BinOp) -> Self {
|
||||||
|
self.with_unify_error_situation(UnifyErrorSituation::Operator(binop))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn return_annotation_mismatch(self) -> Self {
|
||||||
|
self.with_unify_error_situation(UnifyErrorSituation::ReturnAnnotationMismatch)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_unify_error_rigid_names(mut self, new_names: &HashMap<u64, String>) -> Self {
|
pub fn with_unify_error_rigid_names(mut self, new_names: &HashMap<u64, String>) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Error::CouldNotUnify {
|
Error::CouldNotUnify {
|
||||||
|
@ -269,69 +350,89 @@ impl Error {
|
||||||
other => other,
|
other => other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_annotation_mismatch(self) -> Self {
|
|
||||||
self.with_unify_error_situation(UnifyErrorSituation::ReturnAnnotationMismatch)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
|
||||||
pub enum Warning {
|
pub enum Warning {
|
||||||
|
#[error("todo")]
|
||||||
Todo {
|
Todo {
|
||||||
kind: TodoKind,
|
kind: TodoKind,
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
typ: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("implicitly discarded result")]
|
||||||
ImplicitlyDiscardedResult {
|
ImplicitlyDiscardedResult {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused literal")]
|
||||||
UnusedLiteral {
|
UnusedLiteral {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("record update with no fields")]
|
||||||
NoFieldsRecordUpdate {
|
NoFieldsRecordUpdate {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("record update using all fields")]
|
||||||
AllFieldsRecordUpdate {
|
AllFieldsRecordUpdate {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused type {name}")]
|
||||||
UnusedType {
|
UnusedType {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
imported: bool,
|
imported: bool,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused constructor {name}")]
|
||||||
UnusedConstructor {
|
UnusedConstructor {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
imported: bool,
|
imported: bool,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused imported value {name}")]
|
||||||
UnusedImportedValue {
|
UnusedImportedValue {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused imported module {name}")]
|
||||||
UnusedImportedModule {
|
UnusedImportedModule {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused private module constant {name}")]
|
||||||
UnusedPrivateModuleConstant {
|
UnusedPrivateModuleConstant {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused private function {name}")]
|
||||||
UnusedPrivateFunction {
|
UnusedPrivateFunction {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unused variable {name}")]
|
||||||
UnusedVariable {
|
UnusedVariable {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
@ -357,3 +458,9 @@ pub enum UnifyErrorSituation {
|
||||||
/// The final value of a try expression was not a Result.
|
/// The final value of a try expression was not a Result.
|
||||||
TryReturnResult,
|
TryReturnResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum UnknownRecordFieldSituation {
|
||||||
|
/// This unknown record field is being called as a function. i.e. `record.field()`
|
||||||
|
FunctionCall,
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -34,6 +34,12 @@ pub struct ScopeResetData {
|
||||||
rigid_type_names: HashMap<u64, String>,
|
rigid_type_names: HashMap<u64, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Hydrator {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Hydrator {
|
impl Hydrator {
|
||||||
pub fn new() -> Hydrator {
|
pub fn new() -> Hydrator {
|
||||||
Hydrator {
|
Hydrator {
|
||||||
|
|
|
@ -61,7 +61,8 @@ impl UntypedModule {
|
||||||
let mut definitions = Vec::with_capacity(self.definitions.len());
|
let mut definitions = Vec::with_capacity(self.definitions.len());
|
||||||
let mut consts = vec![];
|
let mut consts = vec![];
|
||||||
let mut not_consts = vec![];
|
let mut not_consts = vec![];
|
||||||
for def in self.into_definitions() {
|
|
||||||
|
for def in self.definitions().cloned() {
|
||||||
match def {
|
match def {
|
||||||
Definition::ModuleConstant { .. } => consts.push(def),
|
Definition::ModuleConstant { .. } => consts.push(def),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,573 @@
|
||||||
|
///! Type inference and checking of patterns used in case expressions
|
||||||
|
///! and variables bindings.
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
ops::Deref,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
environment::{assert_no_labeled_arguments, EntityKind, Environment},
|
||||||
|
error::Error,
|
||||||
|
hydrator::Hydrator,
|
||||||
|
PatternConstructor, Type, ValueConstructor, ValueConstructorVariant,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
ast::{CallArg, Pattern, Span, SrcId, TypedPattern, UntypedMultiPattern, UntypedPattern},
|
||||||
|
builtins::{int, list, string},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PatternTyper<'a, 'b> {
|
||||||
|
environment: &'a mut Environment<'b>,
|
||||||
|
hydrator: &'a Hydrator,
|
||||||
|
mode: PatternMode,
|
||||||
|
initial_pattern_vars: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PatternMode {
|
||||||
|
Initial,
|
||||||
|
Alternative(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> PatternTyper<'a, 'b> {
|
||||||
|
pub fn new(environment: &'a mut Environment<'b>, hydrator: &'a Hydrator) -> Self {
|
||||||
|
Self {
|
||||||
|
environment,
|
||||||
|
hydrator,
|
||||||
|
mode: PatternMode::Initial,
|
||||||
|
initial_pattern_vars: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_variable(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
typ: Arc<Type>,
|
||||||
|
location: Span,
|
||||||
|
err_location: Span,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match &mut self.mode {
|
||||||
|
PatternMode::Initial => {
|
||||||
|
// Register usage for the unused variable detection
|
||||||
|
self.environment
|
||||||
|
.init_usage(name.to_string(), EntityKind::Variable, location);
|
||||||
|
|
||||||
|
// Ensure there are no duplicate variable names in the pattern
|
||||||
|
if self.initial_pattern_vars.contains(name) {
|
||||||
|
return Err(Error::DuplicateVarInPattern {
|
||||||
|
name: name.to_string(),
|
||||||
|
location: err_location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Record that this variable originated in this pattern so any
|
||||||
|
// following alternative patterns can be checked to ensure they
|
||||||
|
// have the same variables.
|
||||||
|
self.initial_pattern_vars.insert(name.to_string());
|
||||||
|
|
||||||
|
// And now insert the variable for use in the code that comes
|
||||||
|
// after the pattern.
|
||||||
|
self.environment.insert_variable(
|
||||||
|
name.to_string(),
|
||||||
|
ValueConstructorVariant::LocalVariable { location },
|
||||||
|
typ,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternMode::Alternative(assigned) => {
|
||||||
|
match self.environment.scope.get(name) {
|
||||||
|
// This variable was defined in the Initial multi-pattern
|
||||||
|
Some(initial) if self.initial_pattern_vars.contains(name) => {
|
||||||
|
assigned.push(name.to_string());
|
||||||
|
let initial_typ = initial.tipo.clone();
|
||||||
|
self.environment.unify(initial_typ, typ, err_location)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This variable was not defined in the Initial multi-pattern
|
||||||
|
_ => Err(Error::ExtraVarInAlternativePattern {
|
||||||
|
name: name.to_string(),
|
||||||
|
location: err_location,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infer_alternative_multi_pattern(
|
||||||
|
&mut self,
|
||||||
|
multi_pattern: UntypedMultiPattern,
|
||||||
|
subjects: &[Arc<Type>],
|
||||||
|
location: &Span,
|
||||||
|
) -> Result<Vec<TypedPattern>, Error> {
|
||||||
|
self.mode = PatternMode::Alternative(vec![]);
|
||||||
|
let typed_multi = self.infer_multi_pattern(multi_pattern, subjects, location)?;
|
||||||
|
match &self.mode {
|
||||||
|
PatternMode::Initial => panic!("Pattern mode switched from Alternative to Initial"),
|
||||||
|
PatternMode::Alternative(assigned)
|
||||||
|
if assigned.len() != self.initial_pattern_vars.len() =>
|
||||||
|
{
|
||||||
|
for name in assigned {
|
||||||
|
self.initial_pattern_vars.remove(name);
|
||||||
|
}
|
||||||
|
Err(Error::MissingVarInAlternativePattern {
|
||||||
|
location: *location,
|
||||||
|
// It is safe to use expect here as we checked the length above
|
||||||
|
name: self
|
||||||
|
.initial_pattern_vars
|
||||||
|
.iter()
|
||||||
|
.next()
|
||||||
|
.expect("Getting undefined pattern variable")
|
||||||
|
.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PatternMode::Alternative(_) => Ok(typed_multi),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infer_multi_pattern(
|
||||||
|
&mut self,
|
||||||
|
multi_pattern: UntypedMultiPattern,
|
||||||
|
subjects: &[Arc<Type>],
|
||||||
|
location: &Span,
|
||||||
|
) -> Result<Vec<TypedPattern>, Error> {
|
||||||
|
// If there are N subjects the multi-pattern is expected to be N patterns
|
||||||
|
if subjects.len() != multi_pattern.len() {
|
||||||
|
return Err(Error::IncorrectNumClausePatterns {
|
||||||
|
location: *location,
|
||||||
|
expected: subjects.len(),
|
||||||
|
given: multi_pattern.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unify each pattern in the multi-pattern with the corresponding subject
|
||||||
|
let mut typed_multi = Vec::with_capacity(multi_pattern.len());
|
||||||
|
for (pattern, subject_type) in multi_pattern.into_iter().zip(subjects) {
|
||||||
|
let pattern = self.unify(pattern, subject_type.clone())?;
|
||||||
|
typed_multi.push(pattern);
|
||||||
|
}
|
||||||
|
Ok(typed_multi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn infer_pattern_bit_string(
|
||||||
|
// &mut self,
|
||||||
|
// mut segments: Vec<UntypedPatternBitStringSegment>,
|
||||||
|
// location: Span,
|
||||||
|
// ) -> Result<TypedPattern, Error> {
|
||||||
|
// let last_segment = segments.pop();
|
||||||
|
|
||||||
|
// let mut typed_segments: Vec<_> = segments
|
||||||
|
// .into_iter()
|
||||||
|
// .map(|s| self.infer_pattern_segment(s, false))
|
||||||
|
// .try_collect()?;
|
||||||
|
|
||||||
|
// if let Some(s) = last_segment {
|
||||||
|
// let typed_last_segment = self.infer_pattern_segment(s, true)?;
|
||||||
|
// typed_segments.push(typed_last_segment)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(TypedPattern::BitString {
|
||||||
|
// location,
|
||||||
|
// segments: typed_segments,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn infer_pattern_segment(
|
||||||
|
// &mut self,
|
||||||
|
// segment: UntypedPatternBitStringSegment,
|
||||||
|
// is_last_segment: bool,
|
||||||
|
// ) -> Result<TypedPatternBitStringSegment, Error> {
|
||||||
|
// let UntypedPatternBitStringSegment {
|
||||||
|
// location,
|
||||||
|
// options,
|
||||||
|
// value,
|
||||||
|
// ..
|
||||||
|
// } = segment;
|
||||||
|
|
||||||
|
// let options: Vec<_> = options
|
||||||
|
// .into_iter()
|
||||||
|
// .map(|o| infer_bit_string_segment_option(o, |value, typ| self.unify(value, typ)))
|
||||||
|
// .try_collect()?;
|
||||||
|
|
||||||
|
// let segment_type = bit_string::type_options_for_pattern(&options, !is_last_segment)
|
||||||
|
// .map_err(|error| Error::BitStringSegmentError {
|
||||||
|
// error: error.error,
|
||||||
|
// location: error.location,
|
||||||
|
// })?;
|
||||||
|
|
||||||
|
// let typ = {
|
||||||
|
// match value.deref() {
|
||||||
|
// Pattern::Var { .. } if segment_type == string() => {
|
||||||
|
// Err(Error::BitStringSegmentError {
|
||||||
|
// error: bit_string::ErrorType::VariableUtfSegmentInPattern,
|
||||||
|
// location,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// _ => Ok(segment_type),
|
||||||
|
// }
|
||||||
|
// }?;
|
||||||
|
// let typed_value = self.unify(*value, typ.clone())?;
|
||||||
|
|
||||||
|
// Ok(BitStringSegment {
|
||||||
|
// location,
|
||||||
|
// value: Box::new(typed_value),
|
||||||
|
// options,
|
||||||
|
// type_: typ,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// When we have an assignment or a case expression we unify the pattern with the
|
||||||
|
/// inferred type of the subject in order to determine what variables to insert
|
||||||
|
/// into the environment (or to detect a type error).
|
||||||
|
pub fn unify(
|
||||||
|
&mut self,
|
||||||
|
pattern: UntypedPattern,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
) -> Result<TypedPattern, Error> {
|
||||||
|
match pattern {
|
||||||
|
Pattern::Discard { name, location } => Ok(Pattern::Discard { name, location }),
|
||||||
|
|
||||||
|
Pattern::Var { name, location, .. } => {
|
||||||
|
self.insert_variable(&name, tipo, location, location)?;
|
||||||
|
|
||||||
|
Ok(Pattern::Var { name, location })
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern::VarUsage { name, location, .. } => {
|
||||||
|
let ValueConstructor { tipo, .. } = self
|
||||||
|
.environment
|
||||||
|
.get_variable(&name)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| Error::UnknownVariable {
|
||||||
|
location,
|
||||||
|
name: name.to_string(),
|
||||||
|
variables: self.environment.local_value_names(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.environment.increment_usage(&name);
|
||||||
|
|
||||||
|
let tipo = self
|
||||||
|
.environment
|
||||||
|
.instantiate(tipo, &mut HashMap::new(), self.hydrator);
|
||||||
|
|
||||||
|
self.environment.unify(int(), tipo.clone(), location)?;
|
||||||
|
|
||||||
|
Ok(Pattern::VarUsage {
|
||||||
|
name,
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern::Concatenate {
|
||||||
|
// location,
|
||||||
|
// left_location,
|
||||||
|
// right_location,
|
||||||
|
// left_side_string,
|
||||||
|
// right_side_assignment,
|
||||||
|
// } => {
|
||||||
|
// // The entire concatenate pattern must be a string
|
||||||
|
// self.environment.unify(tipo, string(), location)?;
|
||||||
|
|
||||||
|
// // The right hand side may assign a variable, which is the suffix of the string
|
||||||
|
// if let AssignName::Variable(right) = &right_side_assignment {
|
||||||
|
// self.insert_variable(right.as_ref(), string(), right_location, location)?;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Ok(Pattern::Concatenate {
|
||||||
|
// location,
|
||||||
|
// left_location,
|
||||||
|
// right_location,
|
||||||
|
// left_side_string,
|
||||||
|
// right_side_assignment,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
Pattern::Assign {
|
||||||
|
name,
|
||||||
|
pattern,
|
||||||
|
location,
|
||||||
|
} => {
|
||||||
|
self.insert_variable(&name, tipo.clone(), location, pattern.location())?;
|
||||||
|
|
||||||
|
let pattern = self.unify(*pattern, tipo)?;
|
||||||
|
|
||||||
|
Ok(Pattern::Assign {
|
||||||
|
name,
|
||||||
|
pattern: Box::new(pattern),
|
||||||
|
location,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern::Int { location, value } => {
|
||||||
|
self.environment.unify(tipo, int(), location)?;
|
||||||
|
|
||||||
|
Ok(Pattern::Int { location, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern::String { location, value } => {
|
||||||
|
self.environment.unify(tipo, string(), location)?;
|
||||||
|
|
||||||
|
Ok(Pattern::String { location, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern::List {
|
||||||
|
location,
|
||||||
|
elements,
|
||||||
|
tail,
|
||||||
|
} => match tipo.get_app_args(true, "", "List", 1, self.environment) {
|
||||||
|
Some(args) => {
|
||||||
|
let tipo = args
|
||||||
|
.get(0)
|
||||||
|
.expect("Failed to get type argument of List")
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let elements = elements
|
||||||
|
.into_iter()
|
||||||
|
.map(|element| self.unify(element, tipo.clone()))
|
||||||
|
.try_collect()?;
|
||||||
|
|
||||||
|
let tail = match tail {
|
||||||
|
Some(tail) => Some(Box::new(self.unify(*tail, list(tipo))?)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Pattern::List {
|
||||||
|
location,
|
||||||
|
elements,
|
||||||
|
tail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
None => Err(Error::CouldNotUnify {
|
||||||
|
given: list(self.environment.new_unbound_var()),
|
||||||
|
expected: tipo.clone(),
|
||||||
|
situation: None,
|
||||||
|
location,
|
||||||
|
rigid_type_names: HashMap::new(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() {
|
||||||
|
// Type::Tuple { elems: type_elems } => {
|
||||||
|
// if elems.len() != type_elems.len() {
|
||||||
|
// return Err(Error::IncorrectArity {
|
||||||
|
// labels: vec![],
|
||||||
|
// location,
|
||||||
|
// expected: type_elems.len(),
|
||||||
|
// given: elems.len(),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let elems = elems
|
||||||
|
// .into_iter()
|
||||||
|
// .zip(type_elems)
|
||||||
|
// .map(|(pattern, typ)| self.unify(pattern, typ.clone()))
|
||||||
|
// .try_collect()?;
|
||||||
|
|
||||||
|
// Ok(Pattern::Tuple { elems, location })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Type::Var { .. } => {
|
||||||
|
// let elems_types: Vec<_> = (0..(elems.len()))
|
||||||
|
// .map(|_| self.environment.new_unbound_var())
|
||||||
|
// .collect();
|
||||||
|
// self.environment
|
||||||
|
// .unify(tuple(elems_types.clone()), type_)
|
||||||
|
// .map_err(|e| convert_unify_error(e, location))?;
|
||||||
|
// let elems = elems
|
||||||
|
// .into_iter()
|
||||||
|
// .zip(elems_types)
|
||||||
|
// .map(|(pattern, type_)| self.unify(pattern, type_))
|
||||||
|
// .try_collect()?;
|
||||||
|
// Ok(Pattern::Tuple { elems, location })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _ => {
|
||||||
|
// let elems_types = (0..(elems.len()))
|
||||||
|
// .map(|_| self.environment.new_unbound_var())
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// Err(Error::CouldNotUnify {
|
||||||
|
// given: tuple(elems_types),
|
||||||
|
// expected: type_,
|
||||||
|
// situation: None,
|
||||||
|
// location,
|
||||||
|
// rigid_type_names: hashmap![],
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// Pattern::BitString { location, segments } => {
|
||||||
|
// self.environment
|
||||||
|
// .unify(type_, bit_string())
|
||||||
|
// .map_err(|e| convert_unify_error(e, location))?;
|
||||||
|
// self.infer_pattern_bit_string(segments, location)
|
||||||
|
// }
|
||||||
|
Pattern::Constructor {
|
||||||
|
location,
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
arguments: mut pattern_args,
|
||||||
|
with_spread,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Register the value as seen for detection of unused values
|
||||||
|
self.environment.increment_usage(&name);
|
||||||
|
|
||||||
|
let cons =
|
||||||
|
self.environment
|
||||||
|
.get_value_constructor(module.as_ref(), &name, location)?;
|
||||||
|
|
||||||
|
match cons.field_map() {
|
||||||
|
// The fun has a field map so labelled arguments may be present and need to be reordered.
|
||||||
|
Some(field_map) => {
|
||||||
|
if with_spread {
|
||||||
|
// Using the spread operator when you have already provided variables for all of the
|
||||||
|
// record's fields throws an error
|
||||||
|
if pattern_args.len() == field_map.arity as usize {
|
||||||
|
return Err(Error::UnnecessarySpreadOperator {
|
||||||
|
location: Span {
|
||||||
|
src: SrcId::empty(),
|
||||||
|
start: location.end - 3,
|
||||||
|
end: location.end - 1,
|
||||||
|
},
|
||||||
|
arity: field_map.arity as usize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The location of the spread operator itself
|
||||||
|
let spread_location = Span {
|
||||||
|
src: SrcId::empty(),
|
||||||
|
start: location.end - 3,
|
||||||
|
end: location.end - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert discard variables to match the unspecified fields
|
||||||
|
// In order to support both positional and labelled arguments we have to insert
|
||||||
|
// them after all positional variables and before the labelled ones. This means
|
||||||
|
// we have calculate that index and then insert() the discards. It would be faster
|
||||||
|
// if we could put the discards anywhere which would let us use push().
|
||||||
|
// Potential future optimisation.
|
||||||
|
let index_of_first_labelled_arg = pattern_args
|
||||||
|
.iter()
|
||||||
|
.position(|a| a.label.is_some())
|
||||||
|
.unwrap_or(pattern_args.len());
|
||||||
|
|
||||||
|
while pattern_args.len() < field_map.arity as usize {
|
||||||
|
let new_call_arg = CallArg {
|
||||||
|
value: Pattern::Discard {
|
||||||
|
name: "_".to_string(),
|
||||||
|
location: spread_location,
|
||||||
|
},
|
||||||
|
location: spread_location,
|
||||||
|
label: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
pattern_args.insert(index_of_first_labelled_arg, new_call_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field_map.reorder(&mut pattern_args, location)?
|
||||||
|
}
|
||||||
|
|
||||||
|
// The fun has no field map and so we error if arguments have been labelled
|
||||||
|
None => assert_no_labeled_arguments(&pattern_args)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
let constructor_typ = cons.tipo.clone();
|
||||||
|
let constructor = match cons.variant {
|
||||||
|
ValueConstructorVariant::Record { ref name, .. } => {
|
||||||
|
PatternConstructor::Record {
|
||||||
|
name: name.clone(),
|
||||||
|
field_map: cons.field_map().cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueConstructorVariant::LocalVariable { .. }
|
||||||
|
| ValueConstructorVariant::ModuleConstant { .. }
|
||||||
|
| ValueConstructorVariant::ModuleFn { .. } => {
|
||||||
|
panic!("Unexpected value constructor type for a constructor pattern.",)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let instantiated_constructor_type = self.environment.instantiate(
|
||||||
|
constructor_typ,
|
||||||
|
&mut HashMap::new(),
|
||||||
|
self.hydrator,
|
||||||
|
);
|
||||||
|
match instantiated_constructor_type.deref() {
|
||||||
|
Type::Fn { args, ret } => {
|
||||||
|
if args.len() == pattern_args.len() {
|
||||||
|
let pattern_args = pattern_args
|
||||||
|
.into_iter()
|
||||||
|
.zip(args)
|
||||||
|
.map(|(arg, typ)| {
|
||||||
|
let CallArg {
|
||||||
|
value,
|
||||||
|
location,
|
||||||
|
label,
|
||||||
|
} = arg;
|
||||||
|
|
||||||
|
let value = self.unify(value, typ.clone())?;
|
||||||
|
|
||||||
|
Ok(CallArg {
|
||||||
|
value,
|
||||||
|
location,
|
||||||
|
label,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.try_collect()?;
|
||||||
|
|
||||||
|
self.environment.unify(tipo, ret.clone(), location)?;
|
||||||
|
|
||||||
|
Ok(Pattern::Constructor {
|
||||||
|
location,
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
arguments: pattern_args,
|
||||||
|
constructor,
|
||||||
|
with_spread,
|
||||||
|
tipo: instantiated_constructor_type,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::IncorrectArity {
|
||||||
|
labels: vec![],
|
||||||
|
location,
|
||||||
|
expected: args.len(),
|
||||||
|
given: pattern_args.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::App { .. } => {
|
||||||
|
if pattern_args.is_empty() {
|
||||||
|
self.environment.unify(
|
||||||
|
tipo,
|
||||||
|
instantiated_constructor_type.clone(),
|
||||||
|
location,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Pattern::Constructor {
|
||||||
|
location,
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
arguments: vec![],
|
||||||
|
constructor,
|
||||||
|
with_spread,
|
||||||
|
tipo: instantiated_constructor_type,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::IncorrectArity {
|
||||||
|
labels: vec![],
|
||||||
|
location,
|
||||||
|
expected: 0,
|
||||||
|
given: pattern_args.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => panic!("Unexpected constructor type for a constructor pattern.",),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,313 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use vec1::Vec1;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{AssignmentKind, CallArg, Pattern, Span, SrcId, PIPE_VARIABLE},
|
||||||
|
builtins::function,
|
||||||
|
expr::{TypedExpr, UntypedExpr},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
error::{Error, UnifyErrorSituation},
|
||||||
|
expr::ExprTyper,
|
||||||
|
Type, ValueConstructor, ValueConstructorVariant,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct PipeTyper<'a, 'b, 'c> {
|
||||||
|
size: usize,
|
||||||
|
argument_type: Arc<Type>,
|
||||||
|
argument_location: Span,
|
||||||
|
location: Span,
|
||||||
|
expressions: Vec<TypedExpr>,
|
||||||
|
expr_typer: &'a mut ExprTyper<'b, 'c>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
|
||||||
|
pub fn infer(
|
||||||
|
expr_typer: &'a mut ExprTyper<'b, 'c>,
|
||||||
|
expressions: Vec1<UntypedExpr>,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
let size = expressions.len();
|
||||||
|
|
||||||
|
let end = &expressions[..]
|
||||||
|
.last()
|
||||||
|
// The vec is non-empty, this indexing can never fail
|
||||||
|
.expect("Empty pipeline in typer")
|
||||||
|
.location()
|
||||||
|
.end;
|
||||||
|
|
||||||
|
let mut expressions = expressions.into_iter();
|
||||||
|
|
||||||
|
let first = expr_typer.infer(expressions.next().expect("Empty pipeline in typer"))?;
|
||||||
|
|
||||||
|
let mut typer = Self {
|
||||||
|
size,
|
||||||
|
expr_typer,
|
||||||
|
argument_type: first.tipo(),
|
||||||
|
argument_location: first.location(),
|
||||||
|
location: Span {
|
||||||
|
src: SrcId::empty(),
|
||||||
|
start: first.location().start,
|
||||||
|
end: *end,
|
||||||
|
},
|
||||||
|
expressions: Vec::with_capacity(size),
|
||||||
|
};
|
||||||
|
// No need to update self.argument_* as we set it above
|
||||||
|
typer.push_assignment_no_update(first);
|
||||||
|
|
||||||
|
// Perform the type checking
|
||||||
|
typer.infer_expressions(expressions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_expressions(
|
||||||
|
mut self,
|
||||||
|
expressions: impl IntoIterator<Item = UntypedExpr>,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
let result = self.infer_each_expression(expressions);
|
||||||
|
|
||||||
|
// Clean-up the pipe variables inserted so they cannot be used outside this pipeline
|
||||||
|
let _ = self.expr_typer.environment.scope.remove(PIPE_VARIABLE);
|
||||||
|
|
||||||
|
// Return any errors after clean-up
|
||||||
|
result?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::Pipeline {
|
||||||
|
expressions: self.expressions,
|
||||||
|
location: self.location,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_each_expression(
|
||||||
|
&mut self,
|
||||||
|
expressions: impl IntoIterator<Item = UntypedExpr>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
for (i, call) in expressions.into_iter().enumerate() {
|
||||||
|
let call = match call {
|
||||||
|
// left |> right(..args)
|
||||||
|
UntypedExpr::Call {
|
||||||
|
fun,
|
||||||
|
arguments,
|
||||||
|
location,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let fun = self.expr_typer.infer(*fun)?;
|
||||||
|
|
||||||
|
match fun.tipo().fn_arity() {
|
||||||
|
// Rewrite as right(left, ..args)
|
||||||
|
Some(arity) if arity == arguments.len() + 1 => {
|
||||||
|
self.infer_insert_pipe(fun, arguments, location)?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite as right(..args)(left)
|
||||||
|
_ => self.infer_apply_to_call_pipe(fun, arguments, location)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// right(left)
|
||||||
|
call => self.infer_apply_pipe(call)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if i + 2 == self.size {
|
||||||
|
self.expressions.push(call);
|
||||||
|
} else {
|
||||||
|
self.push_assignment(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a call argument that can be used to refer to the value on the
|
||||||
|
/// left hand side of the pipe
|
||||||
|
fn typed_left_hand_value_variable_call_argument(&self) -> CallArg<TypedExpr> {
|
||||||
|
CallArg {
|
||||||
|
label: None,
|
||||||
|
location: self.argument_location,
|
||||||
|
value: self.typed_left_hand_value_variable(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a call argument that can be used to refer to the value on the
|
||||||
|
/// left hand side of the pipe
|
||||||
|
fn untyped_left_hand_value_variable_call_argument(&self) -> CallArg<UntypedExpr> {
|
||||||
|
CallArg {
|
||||||
|
label: None,
|
||||||
|
location: self.argument_location,
|
||||||
|
value: self.untyped_left_hand_value_variable(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a variable that can be used to refer to the value on the left
|
||||||
|
/// hand side of the pipe
|
||||||
|
fn typed_left_hand_value_variable(&self) -> TypedExpr {
|
||||||
|
TypedExpr::Var {
|
||||||
|
location: self.argument_location,
|
||||||
|
name: PIPE_VARIABLE.to_string(),
|
||||||
|
constructor: ValueConstructor {
|
||||||
|
public: true,
|
||||||
|
tipo: self.argument_type.clone(),
|
||||||
|
variant: ValueConstructorVariant::LocalVariable {
|
||||||
|
location: self.argument_location,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a variable that can be used to refer to the value on the left
|
||||||
|
/// hand side of the pipe
|
||||||
|
fn untyped_left_hand_value_variable(&self) -> UntypedExpr {
|
||||||
|
UntypedExpr::Var {
|
||||||
|
location: self.argument_location,
|
||||||
|
name: PIPE_VARIABLE.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an assignment for the value on the left hand side of the pipe
|
||||||
|
fn push_assignment(&mut self, expression: TypedExpr) {
|
||||||
|
self.argument_type = expression.tipo();
|
||||||
|
self.argument_location = expression.location();
|
||||||
|
self.push_assignment_no_update(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_assignment_no_update(&mut self, expression: TypedExpr) {
|
||||||
|
let location = expression.location();
|
||||||
|
|
||||||
|
// Insert the variable for use in type checking the rest of the pipeline
|
||||||
|
self.expr_typer.environment.insert_variable(
|
||||||
|
PIPE_VARIABLE.to_string(),
|
||||||
|
ValueConstructorVariant::LocalVariable { location },
|
||||||
|
expression.tipo(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the assignment to the AST
|
||||||
|
let assignment = TypedExpr::Assignment {
|
||||||
|
location,
|
||||||
|
tipo: expression.tipo(),
|
||||||
|
kind: AssignmentKind::Let,
|
||||||
|
value: Box::new(expression),
|
||||||
|
pattern: Pattern::Var {
|
||||||
|
location,
|
||||||
|
name: PIPE_VARIABLE.to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
self.expressions.push(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to infer a |> b(..c) as b(..c)(a)
|
||||||
|
fn infer_apply_to_call_pipe(
|
||||||
|
&mut self,
|
||||||
|
function: TypedExpr,
|
||||||
|
args: Vec<CallArg<UntypedExpr>>,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
let (function, args, tipo) = self
|
||||||
|
.expr_typer
|
||||||
|
.do_infer_call_with_known_fun(function, args, location)?;
|
||||||
|
|
||||||
|
let function = TypedExpr::Call {
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
args,
|
||||||
|
fun: Box::new(function),
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = vec![self.untyped_left_hand_value_variable_call_argument()];
|
||||||
|
// TODO: use `.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch)`
|
||||||
|
// This will require the typing of the arguments to be lifted up out of
|
||||||
|
// the function below. If it is not we don't know if the error comes
|
||||||
|
// from incorrect usage of the pipe or if it originates from the
|
||||||
|
// argument expressions.
|
||||||
|
let (function, args, tipo) = self
|
||||||
|
.expr_typer
|
||||||
|
.do_infer_call_with_known_fun(function, args, location)?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::Call {
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
args,
|
||||||
|
fun: Box::new(function),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to infer a |> b(c) as b(a, c)
|
||||||
|
fn infer_insert_pipe(
|
||||||
|
&mut self,
|
||||||
|
function: TypedExpr,
|
||||||
|
mut arguments: Vec<CallArg<UntypedExpr>>,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
arguments.insert(0, self.untyped_left_hand_value_variable_call_argument());
|
||||||
|
// TODO: use `.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch)`
|
||||||
|
// This will require the typing of the arguments to be lifted up out of
|
||||||
|
// the function below. If it is not we don't know if the error comes
|
||||||
|
// from incorrect usage of the pipe or if it originates from the
|
||||||
|
// argument expressions.
|
||||||
|
let (fun, args, tipo) = self
|
||||||
|
.expr_typer
|
||||||
|
.do_infer_call_with_known_fun(function, arguments, location)?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::Call {
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
args,
|
||||||
|
fun: Box::new(fun),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to infer a |> b as b(a)
|
||||||
|
fn infer_apply_pipe(&mut self, func: UntypedExpr) -> Result<TypedExpr, Error> {
|
||||||
|
let func = Box::new(self.expr_typer.infer(func)?);
|
||||||
|
let return_type = self.expr_typer.new_unbound_var();
|
||||||
|
|
||||||
|
// Ensure that the function accepts one argument of the correct type
|
||||||
|
self.expr_typer
|
||||||
|
.environment
|
||||||
|
.unify(
|
||||||
|
func.tipo(),
|
||||||
|
function(vec![self.argument_type.clone()], return_type.clone()),
|
||||||
|
func.location(),
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
let is_pipe_mismatch = self.check_if_pipe_type_mismatch(&e, func.location());
|
||||||
|
|
||||||
|
if is_pipe_mismatch {
|
||||||
|
e.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::Call {
|
||||||
|
location: func.location(),
|
||||||
|
tipo: return_type,
|
||||||
|
fun: func,
|
||||||
|
args: vec![self.typed_left_hand_value_variable_call_argument()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_if_pipe_type_mismatch(&mut self, error: &Error, location: Span) -> bool {
|
||||||
|
let types = match error {
|
||||||
|
Error::CouldNotUnify {
|
||||||
|
expected, given, ..
|
||||||
|
} => (expected.as_ref(), given.as_ref()),
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
match types {
|
||||||
|
(Type::Fn { args: a, .. }, Type::Fn { args: b, .. }) if a.len() == b.len() => {
|
||||||
|
match (a.get(0), b.get(0)) {
|
||||||
|
(Some(a), Some(b)) => self
|
||||||
|
.expr_typer
|
||||||
|
.environment
|
||||||
|
.unify(a.clone(), b.clone(), location)
|
||||||
|
.is_err(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,3 @@
|
||||||
use aiken/builtins.{appendByteString}
|
|
||||||
|
|
||||||
pub type Bool {
|
|
||||||
True
|
|
||||||
False
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append(a: ByteArray, b: ByteArray) -> ByteArray {
|
pub fn append(a: ByteArray, b: ByteArray) -> ByteArray {
|
||||||
appendByteString(a, b)
|
todo
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use sample/syntax.{append, Bool}
|
use sample/syntax.{append}
|
||||||
|
|
||||||
pub type Datum {
|
pub type Datum {
|
||||||
something: String,
|
something: String,
|
||||||
|
|
Loading…
Reference in New Issue