diff --git a/CHANGELOG.md b/CHANGELOG.md index 2804f18b..606aacd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,15 @@ ### Changed +- **aiken-project**: tests filtering with `-m` during check now happens in `Project::collect_tests` +- **aiken-project**: fixed generation of blueprints for recursive and mutually recursive data-types + - **aiken-lang**: block `Data` and `String` from unifying when casting - **aiken-lang**: remove ability for a type with many variants with matching field labels and types to support field access -- **aiken-project**: tests filtering with `-m` during check now happens in `Project::collect_tests` - **aiken-lang**: various uplc code gen fixes -- **aiken-project**: recursive issue with blueprints fixed - **aiken-lang**: update todo warning to include type +- **aiken-lang**: `|>` operator can now be formatted as a single (short) line or forced over multiline in a flexible manner +- **aiken-lang**: the compiler now provides better feedback for type holes (i.e. `_`) in type annotations ## [v0.0.29] - 2023-MM-DD diff --git a/crates/aiken-lang/src/tipo/environment.rs b/crates/aiken-lang/src/tipo/environment.rs index 2038787a..5ae517fb 100644 --- a/crates/aiken-lang/src/tipo/environment.rs +++ b/crates/aiken-lang/src/tipo/environment.rs @@ -1039,8 +1039,6 @@ impl<'a> Environment<'a> { // Construct type from annotations let mut hydrator = Hydrator::new(); - hydrator.permit_holes(true); - let mut arg_types = Vec::new(); for arg in args { @@ -1098,8 +1096,6 @@ impl<'a> Environment<'a> { // 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()) { diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index a299a1cb..21324ff7 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -568,7 +568,7 @@ You can help me by providing a type-annotation for 'x', as such: , type_ScriptContext = "ScriptContext".if_supports_color(Stdout, |s| s.green()) ))] RecordAccessUnknownType { - #[label] + #[label("annotation needed")] location: Span, }, @@ -642,15 +642,6 @@ Perhaps, try the following: with_spread: bool, }, - // TODO: Seems like we can't really trigger this error because we allow type holes everywhere - // anyway. We need to revise that perhaps. - #[error("I stumbled upon an unexpected type hole.\n")] - #[diagnostic(code("unexpected::type_hole"))] - UnexpectedTypeHole { - #[label] - location: Span, - }, - #[error("I tripped over some unknown labels in a pattern or function.\n")] #[diagnostic(code("unknown::labels"))] UnknownLabels(#[related] Vec), @@ -745,7 +736,7 @@ Perhaps, try the following: #[diagnostic(code("unknown::record_field"))] #[diagnostic(help( "{}", - suggest_neighbor(label, fields.iter(), "Did you forget to make it public?\n\nAlso record access is only supported on types with one constructor.") + suggest_neighbor(label, fields.iter(), "Did you forget to make it public?\nNote also that record access is only supported on types with a single constructor.") ))] UnknownRecordField { #[label] @@ -1271,7 +1262,7 @@ pub enum Warning { help( "If your type has one constructor, unless you are casting {} {}, you can\nprefer using a {} binding like so...\n\n{}", "from".if_supports_color(Stderr, |s| s.bold()), - "Data".if_supports_color(Stderr, |s| s.purple()), + "Data".if_supports_color(Stderr, |s| s.bright_blue()), "let".if_supports_color(Stderr, |s| s.purple()), format_suggestion(sample) ) @@ -1287,15 +1278,18 @@ pub enum Warning { }, #[error("I found a todo left in the code.\n")] - #[diagnostic( - help( - "You probably want to replace that one with real code... eventually. The expected type is {}", - tipo.to_pretty_with_names(HashMap::new(), 0).if_supports_color(Stderr, |s| s.purple()) - ) - )] + #[diagnostic(help("You probably want to replace that with actual code... eventually."))] #[diagnostic(code("todo"))] Todo { - #[label] + #[label("An expression of type {} is expected here.", tipo.to_pretty(0))] + location: Span, + tipo: Arc, + }, + + #[error("I found a type hole in an annotation.\n")] + #[diagnostic(code("unexpected::type_hole"))] + UnexpectedTypeHole { + #[label("{}", tipo.to_pretty(0))] location: Span, tipo: Arc, }, diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index cc123029..c7ccc52f 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1917,12 +1917,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } pub fn new(environment: &'a mut Environment<'b>, tracing: Tracing) -> Self { - let mut hydrator = Hydrator::new(); - - hydrator.permit_holes(true); - Self { - hydrator, + hydrator: Hydrator::new(), environment, tracing, ungeneralised_function_used: false, diff --git a/crates/aiken-lang/src/tipo/hydrator.rs b/crates/aiken-lang/src/tipo/hydrator.rs index 9a4088e8..84f87958 100644 --- a/crates/aiken-lang/src/tipo/hydrator.rs +++ b/crates/aiken-lang/src/tipo/hydrator.rs @@ -3,9 +3,14 @@ use std::{collections::HashMap, sync::Arc}; use crate::{ ast::Annotation, builtins::{function, tuple}, + tipo::Span, }; -use super::{environment::Environment, error::Error, Type, TypeConstructor}; +use super::{ + environment::Environment, + error::{Error, Warning}, + Type, TypeConstructor, +}; /// The Hydrator takes an AST representing a type (i.e. a type annotation /// for a function argument) and returns a Type for that annotation. @@ -28,7 +33,6 @@ pub struct Hydrator { /// annotated name on error. rigid_type_names: HashMap, permit_new_type_variables: bool, - permit_holes: bool, } #[derive(Debug)] @@ -49,7 +53,6 @@ impl Hydrator { created_type_variables: HashMap::new(), rigid_type_names: HashMap::new(), permit_new_type_variables: true, - permit_holes: false, } } @@ -72,10 +75,6 @@ impl Hydrator { self.permit_new_type_variables = false } - pub fn permit_holes(&mut self, flag: bool) { - self.permit_holes = flag - } - /// A rigid type is a generic type that was specified as being generic in /// an annotation. As such it should never be instantiated into an unbound /// variable. @@ -98,12 +97,31 @@ impl Hydrator { } } - /// Construct a Type from an AST Type annotation. - /// pub fn type_from_annotation( &mut self, annotation: &Annotation, environment: &mut Environment, + ) -> Result, Error> { + let mut unbounds = vec![]; + let tipo = self.do_type_from_annotation(annotation, environment, &mut unbounds)?; + + if let Some(location) = unbounds.last() { + environment.warnings.push(Warning::UnexpectedTypeHole { + location: **location, + tipo: tipo.clone(), + }); + } + + Ok(tipo) + } + + /// Construct a Type from an AST Type annotation. + /// + fn do_type_from_annotation<'a>( + &mut self, + annotation: &'a Annotation, + environment: &mut Environment, + unbounds: &mut Vec<&'a Span>, ) -> Result, Error> { match annotation { Annotation::Constructor { @@ -115,7 +133,7 @@ impl Hydrator { // Hydrate the type argument AST into types let mut argument_types = Vec::with_capacity(args.len()); for t in args { - let typ = self.type_from_annotation(t, environment)?; + let typ = self.do_type_from_annotation(t, environment, unbounds)?; argument_types.push((t.location(), typ)); } @@ -170,12 +188,12 @@ impl Hydrator { let mut args = Vec::with_capacity(arguments.len()); for arg in arguments { - let arg = self.type_from_annotation(arg, environment)?; + let arg = self.do_type_from_annotation(arg, environment, unbounds)?; args.push(arg); } - let ret = self.type_from_annotation(ret, environment)?; + let ret = self.do_type_from_annotation(ret, environment, unbounds)?; Ok(function(args, ret)) } @@ -206,17 +224,16 @@ impl Hydrator { }), }, - Annotation::Hole { .. } if self.permit_holes => Ok(environment.new_unbound_var()), - - Annotation::Hole { location, .. } => Err(Error::UnexpectedTypeHole { - location: *location, - }), + Annotation::Hole { location, .. } => { + unbounds.push(location); + Ok(environment.new_unbound_var()) + } Annotation::Tuple { elems, .. } => { let mut typed_elems = vec![]; for elem in elems { - let typed_elem = self.type_from_annotation(elem, environment)?; + let typed_elem = self.do_type_from_annotation(elem, environment, unbounds)?; typed_elems.push(typed_elem) }