feat: typecheck validators
This commit is contained in:
parent
2e7fe191db
commit
a044c3580e
|
@ -265,7 +265,7 @@ pub type UntypedValidator = Validator<(), UntypedExpr>;
|
||||||
pub struct Validator<T, Expr> {
|
pub struct Validator<T, Expr> {
|
||||||
pub doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
pub end_position: usize,
|
pub end_position: usize,
|
||||||
pub function: Function<T, Expr>,
|
pub fun: Function<T, Expr>,
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
pub params: Vec<Arg<T>>,
|
pub params: Vec<Arg<T>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,7 +238,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
|
|
||||||
Definition::Validator(Validator {
|
Definition::Validator(Validator {
|
||||||
end_position,
|
end_position,
|
||||||
function,
|
fun: function,
|
||||||
params,
|
params,
|
||||||
..
|
..
|
||||||
}) => self.definition_validator(params, function, *end_position),
|
}) => self.definition_validator(params, function, *end_position),
|
||||||
|
@ -560,26 +560,26 @@ impl<'comments> Formatter<'comments> {
|
||||||
fn definition_validator<'a>(
|
fn definition_validator<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: &'a [UntypedArg],
|
params: &'a [UntypedArg],
|
||||||
function: &'a UntypedFunction,
|
fun: &'a UntypedFunction,
|
||||||
end_position: usize,
|
end_position: usize,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
// Fn and args
|
// Fn and args
|
||||||
let head = "fn".to_doc().append(wrap_args(
|
let head = "fn".to_doc().append(wrap_args(
|
||||||
function.arguments.iter().map(|e| (self.fn_arg(e), false)),
|
fun.arguments.iter().map(|e| (self.fn_arg(e), false)),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add return annotation
|
// Add return annotation
|
||||||
let head = match &function.return_annotation {
|
let head = match &fun.return_annotation {
|
||||||
Some(anno) => head.append(" -> ").append(self.annotation(anno)),
|
Some(anno) => head.append(" -> ").append(self.annotation(anno)),
|
||||||
None => head,
|
None => head,
|
||||||
}
|
}
|
||||||
.group();
|
.group();
|
||||||
|
|
||||||
// Format body
|
// Format body
|
||||||
let body = self.expr(&function.body);
|
let body = self.expr(&fun.body);
|
||||||
|
|
||||||
// Add any trailing comments
|
// Add any trailing comments
|
||||||
let body = match printed_comments(self.pop_comments(function.end_position), false) {
|
let body = match printed_comments(self.pop_comments(fun.end_position), false) {
|
||||||
Some(comments) => body.append(line()).append(comments),
|
Some(comments) => body.append(line()).append(comments),
|
||||||
None => body,
|
None => body,
|
||||||
};
|
};
|
||||||
|
@ -588,7 +588,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
let v_head = "validator"
|
let v_head = "validator"
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(function.name.as_str())
|
.append(fun.name.as_str())
|
||||||
.append(wrap_args(params.iter().map(|e| (self.fn_arg(e), false))));
|
.append(wrap_args(params.iter().map(|e| (self.fn_arg(e), false))));
|
||||||
|
|
||||||
// Stick it all together
|
// Stick it all together
|
||||||
|
|
|
@ -284,12 +284,12 @@ pub fn validator_parser() -> impl Parser<Token, ast::UntypedDefinition, Error =
|
||||||
)
|
)
|
||||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||||
)
|
)
|
||||||
.map_with_span(|((name, (params, params_span)), mut function), span| {
|
.map_with_span(|((name, (params, params_span)), mut fun), span| {
|
||||||
function.name = name;
|
fun.name = name;
|
||||||
|
|
||||||
ast::UntypedDefinition::Validator(ast::Validator {
|
ast::UntypedDefinition::Validator(ast::Validator {
|
||||||
doc: None,
|
doc: None,
|
||||||
function,
|
fun,
|
||||||
location: Span {
|
location: Span {
|
||||||
start: span.start,
|
start: span.start,
|
||||||
end: params_span.end,
|
end: params_span.end,
|
||||||
|
|
|
@ -10,11 +10,11 @@ use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind, Pattern,
|
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind, Pattern,
|
||||||
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
|
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
|
||||||
UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE,
|
UnqualifiedImport, UntypedDefinition, Use, Validator, PIPE_VARIABLE,
|
||||||
},
|
},
|
||||||
builtins::{self, function, generic_var, tuple, unbound_var},
|
builtins::{self, function, generic_var, tuple, unbound_var},
|
||||||
tipo::fields::FieldMap,
|
tipo::fields::FieldMap,
|
||||||
IdGenerator, VALIDATOR_NAMES,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -247,6 +247,7 @@ impl<'a> Environment<'a> {
|
||||||
| Definition::DataType { .. }
|
| Definition::DataType { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
|
| Definition::Validator { .. }
|
||||||
| Definition::ModuleConstant { .. }) => definition,
|
| Definition::ModuleConstant { .. }) => definition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,11 +1071,73 @@ impl<'a> Environment<'a> {
|
||||||
tipo,
|
tipo,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !public && (kind.is_lib() || !VALIDATOR_NAMES.contains(&name.as_str())) {
|
if !public && kind.is_lib() {
|
||||||
self.init_usage(name.clone(), EntityKind::PrivateFunction, *location);
|
self.init_usage(name.clone(), EntityKind::PrivateFunction, *location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Definition::Validator(Validator {
|
||||||
|
fun,
|
||||||
|
location,
|
||||||
|
params,
|
||||||
|
..
|
||||||
|
}) if kind.is_validator() => {
|
||||||
|
assert_unique_value_name(names, &fun.name, location)?;
|
||||||
|
|
||||||
|
// Create the field map so we can reorder labels for usage of this function
|
||||||
|
let mut field_map = FieldMap::new(fun.arguments.len() + params.len(), true);
|
||||||
|
|
||||||
|
// Chain together extra params and function.arguments
|
||||||
|
for (i, arg) in params.iter().chain(fun.arguments.iter()).enumerate() {
|
||||||
|
field_map.insert(arg.arg_name.get_label().clone(), i, &arg.location)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_map = field_map.into_option();
|
||||||
|
|
||||||
|
// Construct type from annotations
|
||||||
|
let mut hydrator = Hydrator::new();
|
||||||
|
|
||||||
|
hydrator.permit_holes(false);
|
||||||
|
|
||||||
|
let mut arg_types = Vec::new();
|
||||||
|
|
||||||
|
for arg in params.iter().chain(fun.arguments.iter()) {
|
||||||
|
let tipo = hydrator.type_from_option_annotation(&arg.annotation, self)?;
|
||||||
|
|
||||||
|
arg_types.push(tipo);
|
||||||
|
}
|
||||||
|
|
||||||
|
let return_type =
|
||||||
|
hydrator.type_from_option_annotation(&fun.return_annotation, self)?;
|
||||||
|
|
||||||
|
let tipo = function(arg_types, return_type);
|
||||||
|
|
||||||
|
// Keep track of which types we create from annotations so we can know
|
||||||
|
// which generic types not to instantiate later when performing
|
||||||
|
// inference of the function body.
|
||||||
|
hydrators.insert(fun.name.clone(), hydrator);
|
||||||
|
|
||||||
|
// Insert the function into the environment
|
||||||
|
self.insert_variable(
|
||||||
|
fun.name.clone(),
|
||||||
|
ValueConstructorVariant::ModuleFn {
|
||||||
|
name: fun.name.clone(),
|
||||||
|
field_map,
|
||||||
|
module: module_name.to_owned(),
|
||||||
|
arity: params.len() + fun.arguments.len(),
|
||||||
|
location: fun.location,
|
||||||
|
builtin: None,
|
||||||
|
},
|
||||||
|
tipo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Definition::Validator(Validator { location, .. }) => {
|
||||||
|
self.warnings.push(Warning::ValidatorInLibraryModule {
|
||||||
|
location: *location,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Definition::Test(Function { name, location, .. }) => {
|
Definition::Test(Function { name, location, .. }) => {
|
||||||
assert_unique_value_name(names, name, location)?;
|
assert_unique_value_name(names, name, location)?;
|
||||||
hydrators.insert(name.clone(), Hydrator::new());
|
hydrators.insert(name.clone(), Hydrator::new());
|
||||||
|
|
|
@ -1209,6 +1209,14 @@ pub enum Warning {
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I came across a validator in a {} module\nwhich means I'm going to ignore it.\n", "lib/".purple())]
|
||||||
|
#[diagnostic(help(
|
||||||
|
"No big deal, but you might want to move it to a\n{} module or remove it to get rid of that warning.",
|
||||||
|
"validators/".purple()
|
||||||
|
))]
|
||||||
|
#[diagnostic(code("unused::validator"))]
|
||||||
|
ValidatorInLibraryModule { location: Span },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
DataType, Definition, Function, Layer, ModuleConstant, ModuleKind, RecordConstructor,
|
DataType, Definition, Function, Layer, ModuleConstant, ModuleKind, RecordConstructor,
|
||||||
RecordConstructorArg, Span, TypeAlias, TypedDefinition, TypedModule, UntypedDefinition,
|
RecordConstructorArg, Span, TypeAlias, TypedDefinition, TypedModule, UntypedDefinition,
|
||||||
UntypedModule, Use,
|
UntypedModule, Use, Validator,
|
||||||
},
|
},
|
||||||
builtins,
|
builtins,
|
||||||
builtins::function,
|
builtins::function,
|
||||||
|
@ -72,6 +72,8 @@ impl UntypedModule {
|
||||||
for def in self.definitions().cloned() {
|
for def in self.definitions().cloned() {
|
||||||
match def {
|
match def {
|
||||||
Definition::ModuleConstant { .. } => consts.push(def),
|
Definition::ModuleConstant { .. } => consts.push(def),
|
||||||
|
Definition::Validator { .. } if kind.is_validator() => not_consts.push(def),
|
||||||
|
Definition::Validator { .. } => (),
|
||||||
Definition::Fn { .. }
|
Definition::Fn { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
| Definition::TypeAlias { .. }
|
| Definition::TypeAlias { .. }
|
||||||
|
@ -248,14 +250,58 @@ fn infer_definition(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Definition::Validator(Validator {
|
||||||
|
doc,
|
||||||
|
location,
|
||||||
|
end_position,
|
||||||
|
mut fun,
|
||||||
|
mut params,
|
||||||
|
}) => {
|
||||||
|
let params_length = params.len();
|
||||||
|
params.append(&mut fun.arguments);
|
||||||
|
fun.arguments = params;
|
||||||
|
|
||||||
|
if let Definition::Fn(mut typed_fun) = infer_definition(
|
||||||
|
Definition::Fn(fun),
|
||||||
|
module_name,
|
||||||
|
hydrators,
|
||||||
|
environment,
|
||||||
|
kind,
|
||||||
|
)? {
|
||||||
|
// Do we want to do this here?
|
||||||
|
// or should we remove this and keep the later check
|
||||||
|
// the later check has a much more specific error message
|
||||||
|
// we may want to not do this here
|
||||||
|
environment.unify(
|
||||||
|
typed_fun.return_type.clone(),
|
||||||
|
builtins::bool(),
|
||||||
|
typed_fun.location,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let typed_params = typed_fun.arguments.drain(0..params_length).collect();
|
||||||
|
|
||||||
|
Ok(Definition::Validator(Validator {
|
||||||
|
doc,
|
||||||
|
end_position,
|
||||||
|
fun: typed_fun,
|
||||||
|
location,
|
||||||
|
params: typed_params,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
unreachable!("validator definition inferred as something other than a function?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Definition::Test(f) => {
|
Definition::Test(f) => {
|
||||||
if let Definition::Fn(f) =
|
if let Definition::Fn(f) =
|
||||||
infer_definition(Definition::Fn(f), module_name, hydrators, environment, kind)?
|
infer_definition(Definition::Fn(f), module_name, hydrators, environment, kind)?
|
||||||
{
|
{
|
||||||
environment.unify(f.return_type.clone(), builtins::bool(), f.location, false)?;
|
environment.unify(f.return_type.clone(), builtins::bool(), f.location, false)?;
|
||||||
|
|
||||||
Ok(Definition::Test(f))
|
Ok(Definition::Test(f))
|
||||||
} else {
|
} else {
|
||||||
unreachable!("test defintion inferred as something else than a function?")
|
unreachable!("test definition inferred as something other than a function?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue