Provide default annotation to validators (incl. fallback)

Without that, we may encounter weird error messages when writing
  validators without an explicit `else`. Since we automatically fill it
  with a `fail`; without annotation, it unifies to a generic parameter.

  The existing check that would look for the body being an error term is
  ill-advised as it doesn't work as soon as one adds tracing, or make
  the validator a parameterized validator. Plus, it may simply trigger
  the wrong behavior as one can now annotate a validator with _whatever_
  and get pass the type-checker by plucking a `fail` keyword as body.
This commit is contained in:
KtorZ 2024-08-25 17:08:46 +02:00
parent 1198d7a5ae
commit af9a785d65
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 36 additions and 34 deletions

View File

@ -1,12 +1,10 @@
use chumsky::prelude::*; use super::function::param;
use crate::{ use crate::{
ast::{self, ArgBy, ArgName}, ast::{self, ArgBy, ArgName},
expr::UntypedExpr, expr::UntypedExpr,
parser::{annotation, error::ParseError, expr, token::Token}, parser::{annotation, error::ParseError, expr, token::Token},
}; };
use chumsky::prelude::*;
use super::function::param;
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
just(Token::Validator) just(Token::Validator)
@ -52,36 +50,38 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
}, },
)); ));
let location = ast::Span {
start: span.start,
// capture the span from the optional params
end: params_span.end,
};
ast::UntypedDefinition::Validator(ast::Validator { ast::UntypedDefinition::Validator(ast::Validator {
doc: None, doc: None,
name, name,
handlers, handlers,
location: ast::Span { location,
start: span.start,
// capture the span from the optional params
end: params_span.end,
},
params, params,
end_position: span.end - 1, end_position: span.end - 1,
fallback: opt_catch_all.unwrap_or(ast::Function { fallback: opt_catch_all.unwrap_or(ast::Function {
arguments: vec![ast::UntypedArg { arguments: vec![ast::UntypedArg {
by: ArgBy::ByName(ArgName::Discarded { by: ArgBy::ByName(ArgName::Discarded {
name: "_ctx".to_string(), name: "_".to_string(),
label: "_ctx".to_string(), label: "_".to_string(),
location: ast::Span::empty(), location,
}), }),
location: ast::Span::empty(), location,
annotation: None, annotation: None,
doc: None, doc: None,
is_validator_param: false, is_validator_param: false,
}], }],
body: UntypedExpr::fail(None, ast::Span::empty()), body: UntypedExpr::fail(None, location),
doc: None, doc: None,
location: ast::Span::empty(), location,
end_position: span.end - 1, end_position: location.end - 1,
name: "else".to_string(), name: "else".to_string(),
public: true, public: true,
return_annotation: None, return_annotation: Some(ast::Annotation::boolean(location)),
return_type: (), return_type: (),
on_test_failure: ast::OnTestFailure::FailImmediately, on_test_failure: ast::OnTestFailure::FailImmediately,
}), }),
@ -103,23 +103,28 @@ pub fn args_and_body() -> impl Parser<Token, ast::UntypedFunction, Error = Parse
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
) )
.map_with_span( .map_with_span(
|(((arguments, args_span), return_annotation), body), span| ast::Function { |(((arguments, args_span), return_annotation), body), span| {
arguments, let location = ast::Span {
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
doc: None,
location: ast::Span {
start: span.start, start: span.start,
end: return_annotation end: return_annotation
.as_ref() .as_ref()
.map(|l| l.location().end) .map(|l| l.location().end)
.unwrap_or_else(|| args_span.end), .unwrap_or_else(|| args_span.end),
}, };
ast::Function {
arguments,
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
doc: None,
location,
end_position: span.end - 1, end_position: span.end - 1,
name: "temp".to_string(), name: "temp".to_string(),
public: true, public: true,
return_annotation, return_annotation: return_annotation
.or(Some(ast::Annotation::boolean(location))),
return_type: (), return_type: (),
on_test_failure: ast::OnTestFailure::FailImmediately, on_test_failure: ast::OnTestFailure::FailImmediately,
}
}, },
) )
} }

View File

@ -259,8 +259,7 @@ fn infer_definition(
typed_fallback.name = old_name; typed_fallback.name = old_name;
if !typed_fallback.body.is_error_term() && !typed_fallback.return_type.is_bool() if !typed_fallback.return_type.is_bool() {
{
return Err(Error::ValidatorMustReturnBool { return Err(Error::ValidatorMustReturnBool {
return_type: typed_fallback.return_type.clone(), return_type: typed_fallback.return_type.clone(),
location: typed_fallback.location, location: typed_fallback.location,

View File

@ -100,9 +100,7 @@ pub fn eval_phase_two_raw(
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, tx_bytes)) .or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, tx_bytes))
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, tx_bytes))?; .or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, tx_bytes))?;
let cost_mdls = cost_mdls_bytes let cost_mdls = cost_mdls_bytes.map(CostMdls::decode_fragment).transpose()?;
.map(|x| CostMdls::decode_fragment(x))
.transpose()?;
let budget = ExBudget { let budget = ExBudget {
cpu: initial_budget.0 as i64, cpu: initial_budget.0 as i64,