Rework 'compact' mode for traces
- Trace-if-false are now completely discarded in compact mode. - Only the label (i.e. first trace argument) is preserved. - When compiling with tracing _compact_, the first label MUST unify to a string. This shouldn't be an issue generally speaking and would enforce that traces follow the pattern ``` label: arg_0[, arg_1, ..., arg_n] ``` Note that what isn't obvious with these changes is that we now support what the "emit" keyword was trying to achieve; as we compile now with user-defined traces only, and in compact mode to only keep event labels in the final contract; while allowing larger payloads with verbose tracing.
This commit is contained in:
parent
a9d782e206
commit
d6fd37c80e
|
@ -1957,6 +1957,10 @@ pub enum TraceLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tracing {
|
impl Tracing {
|
||||||
|
pub fn verbose() -> Self {
|
||||||
|
Tracing::All(TraceLevel::Verbose)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn silent() -> Self {
|
pub fn silent() -> Self {
|
||||||
Tracing::All(TraceLevel::Silent)
|
Tracing::All(TraceLevel::Silent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ fn check_module(
|
||||||
ast: UntypedModule,
|
ast: UntypedModule,
|
||||||
extra: Vec<(String, UntypedModule)>,
|
extra: Vec<(String, UntypedModule)>,
|
||||||
kind: ModuleKind,
|
kind: ModuleKind,
|
||||||
|
tracing: Tracing,
|
||||||
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||||
let id_gen = IdGenerator::new();
|
let id_gen = IdGenerator::new();
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ fn check_module(
|
||||||
kind,
|
kind,
|
||||||
"test/project",
|
"test/project",
|
||||||
&module_types,
|
&module_types,
|
||||||
Tracing::All(TraceLevel::Verbose),
|
tracing,
|
||||||
&mut warnings,
|
&mut warnings,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -57,20 +58,27 @@ fn check_module(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check(ast: UntypedModule) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
fn check(ast: UntypedModule) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||||
check_module(ast, Vec::new(), ModuleKind::Lib)
|
check_module(ast, Vec::new(), ModuleKind::Lib, Tracing::verbose())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_with_verbosity(
|
||||||
|
ast: UntypedModule,
|
||||||
|
level: TraceLevel,
|
||||||
|
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||||
|
check_module(ast, Vec::new(), ModuleKind::Lib, Tracing::All(level))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_with_deps(
|
fn check_with_deps(
|
||||||
ast: UntypedModule,
|
ast: UntypedModule,
|
||||||
extra: Vec<(String, UntypedModule)>,
|
extra: Vec<(String, UntypedModule)>,
|
||||||
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||||
check_module(ast, extra, ModuleKind::Lib)
|
check_module(ast, extra, ModuleKind::Lib, Tracing::verbose())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_validator(
|
fn check_validator(
|
||||||
ast: UntypedModule,
|
ast: UntypedModule,
|
||||||
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||||
check_module(ast, Vec::new(), ModuleKind::Validator)
|
check_module(ast, Vec::new(), ModuleKind::Validator, Tracing::verbose())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1288,8 +1296,32 @@ fn trace_non_strings() {
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_string_label_compact() {
|
||||||
|
let source_code = r#"
|
||||||
|
test foo() {
|
||||||
|
trace @"foo": [1,2,3]
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_non_string_label_compact() {
|
||||||
|
let source_code = r#"
|
||||||
|
test foo() {
|
||||||
|
trace(14 + 42)
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
check(parse(source_code)),
|
check_with_verbosity(parse(source_code), TraceLevel::Compact),
|
||||||
Err((_, Error::CouldNotUnify { .. }))
|
Err((_, Error::CouldNotUnify { .. }))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
expr::{FnStyle, TypedExpr, UntypedExpr},
|
expr::{FnStyle, TypedExpr, UntypedExpr},
|
||||||
format,
|
format,
|
||||||
line_numbers::LineNumbers,
|
|
||||||
tipo::{fields::FieldMap, DefaultFunction, PatternConstructor, TypeVar},
|
tipo::{fields::FieldMap, DefaultFunction, PatternConstructor, TypeVar},
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
@ -41,7 +40,6 @@ pub(crate) fn infer_function(
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
hydrators: &mut HashMap<String, Hydrator>,
|
hydrators: &mut HashMap<String, Hydrator>,
|
||||||
environment: &mut Environment<'_>,
|
environment: &mut Environment<'_>,
|
||||||
lines: &LineNumbers,
|
|
||||||
tracing: Tracing,
|
tracing: Tracing,
|
||||||
) -> Result<Function<Rc<Type>, TypedExpr, TypedArg>, Error> {
|
) -> Result<Function<Rc<Type>, TypedExpr, TypedArg>, Error> {
|
||||||
if let Some(typed_fun) = environment.inferred_functions.get(&fun.name) {
|
if let Some(typed_fun) = environment.inferred_functions.get(&fun.name) {
|
||||||
|
@ -122,7 +120,7 @@ pub(crate) fn infer_function(
|
||||||
.remove(name)
|
.remove(name)
|
||||||
.unwrap_or_else(|| panic!("Could not find hydrator for fn {name}"));
|
.unwrap_or_else(|| panic!("Could not find hydrator for fn {name}"));
|
||||||
|
|
||||||
let mut expr_typer = ExprTyper::new(environment, lines, tracing);
|
let mut expr_typer = ExprTyper::new(environment, tracing);
|
||||||
expr_typer.hydrator = hydrator;
|
expr_typer.hydrator = hydrator;
|
||||||
expr_typer.not_yet_inferred = BTreeSet::from_iter(hydrators.keys().cloned());
|
expr_typer.not_yet_inferred = BTreeSet::from_iter(hydrators.keys().cloned());
|
||||||
|
|
||||||
|
@ -155,12 +153,11 @@ pub(crate) fn infer_function(
|
||||||
environment.current_module,
|
environment.current_module,
|
||||||
hydrators,
|
hydrators,
|
||||||
environment,
|
environment,
|
||||||
lines,
|
|
||||||
tracing,
|
tracing,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Then, try again the entire function definition.
|
// Then, try again the entire function definition.
|
||||||
return infer_function(fun, module_name, hydrators, environment, lines, tracing);
|
return infer_function(fun, module_name, hydrators, environment, tracing);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (arguments, body, return_type) = inferred?;
|
let (arguments, body, return_type) = inferred?;
|
||||||
|
@ -223,8 +220,6 @@ pub(crate) fn infer_function(
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ExprTyper<'a, 'b> {
|
pub(crate) struct ExprTyper<'a, 'b> {
|
||||||
pub(crate) lines: &'a LineNumbers,
|
|
||||||
|
|
||||||
pub(crate) environment: &'a mut Environment<'b>,
|
pub(crate) environment: &'a mut Environment<'b>,
|
||||||
|
|
||||||
// We tweak the tracing behavior during type-check. Traces are either kept or left out of the
|
// We tweak the tracing behavior during type-check. Traces are either kept or left out of the
|
||||||
|
@ -244,18 +239,13 @@ pub(crate) struct ExprTyper<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ExprTyper<'a, 'b> {
|
impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
pub fn new(
|
pub fn new(environment: &'a mut Environment<'b>, tracing: Tracing) -> Self {
|
||||||
environment: &'a mut Environment<'b>,
|
|
||||||
lines: &'a LineNumbers,
|
|
||||||
tracing: Tracing,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
hydrator: Hydrator::new(),
|
hydrator: Hydrator::new(),
|
||||||
not_yet_inferred: BTreeSet::new(),
|
not_yet_inferred: BTreeSet::new(),
|
||||||
environment,
|
environment,
|
||||||
tracing,
|
tracing,
|
||||||
ungeneralised_function_used: false,
|
ungeneralised_function_used: false,
|
||||||
lines,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,25 +671,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
.to_pretty_string(999)
|
.to_pretty_string(999)
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
TraceLevel::Compact => Some(TypedExpr::String {
|
TraceLevel::Compact | TraceLevel::Silent => None,
|
||||||
location,
|
|
||||||
tipo: string(),
|
|
||||||
value: self
|
|
||||||
.lines
|
|
||||||
.line_and_column_number(location.start)
|
|
||||||
.expect("Spans are within bounds.")
|
|
||||||
.to_string(),
|
|
||||||
}),
|
|
||||||
TraceLevel::Silent => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let typed_value = self.infer(value)?;
|
let typed_value = self.infer(value)?;
|
||||||
|
|
||||||
self.unify(bool(), typed_value.tipo(), typed_value.location(), false)?;
|
self.unify(bool(), typed_value.tipo(), typed_value.location(), false)?;
|
||||||
|
|
||||||
match self.tracing.trace_level(false) {
|
match text {
|
||||||
TraceLevel::Silent => Ok(typed_value),
|
None => Ok(typed_value),
|
||||||
TraceLevel::Verbose | TraceLevel::Compact => Ok(TypedExpr::If {
|
Some(text) => Ok(TypedExpr::If {
|
||||||
location,
|
location,
|
||||||
branches: vec1::vec1![IfBranch {
|
branches: vec1::vec1![IfBranch {
|
||||||
condition: typed_value,
|
condition: typed_value,
|
||||||
|
@ -710,7 +691,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
final_else: Box::new(TypedExpr::Trace {
|
final_else: Box::new(TypedExpr::Trace {
|
||||||
location,
|
location,
|
||||||
tipo: bool(),
|
tipo: bool(),
|
||||||
text: Box::new(text.expect("TraceLevel::Silent excluded from pattern-guard")),
|
text: Box::new(text),
|
||||||
then: Box::new(var_false),
|
then: Box::new(var_false),
|
||||||
}),
|
}),
|
||||||
tipo: bool(),
|
tipo: bool(),
|
||||||
|
@ -2426,29 +2407,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
label: UntypedExpr,
|
label: UntypedExpr,
|
||||||
arguments: Vec<UntypedExpr>,
|
arguments: Vec<UntypedExpr>,
|
||||||
) -> Result<TypedExpr, Error> {
|
) -> Result<TypedExpr, Error> {
|
||||||
let label = self.infer_trace_arg(label)?;
|
|
||||||
|
|
||||||
let typed_arguments = arguments
|
let typed_arguments = arguments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg| self.infer_trace_arg(arg))
|
.map(|arg| self.infer_trace_arg(arg))
|
||||||
.collect::<Result<Vec<_>, Error>>()?;
|
.collect::<Result<Vec<_>, Error>>()?;
|
||||||
|
|
||||||
let text = if typed_arguments.is_empty() {
|
|
||||||
label
|
|
||||||
} else {
|
|
||||||
let delimiter = |ix| TypedExpr::String {
|
|
||||||
location: Span::empty(),
|
|
||||||
tipo: string(),
|
|
||||||
value: if ix == 0 { ": " } else { ", " }.to_string(),
|
|
||||||
};
|
|
||||||
typed_arguments
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.fold(label, |text, (ix, arg)| {
|
|
||||||
append_string_expr(append_string_expr(text, delimiter(ix)), arg)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let then = self.infer(then)?;
|
let then = self.infer(then)?;
|
||||||
let tipo = then.tipo();
|
let tipo = then.tipo();
|
||||||
|
|
||||||
|
@ -2461,26 +2424,42 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
|
|
||||||
match self.tracing.trace_level(false) {
|
match self.tracing.trace_level(false) {
|
||||||
TraceLevel::Silent => Ok(then),
|
TraceLevel::Silent => Ok(then),
|
||||||
TraceLevel::Compact => Ok(TypedExpr::Trace {
|
TraceLevel::Compact => {
|
||||||
location,
|
let text = self.infer(label)?;
|
||||||
tipo,
|
self.unify(string(), text.tipo(), text.location(), false)?;
|
||||||
then: Box::new(then),
|
Ok(TypedExpr::Trace {
|
||||||
text: Box::new(TypedExpr::String {
|
|
||||||
location,
|
location,
|
||||||
tipo: string(),
|
tipo,
|
||||||
value: self
|
then: Box::new(then),
|
||||||
.lines
|
text: Box::new(text),
|
||||||
.line_and_column_number(location.start)
|
})
|
||||||
.expect("Spans are within bounds.")
|
}
|
||||||
.to_string(),
|
TraceLevel::Verbose => {
|
||||||
}),
|
let label = self.infer_trace_arg(label)?;
|
||||||
}),
|
|
||||||
TraceLevel::Verbose => Ok(TypedExpr::Trace {
|
let text = if typed_arguments.is_empty() {
|
||||||
location,
|
label
|
||||||
tipo,
|
} else {
|
||||||
then: Box::new(then),
|
let delimiter = |ix| TypedExpr::String {
|
||||||
text: Box::new(text),
|
location: Span::empty(),
|
||||||
}),
|
tipo: string(),
|
||||||
|
value: if ix == 0 { ": " } else { ", " }.to_string(),
|
||||||
|
};
|
||||||
|
typed_arguments
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(label, |text, (ix, arg)| {
|
||||||
|
append_string_expr(append_string_expr(text, delimiter(ix)), arg)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TypedExpr::Trace {
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
then: Box::new(then),
|
||||||
|
text: Box::new(text),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
builtins,
|
builtins,
|
||||||
builtins::{fuzzer, generic_var},
|
builtins::{fuzzer, generic_var},
|
||||||
line_numbers::LineNumbers,
|
|
||||||
tipo::{expr::infer_function, Span, Type, TypeVar},
|
tipo::{expr::infer_function, Span, Type, TypeVar},
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
@ -86,14 +85,8 @@ impl UntypedModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
for def in consts.into_iter().chain(not_consts) {
|
for def in consts.into_iter().chain(not_consts) {
|
||||||
let definition = infer_definition(
|
let definition =
|
||||||
def,
|
infer_definition(def, &module_name, &mut hydrators, &mut environment, tracing)?;
|
||||||
&module_name,
|
|
||||||
&mut hydrators,
|
|
||||||
&mut environment,
|
|
||||||
&self.lines,
|
|
||||||
tracing,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
definitions.push(definition);
|
definitions.push(definition);
|
||||||
}
|
}
|
||||||
|
@ -162,7 +155,6 @@ fn infer_definition(
|
||||||
module_name: &String,
|
module_name: &String,
|
||||||
hydrators: &mut HashMap<String, Hydrator>,
|
hydrators: &mut HashMap<String, Hydrator>,
|
||||||
environment: &mut Environment<'_>,
|
environment: &mut Environment<'_>,
|
||||||
lines: &LineNumbers,
|
|
||||||
tracing: Tracing,
|
tracing: Tracing,
|
||||||
) -> Result<TypedDefinition, Error> {
|
) -> Result<TypedDefinition, Error> {
|
||||||
match def {
|
match def {
|
||||||
|
@ -171,7 +163,6 @@ fn infer_definition(
|
||||||
module_name,
|
module_name,
|
||||||
hydrators,
|
hydrators,
|
||||||
environment,
|
environment,
|
||||||
lines,
|
|
||||||
tracing,
|
tracing,
|
||||||
)?)),
|
)?)),
|
||||||
|
|
||||||
|
@ -228,7 +219,7 @@ fn infer_definition(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut typed_fun =
|
let mut typed_fun =
|
||||||
infer_function(&fun, module_name, hydrators, environment, lines, tracing)?;
|
infer_function(&fun, module_name, hydrators, environment, tracing)?;
|
||||||
|
|
||||||
if !typed_fun.return_type.is_bool() {
|
if !typed_fun.return_type.is_bool() {
|
||||||
return Err(Error::ValidatorMustReturnBool {
|
return Err(Error::ValidatorMustReturnBool {
|
||||||
|
@ -267,14 +258,8 @@ fn infer_definition(
|
||||||
let params = params.into_iter().chain(other.arguments);
|
let params = params.into_iter().chain(other.arguments);
|
||||||
other.arguments = params.collect();
|
other.arguments = params.collect();
|
||||||
|
|
||||||
let mut other_typed_fun = infer_function(
|
let mut other_typed_fun =
|
||||||
&other,
|
infer_function(&other, module_name, hydrators, environment, tracing)?;
|
||||||
module_name,
|
|
||||||
hydrators,
|
|
||||||
environment,
|
|
||||||
lines,
|
|
||||||
tracing,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if !other_typed_fun.return_type.is_bool() {
|
if !other_typed_fun.return_type.is_bool() {
|
||||||
return Err(Error::ValidatorMustReturnBool {
|
return Err(Error::ValidatorMustReturnBool {
|
||||||
|
@ -338,8 +323,7 @@ fn infer_definition(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let typed_via =
|
let typed_via = ExprTyper::new(environment, tracing).infer(arg.via.clone())?;
|
||||||
ExprTyper::new(environment, lines, tracing).infer(arg.via.clone())?;
|
|
||||||
|
|
||||||
let hydrator: &mut Hydrator = hydrators.get_mut(&f.name).unwrap();
|
let hydrator: &mut Hydrator = hydrators.get_mut(&f.name).unwrap();
|
||||||
|
|
||||||
|
@ -404,14 +388,7 @@ fn infer_definition(
|
||||||
None => Ok((None, None)),
|
None => Ok((None, None)),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let typed_f = infer_function(
|
let typed_f = infer_function(&f.into(), module_name, hydrators, environment, tracing)?;
|
||||||
&f.into(),
|
|
||||||
module_name,
|
|
||||||
hydrators,
|
|
||||||
environment,
|
|
||||||
lines,
|
|
||||||
tracing,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
environment.unify(
|
environment.unify(
|
||||||
typed_f.return_type.clone(),
|
typed_f.return_type.clone(),
|
||||||
|
@ -629,7 +606,7 @@ fn infer_definition(
|
||||||
tipo: _,
|
tipo: _,
|
||||||
}) => {
|
}) => {
|
||||||
let typed_expr =
|
let typed_expr =
|
||||||
ExprTyper::new(environment, lines, tracing).infer_const(&annotation, *value)?;
|
ExprTyper::new(environment, tracing).infer_const(&annotation, *value)?;
|
||||||
|
|
||||||
let tipo = typed_expr.tipo();
|
let tipo = typed_expr.tipo();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue