Provide better compiler feedback for type holes in annotations.
It is now possible to leave a hole in a type annotation and have the compiler fill-in the expected type of us. This is a pretty useful debugging tool when playing with complex functions.
This commit is contained in:
parent
0e7494541d
commit
d59305a1b0
|
@ -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
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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<UnknownLabels>),
|
||||
|
@ -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<Type>,
|
||||
},
|
||||
|
||||
#[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<Type>,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<u64, String>,
|
||||
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<Arc<Type>, 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<Arc<Type>, 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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue