diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 20e7b373..97c48267 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1957,6 +1957,10 @@ pub enum TraceLevel { } impl Tracing { + pub fn verbose() -> Self { + Tracing::All(TraceLevel::Verbose) + } + pub fn silent() -> Self { Tracing::All(TraceLevel::Silent) } diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index b157d0ce..58d971b4 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -18,6 +18,7 @@ fn check_module( ast: UntypedModule, extra: Vec<(String, UntypedModule)>, kind: ModuleKind, + tracing: Tracing, ) -> Result<(Vec, TypedModule), (Vec, Error)> { let id_gen = IdGenerator::new(); @@ -47,7 +48,7 @@ fn check_module( kind, "test/project", &module_types, - Tracing::All(TraceLevel::Verbose), + tracing, &mut warnings, ); @@ -57,20 +58,27 @@ fn check_module( } fn check(ast: UntypedModule) -> Result<(Vec, TypedModule), (Vec, 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, TypedModule), (Vec, Error)> { + check_module(ast, Vec::new(), ModuleKind::Lib, Tracing::All(level)) } fn check_with_deps( ast: UntypedModule, extra: Vec<(String, UntypedModule)>, ) -> Result<(Vec, TypedModule), (Vec, Error)> { - check_module(ast, extra, ModuleKind::Lib) + check_module(ast, extra, ModuleKind::Lib, Tracing::verbose()) } fn check_validator( ast: UntypedModule, ) -> Result<(Vec, TypedModule), (Vec, Error)> { - check_module(ast, Vec::new(), ModuleKind::Validator) + check_module(ast, Vec::new(), ModuleKind::Validator, Tracing::verbose()) } #[test] @@ -1288,8 +1296,32 @@ fn trace_non_strings() { 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!( - check(parse(source_code)), + check_with_verbosity(parse(source_code), TraceLevel::Compact), Err((_, Error::CouldNotUnify { .. })) )) } diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index e0d84d68..83fc492c 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -24,7 +24,6 @@ use crate::{ }, expr::{FnStyle, TypedExpr, UntypedExpr}, format, - line_numbers::LineNumbers, tipo::{fields::FieldMap, DefaultFunction, PatternConstructor, TypeVar}, IdGenerator, }; @@ -41,7 +40,6 @@ pub(crate) fn infer_function( module_name: &str, hydrators: &mut HashMap, environment: &mut Environment<'_>, - lines: &LineNumbers, tracing: Tracing, ) -> Result, TypedExpr, TypedArg>, Error> { if let Some(typed_fun) = environment.inferred_functions.get(&fun.name) { @@ -122,7 +120,7 @@ pub(crate) fn infer_function( .remove(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.not_yet_inferred = BTreeSet::from_iter(hydrators.keys().cloned()); @@ -155,12 +153,11 @@ pub(crate) fn infer_function( environment.current_module, hydrators, environment, - lines, tracing, )?; // 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?; @@ -223,8 +220,6 @@ pub(crate) fn infer_function( #[derive(Debug)] pub(crate) struct ExprTyper<'a, 'b> { - pub(crate) lines: &'a LineNumbers, - pub(crate) environment: &'a mut Environment<'b>, // 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> { - pub fn new( - environment: &'a mut Environment<'b>, - lines: &'a LineNumbers, - tracing: Tracing, - ) -> Self { + pub fn new(environment: &'a mut Environment<'b>, tracing: Tracing) -> Self { Self { hydrator: Hydrator::new(), not_yet_inferred: BTreeSet::new(), environment, tracing, ungeneralised_function_used: false, - lines, } } @@ -681,25 +671,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .to_pretty_string(999) ), }), - TraceLevel::Compact => Some(TypedExpr::String { - location, - tipo: string(), - value: self - .lines - .line_and_column_number(location.start) - .expect("Spans are within bounds.") - .to_string(), - }), - TraceLevel::Silent => None, + TraceLevel::Compact | TraceLevel::Silent => None, }; let typed_value = self.infer(value)?; self.unify(bool(), typed_value.tipo(), typed_value.location(), false)?; - match self.tracing.trace_level(false) { - TraceLevel::Silent => Ok(typed_value), - TraceLevel::Verbose | TraceLevel::Compact => Ok(TypedExpr::If { + match text { + None => Ok(typed_value), + Some(text) => Ok(TypedExpr::If { location, branches: vec1::vec1![IfBranch { condition: typed_value, @@ -710,7 +691,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { final_else: Box::new(TypedExpr::Trace { location, tipo: bool(), - text: Box::new(text.expect("TraceLevel::Silent excluded from pattern-guard")), + text: Box::new(text), then: Box::new(var_false), }), tipo: bool(), @@ -2426,29 +2407,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> { label: UntypedExpr, arguments: Vec, ) -> Result { - let label = self.infer_trace_arg(label)?; - let typed_arguments = arguments .into_iter() .map(|arg| self.infer_trace_arg(arg)) .collect::, 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 tipo = then.tipo(); @@ -2461,26 +2424,42 @@ impl<'a, 'b> ExprTyper<'a, 'b> { match self.tracing.trace_level(false) { TraceLevel::Silent => Ok(then), - TraceLevel::Compact => Ok(TypedExpr::Trace { - location, - tipo, - then: Box::new(then), - text: Box::new(TypedExpr::String { + TraceLevel::Compact => { + let text = self.infer(label)?; + self.unify(string(), text.tipo(), text.location(), false)?; + Ok(TypedExpr::Trace { location, - tipo: string(), - value: self - .lines - .line_and_column_number(location.start) - .expect("Spans are within bounds.") - .to_string(), - }), - }), - TraceLevel::Verbose => Ok(TypedExpr::Trace { - location, - tipo, - then: Box::new(then), - text: Box::new(text), - }), + tipo, + then: Box::new(then), + text: Box::new(text), + }) + } + TraceLevel::Verbose => { + let label = self.infer_trace_arg(label)?; + + 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) + }) + }; + + Ok(TypedExpr::Trace { + location, + tipo, + then: Box::new(then), + text: Box::new(text), + }) + } } } diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 075aa774..b9a35b0e 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -13,7 +13,6 @@ use crate::{ }, builtins, builtins::{fuzzer, generic_var}, - line_numbers::LineNumbers, tipo::{expr::infer_function, Span, Type, TypeVar}, IdGenerator, }; @@ -86,14 +85,8 @@ impl UntypedModule { } for def in consts.into_iter().chain(not_consts) { - let definition = infer_definition( - def, - &module_name, - &mut hydrators, - &mut environment, - &self.lines, - tracing, - )?; + let definition = + infer_definition(def, &module_name, &mut hydrators, &mut environment, tracing)?; definitions.push(definition); } @@ -162,7 +155,6 @@ fn infer_definition( module_name: &String, hydrators: &mut HashMap, environment: &mut Environment<'_>, - lines: &LineNumbers, tracing: Tracing, ) -> Result { match def { @@ -171,7 +163,6 @@ fn infer_definition( module_name, hydrators, environment, - lines, tracing, )?)), @@ -228,7 +219,7 @@ fn infer_definition( } 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() { return Err(Error::ValidatorMustReturnBool { @@ -267,14 +258,8 @@ fn infer_definition( let params = params.into_iter().chain(other.arguments); other.arguments = params.collect(); - let mut other_typed_fun = infer_function( - &other, - module_name, - hydrators, - environment, - lines, - tracing, - )?; + let mut other_typed_fun = + infer_function(&other, module_name, hydrators, environment, tracing)?; if !other_typed_fun.return_type.is_bool() { return Err(Error::ValidatorMustReturnBool { @@ -338,8 +323,7 @@ fn infer_definition( }); } - let typed_via = - ExprTyper::new(environment, lines, tracing).infer(arg.via.clone())?; + let typed_via = ExprTyper::new(environment, tracing).infer(arg.via.clone())?; let hydrator: &mut Hydrator = hydrators.get_mut(&f.name).unwrap(); @@ -404,14 +388,7 @@ fn infer_definition( None => Ok((None, None)), }?; - let typed_f = infer_function( - &f.into(), - module_name, - hydrators, - environment, - lines, - tracing, - )?; + let typed_f = infer_function(&f.into(), module_name, hydrators, environment, tracing)?; environment.unify( typed_f.return_type.clone(), @@ -629,7 +606,7 @@ fn infer_definition( tipo: _, }) => { 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();