feat: better rules around Data casting
* you cannot cast to Data ever * you can cast from Data to ANY TYPE * you cannot cast via a function call arg hack
This commit is contained in:
parent
0ee083cc9a
commit
39e0716f5f
|
@ -29,14 +29,6 @@ pub struct ScopeResetData {
|
||||||
local_values: HashMap<String, ValueConstructor>,
|
local_values: HashMap<String, ValueConstructor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXCLUDE_DATA_UNIFY: [&str; 5] = [
|
|
||||||
builtins::INT,
|
|
||||||
builtins::BYTE_ARRAY,
|
|
||||||
builtins::STRING,
|
|
||||||
builtins::BOOL,
|
|
||||||
builtins::LIST,
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Environment<'a> {
|
pub struct Environment<'a> {
|
||||||
/// Accessors defined in the current module
|
/// Accessors defined in the current module
|
||||||
|
@ -1218,16 +1210,8 @@ impl<'a> Environment<'a> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Type::App { name: name1, .. }, Type::App { name: name2, .. }) =
|
if t2.is_data() {
|
||||||
(t1.deref(), t2.deref())
|
return Ok(());
|
||||||
{
|
|
||||||
if name1 == "Data" && !EXCLUDE_DATA_UNIFY.contains(&name2.as_str()) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if name2 == "Data" && !EXCLUDE_DATA_UNIFY.contains(&name1.as_str()) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collapse right hand side type links. Left hand side will be collapsed in the next block.
|
// Collapse right hand side type links. Left hand side will be collapsed in the next block.
|
||||||
|
|
|
@ -52,6 +52,39 @@ impl Diagnostic for UnknownLabels {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Diagnostic)]
|
#[derive(Debug, thiserror::Error, Diagnostic)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("I discovered a type cast from Data without an annotation")]
|
||||||
|
#[diagnostic(code("illegal::type_cast"))]
|
||||||
|
#[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
|
||||||
|
CastDataNoAnn {
|
||||||
|
#[label("missing annotation")]
|
||||||
|
location: Span,
|
||||||
|
value: UntypedExpr,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I struggled to unify the types of two expressions.\n")]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types"))]
|
||||||
|
#[diagnostic(code("type_mismatch"))]
|
||||||
|
#[diagnostic(help("{}", suggest_unify(expected, given, situation, rigid_type_names)))]
|
||||||
|
CouldNotUnify {
|
||||||
|
#[label(
|
||||||
|
"expected type '{}'",
|
||||||
|
expected.to_pretty_with_names(rigid_type_names.clone(), 0),
|
||||||
|
)]
|
||||||
|
location: Span,
|
||||||
|
expected: Arc<Type>,
|
||||||
|
given: Arc<Type>,
|
||||||
|
situation: Option<UnifyErrorSituation>,
|
||||||
|
rigid_type_names: HashMap<u64, String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I almost got caught in an infinite cycle of type definitions.\n")]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#type-aliases"))]
|
||||||
|
#[diagnostic(code("cycle"))]
|
||||||
|
CyclicTypeDefinitions {
|
||||||
|
#[related]
|
||||||
|
errors: Vec<Snippet>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I found two function arguments both called '{}'.\n", label.purple())]
|
#[error("I found two function arguments both called '{}'.\n", label.purple())]
|
||||||
#[diagnostic(code("duplicate::argument"))]
|
#[diagnostic(code("duplicate::argument"))]
|
||||||
#[diagnostic(help("Function arguments cannot have the same name. You can use '{discard}' and numbers to distinguish between similar names."
|
#[diagnostic(help("Function arguments cannot have the same name. You can use '{discard}' and numbers to distinguish between similar names."
|
||||||
|
@ -78,17 +111,6 @@ pub enum Error {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I noticed you were importing '{}' twice.\n", name.purple())]
|
|
||||||
#[diagnostic(code("duplicate::import"))]
|
|
||||||
#[diagnostic(help("The best thing to do from here is to remove one of them."))]
|
|
||||||
DuplicateImport {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
#[label]
|
|
||||||
previous_location: Span,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I stumbled upon the field '{}' twice in a data-type definition.\n", label.purple())]
|
#[error("I stumbled upon the field '{}' twice in a data-type definition.\n", label.purple())]
|
||||||
#[diagnostic(code("duplicate::field"))]
|
#[diagnostic(code("duplicate::field"))]
|
||||||
#[diagnostic(help(r#"Data-types must have fields with strictly different names. You can use '{discard}' and numbers to distinguish between similar names.
|
#[diagnostic(help(r#"Data-types must have fields with strictly different names. You can use '{discard}' and numbers to distinguish between similar names.
|
||||||
|
@ -116,6 +138,17 @@ For example:
|
||||||
label: String,
|
label: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I noticed you were importing '{}' twice.\n", name.purple())]
|
||||||
|
#[diagnostic(code("duplicate::import"))]
|
||||||
|
#[diagnostic(help("The best thing to do from here is to remove one of them."))]
|
||||||
|
DuplicateImport {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
#[label]
|
||||||
|
previous_location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I discovered two top-level objects referred to as '{}'.\n", name.purple())]
|
#[error("I discovered two top-level objects referred to as '{}'.\n", name.purple())]
|
||||||
#[diagnostic(code("duplicate::name"))]
|
#[diagnostic(code("duplicate::name"))]
|
||||||
#[diagnostic(help(r#"Top-level definitions cannot have the same name, even if they refer to objects with different natures (e.g. function and test).
|
#[diagnostic(help(r#"Top-level definitions cannot have the same name, even if they refer to objects with different natures (e.g. function and test).
|
||||||
|
@ -145,6 +178,28 @@ You can use '{discard}' and numbers to distinguish between similar names.
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I realized the variable '{}' was mentioned more than once in an alternative pattern.\n", name.purple())]
|
||||||
|
#[diagnostic(url(
|
||||||
|
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
||||||
|
))]
|
||||||
|
#[diagnostic(code("duplicate::pattern"))]
|
||||||
|
DuplicateVarInPattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I tripped over an extra variable in an alternative pattern: {}.\n", name.purple())]
|
||||||
|
#[diagnostic(url(
|
||||||
|
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
||||||
|
))]
|
||||||
|
#[diagnostic(code("unexpected::variable"))]
|
||||||
|
ExtraVarInAlternativePattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I found a data type that has a function type in it. This is not allowed.\n")]
|
#[error("I found a data type that has a function type in it. This is not allowed.\n")]
|
||||||
#[diagnostic(code("illegal::function_in_type"))]
|
#[diagnostic(code("illegal::function_in_type"))]
|
||||||
#[diagnostic(help("Data-types can't hold functions. If you want to define method-like functions, group the type definition and the methods under a common namespace in a standalone module."))]
|
#[diagnostic(help("Data-types can't hold functions. If you want to define method-like functions, group the type definition and the methods under a common namespace in a standalone module."))]
|
||||||
|
@ -164,22 +219,6 @@ You can use '{discard}' and numbers to distinguish between similar names.
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I discovered a function which is ending with an assignment.\n")]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#named-functions"))]
|
|
||||||
#[diagnostic(code("illegal::return"))]
|
|
||||||
#[diagnostic(help(r#"In Aiken, functions must return an explicit result in the form of an expression. While assignments are technically speaking expressions, they aren't allowed to be the last expression of a function because they convey a different meaning and this could be error-prone.
|
|
||||||
|
|
||||||
If you really meant to return that last expression, try to replace it with the following:
|
|
||||||
|
|
||||||
{sample}"#
|
|
||||||
, sample = format_suggestion(expr)
|
|
||||||
))]
|
|
||||||
LastExpressionIsAssignment {
|
|
||||||
#[label("let-binding as last expression")]
|
|
||||||
location: Span,
|
|
||||||
expr: expr::UntypedExpr,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I saw a {} fields in a context where there should be {}.\n", given.purple(), expected.purple())]
|
#[error("I saw a {} fields in a context where there should be {}.\n", given.purple(), expected.purple())]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types"))]
|
||||||
#[diagnostic(code("arity::constructor"))]
|
#[diagnostic(code("arity::constructor"))]
|
||||||
|
@ -223,6 +262,18 @@ From there, you can define 'increment', a function that takes a single argument
|
||||||
given: usize,
|
given: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: Since we do not actually support patterns on multiple items, we won't likely ever
|
||||||
|
// encounter that error. We could simplify a bit the type-checker and get rid of that error
|
||||||
|
// eventually.
|
||||||
|
#[error("I counted {} different clauses in a multi-pattern instead of {}.\n", given.purple(), expected.purple())]
|
||||||
|
#[diagnostic(code("arity::clause"))]
|
||||||
|
IncorrectNumClausePatterns {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
expected: usize,
|
||||||
|
given: usize,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I saw a pattern on a constructor that has {} field(s) be matched with {} argument(s).\n", expected.purple(), given.len().purple())]
|
#[error("I saw a pattern on a constructor that has {} field(s) be matched with {} argument(s).\n", expected.purple(), given.len().purple())]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#matching"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#matching"))]
|
||||||
#[diagnostic(code("arity::pattern"))]
|
#[diagnostic(code("arity::pattern"))]
|
||||||
|
@ -240,18 +291,6 @@ From there, you can define 'increment', a function that takes a single argument
|
||||||
is_record: bool,
|
is_record: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Since we do not actually support patterns on multiple items, we won't likely ever
|
|
||||||
// encounter that error. We could simplify a bit the type-checker and get rid of that error
|
|
||||||
// eventually.
|
|
||||||
#[error("I counted {} different clauses in a multi-pattern instead of {}.\n", given.purple(), expected.purple())]
|
|
||||||
#[diagnostic(code("arity::clause"))]
|
|
||||||
IncorrectNumClausePatterns {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
expected: usize,
|
|
||||||
given: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I saw a pattern on a {}-tuple be matched into a {}-tuple.\n", expected.purple(), given.purple())]
|
#[error("I saw a pattern on a {}-tuple be matched into a {}-tuple.\n", expected.purple(), given.purple())]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#destructuring"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#destructuring"))]
|
||||||
#[diagnostic(code("arity::tuple"))]
|
#[diagnostic(code("arity::tuple"))]
|
||||||
|
@ -285,6 +324,71 @@ Perhaps, try the following:
|
||||||
given: usize,
|
given: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"I realized the module '{}' contains the keyword '{}', which is forbidden.\n",
|
||||||
|
name.purple(),
|
||||||
|
keyword.purple()
|
||||||
|
)]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/modules"))]
|
||||||
|
#[diagnostic(code("illegal::module_name"))]
|
||||||
|
#[diagnostic(help(r#"You cannot use keywords as part of a module path name. As a quick reminder, here's a list of all the keywords (and thus, of invalid module path names):
|
||||||
|
|
||||||
|
as, assert, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))]
|
||||||
|
KeywordInModuleName { name: String, keyword: String },
|
||||||
|
|
||||||
|
#[error("I discovered a function which is ending with an assignment.\n")]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#named-functions"))]
|
||||||
|
#[diagnostic(code("illegal::return"))]
|
||||||
|
#[diagnostic(help(r#"In Aiken, functions must return an explicit result in the form of an expression. While assignments are technically speaking expressions, they aren't allowed to be the last expression of a function because they convey a different meaning and this could be error-prone.
|
||||||
|
|
||||||
|
If you really meant to return that last expression, try to replace it with the following:
|
||||||
|
|
||||||
|
{sample}"#
|
||||||
|
, sample = format_suggestion(expr)
|
||||||
|
))]
|
||||||
|
LastExpressionIsAssignment {
|
||||||
|
#[label("let-binding as last expression")]
|
||||||
|
location: Span,
|
||||||
|
expr: expr::UntypedExpr,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I found a missing variable in an alternative pattern: {}.\n", name.purple())]
|
||||||
|
#[diagnostic(url(
|
||||||
|
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
||||||
|
))]
|
||||||
|
#[diagnostic(code("missing::variable"))]
|
||||||
|
MissingVarInAlternativePattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I stumbled upon an invalid (non-local) clause guard '{}'.\n", name.purple())]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"))]
|
||||||
|
#[diagnostic(code("illegal::clause_guard"))]
|
||||||
|
#[diagnostic(help("There are some conditions regarding what can be used in a guard. Values must be either local to the function, or defined as module constants. You can't use functions or records in there."))]
|
||||||
|
NonLocalClauseGuardVariable {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"I tripped over an attempt to access tuple elements on something else than a tuple.\n"
|
||||||
|
)]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#tuples"))]
|
||||||
|
#[diagnostic(code("illegal::tuple_index"))]
|
||||||
|
#[diagnostic(help(r#"Because you used a tuple-index on an element, I assumed it had to be a tuple or some kind, but instead I found:
|
||||||
|
|
||||||
|
╰─▶ {type_info}"#
|
||||||
|
, type_info = tipo.to_pretty(4).red()
|
||||||
|
))]
|
||||||
|
NotATuple {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I realized that a given 'when/is' expression is non-exhaustive.\n")]
|
#[error("I realized that a given 'when/is' expression is non-exhaustive.\n")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#matching"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#matching"))]
|
||||||
#[diagnostic(code("non_exhaustive_pattern_match"))]
|
#[diagnostic(code("non_exhaustive_pattern_match"))]
|
||||||
|
@ -319,28 +423,6 @@ In this particular instance, the following cases are missing:
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(
|
|
||||||
"I realized the module '{}' contains the keyword '{}', which is forbidden.\n",
|
|
||||||
name.purple(),
|
|
||||||
keyword.purple()
|
|
||||||
)]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/modules"))]
|
|
||||||
#[diagnostic(code("illegal::module_name"))]
|
|
||||||
#[diagnostic(help(r#"You cannot use keywords as part of a module path name. As a quick reminder, here's a list of all the keywords (and thus, of invalid module path names):
|
|
||||||
|
|
||||||
as, assert, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))]
|
|
||||||
KeywordInModuleName { name: String, keyword: String },
|
|
||||||
|
|
||||||
#[error("I stumbled upon an invalid (non-local) clause guard '{}'.\n", name.purple())]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"))]
|
|
||||||
#[diagnostic(code("illegal::clause_guard"))]
|
|
||||||
#[diagnostic(help("There are some conditions regarding what can be used in a guard. Values must be either local to the function, or defined as module constants. You can't use functions or records in there."))]
|
|
||||||
NonLocalClauseGuardVariable {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I discovered a positional argument after a label argument.\n")]
|
#[error("I discovered a positional argument after a label argument.\n")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#labeled-arguments"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#labeled-arguments"))]
|
||||||
#[diagnostic(code("unexpected::positional_argument"))]
|
#[diagnostic(code("unexpected::positional_argument"))]
|
||||||
|
@ -407,6 +489,15 @@ You can help me by providing a type-annotation for 'x', as such:
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I almost got caught in an endless loop while inferring a recursive type.\n")]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#type-annotations"))]
|
||||||
|
#[diagnostic(code("missing::type_annotation"))]
|
||||||
|
#[diagnostic(help("I have several aptitudes, but inferring recursive types isn't one them. It is still possible to define recursive types just fine, but I will need a little help in the form of type annotation to infer their types should they show up."))]
|
||||||
|
RecursiveType {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I realized you used '{}' as a module name, which is reserved (and not available).\n", name.purple())]
|
#[error("I realized you used '{}' as a module name, which is reserved (and not available).\n", name.purple())]
|
||||||
#[diagnostic(code("illegal::module_name"))]
|
#[diagnostic(code("illegal::module_name"))]
|
||||||
#[diagnostic(help(r#"Some module names are reserved for internal use. This the case of:
|
#[diagnostic(help(r#"Some module names are reserved for internal use. This the case of:
|
||||||
|
@ -418,6 +509,20 @@ Note that 'aiken' is also imported by default; but you can refer to it explicitl
|
||||||
))]
|
))]
|
||||||
ReservedModuleName { name: String },
|
ReservedModuleName { name: String },
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"I discovered an attempt to access the {} element of a {}-tuple.\n",
|
||||||
|
Ordinal(*index + 1).to_string().purple(),
|
||||||
|
size.purple()
|
||||||
|
)]
|
||||||
|
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#tuples"))]
|
||||||
|
#[diagnostic(code("invalid::tuple_index"))]
|
||||||
|
TupleIndexOutOfBound {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
index: usize,
|
||||||
|
size: usize,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I tripped over the following labeled argument: {}.\n", label.purple())]
|
#[error("I tripped over the following labeled argument: {}.\n", label.purple())]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#labeled-arguments"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#labeled-arguments"))]
|
||||||
#[diagnostic(code("unexpected::module_name"))]
|
#[diagnostic(code("unexpected::module_name"))]
|
||||||
|
@ -498,24 +603,6 @@ Perhaps, try the following:
|
||||||
type_constructors: Vec<String>,
|
type_constructors: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I looked for '{}' in '{}' but couldn't find it.\n", name.purple(), module_name.purple())]
|
|
||||||
#[diagnostic(code("unknown::module_value"))]
|
|
||||||
#[diagnostic(help(
|
|
||||||
"{}",
|
|
||||||
suggest_neighbor(
|
|
||||||
name,
|
|
||||||
value_constructors.iter(),
|
|
||||||
&suggest_make_public()
|
|
||||||
)
|
|
||||||
))]
|
|
||||||
UnknownModuleValue {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
module_name: String,
|
|
||||||
value_constructors: Vec<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I looked for '{}' in '{}' but couldn't find it.\n", name.purple(), module_name.purple())]
|
#[error("I looked for '{}' in '{}' but couldn't find it.\n", name.purple(), module_name.purple())]
|
||||||
#[diagnostic(code("unknown::module_type"))]
|
#[diagnostic(code("unknown::module_type"))]
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
|
@ -534,6 +621,24 @@ Perhaps, try the following:
|
||||||
type_constructors: Vec<String>,
|
type_constructors: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I looked for '{}' in '{}' but couldn't find it.\n", name.purple(), module_name.purple())]
|
||||||
|
#[diagnostic(code("unknown::module_value"))]
|
||||||
|
#[diagnostic(help(
|
||||||
|
"{}",
|
||||||
|
suggest_neighbor(
|
||||||
|
name,
|
||||||
|
value_constructors.iter(),
|
||||||
|
&suggest_make_public()
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
UnknownModuleValue {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
module_name: String,
|
||||||
|
value_constructors: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
"I looked for the field '{}' in a record of type '{}' couldn't find it.\n",
|
"I looked for the field '{}' in a record of type '{}' couldn't find it.\n",
|
||||||
label.purple(),
|
label.purple(),
|
||||||
|
@ -566,6 +671,19 @@ Perhaps, try the following:
|
||||||
types: Vec<String>,
|
types: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I found a reference to an unknown data-type constructor: '{}'.\n", name.purple())]
|
||||||
|
#[diagnostic(code("unknown::type_constructor"))]
|
||||||
|
#[diagnostic(help(
|
||||||
|
"{}",
|
||||||
|
suggest_neighbor(name, constructors.iter(), "Did you forget to import it?")
|
||||||
|
))]
|
||||||
|
UnknownTypeConstructor {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
constructors: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I found a reference to an unknown variable.\n")]
|
#[error("I found a reference to an unknown variable.\n")]
|
||||||
#[diagnostic(code("unknown::variable"))]
|
#[diagnostic(code("unknown::variable"))]
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
|
@ -587,19 +705,6 @@ Perhaps, try the following:
|
||||||
variables: Vec<String>,
|
variables: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I found a reference to an unknown data-type constructor: '{}'.\n", name.purple())]
|
|
||||||
#[diagnostic(code("unknown::type_constructor"))]
|
|
||||||
#[diagnostic(help(
|
|
||||||
"{}",
|
|
||||||
suggest_neighbor(name, constructors.iter(), "Did you forget to import it?")
|
|
||||||
))]
|
|
||||||
UnknownTypeConstructor {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
constructors: Vec<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I discovered a redundant spread operator.\n")]
|
#[error("I discovered a redundant spread operator.\n")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#destructuring"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#destructuring"))]
|
||||||
#[diagnostic(code("unexpected::spread_operator"))]
|
#[diagnostic(code("unexpected::spread_operator"))]
|
||||||
|
@ -620,102 +725,6 @@ The best thing to do from here is to remove it."#))]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I struggled to unify the types of two expressions.\n")]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types"))]
|
|
||||||
#[diagnostic(code("type_mismatch"))]
|
|
||||||
#[diagnostic(help("{}", suggest_unify(expected, given, situation, rigid_type_names)))]
|
|
||||||
CouldNotUnify {
|
|
||||||
#[label(
|
|
||||||
"expected type '{}'",
|
|
||||||
expected.to_pretty_with_names(rigid_type_names.clone(), 0),
|
|
||||||
)]
|
|
||||||
location: Span,
|
|
||||||
expected: Arc<Type>,
|
|
||||||
given: Arc<Type>,
|
|
||||||
situation: Option<UnifyErrorSituation>,
|
|
||||||
rigid_type_names: HashMap<u64, String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I tripped over an extra variable in an alternative pattern: {}.\n", name.purple())]
|
|
||||||
#[diagnostic(url(
|
|
||||||
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
|
||||||
))]
|
|
||||||
#[diagnostic(code("unexpected::variable"))]
|
|
||||||
ExtraVarInAlternativePattern {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I found a missing variable in an alternative pattern: {}.\n", name.purple())]
|
|
||||||
#[diagnostic(url(
|
|
||||||
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
|
||||||
))]
|
|
||||||
#[diagnostic(code("missing::variable"))]
|
|
||||||
MissingVarInAlternativePattern {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I realized the variable '{}' was mentioned more than once in an alternative pattern.\n", name.purple())]
|
|
||||||
#[diagnostic(url(
|
|
||||||
"https://aiken-lang.org/language-tour/control-flow#alternative-clause-patterns"
|
|
||||||
))]
|
|
||||||
#[diagnostic(code("duplicate::pattern"))]
|
|
||||||
DuplicateVarInPattern {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I almost got caught in an infinite cycle of type definitions.\n")]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#type-aliases"))]
|
|
||||||
#[diagnostic(code("cycle"))]
|
|
||||||
CyclicTypeDefinitions {
|
|
||||||
#[related]
|
|
||||||
errors: Vec<Snippet>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I almost got caught in an endless loop while inferring a recursive type.\n")]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#type-annotations"))]
|
|
||||||
#[diagnostic(code("missing::type_annotation"))]
|
|
||||||
#[diagnostic(help("I have several aptitudes, but inferring recursive types isn't one them. It is still possible to define recursive types just fine, but I will need a little help in the form of type annotation to infer their types should they show up."))]
|
|
||||||
RecursiveType {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error(
|
|
||||||
"I tripped over an attempt to access tuple elements on something else than a tuple.\n"
|
|
||||||
)]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#tuples"))]
|
|
||||||
#[diagnostic(code("illegal::tuple_index"))]
|
|
||||||
#[diagnostic(help(r#"Because you used a tuple-index on an element, I assumed it had to be a tuple or some kind, but instead I found:
|
|
||||||
|
|
||||||
╰─▶ {type_info}"#
|
|
||||||
, type_info = tipo.to_pretty(4).red()
|
|
||||||
))]
|
|
||||||
NotATuple {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
tipo: Arc<Type>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error(
|
|
||||||
"I discovered an attempt to access the {} element of a {}-tuple.\n",
|
|
||||||
Ordinal(*index + 1).to_string().purple(),
|
|
||||||
size.purple()
|
|
||||||
)]
|
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#tuples"))]
|
|
||||||
#[diagnostic(code("invalid::tuple_index"))]
|
|
||||||
TupleIndexOutOfBound {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
index: usize,
|
|
||||||
size: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("I discovered an attempt to import a validator module: '{}'\n", name.purple())]
|
#[error("I discovered an attempt to import a validator module: '{}'\n", name.purple())]
|
||||||
#[diagnostic(code("illegal::import"))]
|
#[diagnostic(code("illegal::import"))]
|
||||||
#[diagnostic(help("If you are trying to share code defined in a validator then move it to a library module under {}", "lib/".purple()))]
|
#[diagnostic(help("If you are trying to share code defined in a validator then move it to a library module under {}", "lib/".purple()))]
|
||||||
|
@ -724,15 +733,6 @@ The best thing to do from here is to remove it."#))]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I discovered a type cast from Data without an annotation")]
|
|
||||||
#[diagnostic(code("illegal::type_cast"))]
|
|
||||||
#[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
|
|
||||||
CastDataNoAnn {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
value: UntypedExpr,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -951,6 +951,19 @@ fn suggest_unify(
|
||||||
expected.green(),
|
expected.green(),
|
||||||
given.red()
|
given.red()
|
||||||
},
|
},
|
||||||
|
Some(UnifyErrorSituation::UnsafeCast) => formatdoc! {
|
||||||
|
r#"I am inferring the following type:
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
but I found an expression with a different type:
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
It is unsafe to cast Data without using assert"#,
|
||||||
|
expected.green(),
|
||||||
|
given.red()
|
||||||
|
},
|
||||||
None => formatdoc! {
|
None => formatdoc! {
|
||||||
r#"I am inferring the following type:
|
r#"I am inferring the following type:
|
||||||
|
|
||||||
|
@ -1187,6 +1200,9 @@ pub enum UnifyErrorSituation {
|
||||||
|
|
||||||
/// The operands of a binary operator were incorrect.
|
/// The operands of a binary operator were incorrect.
|
||||||
Operator(BinOp),
|
Operator(BinOp),
|
||||||
|
|
||||||
|
/// Called a function with something of type Data but something else was expected
|
||||||
|
UnsafeCast,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
|
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
|
||||||
error::{Error, Warning},
|
error::{Error, UnifyErrorSituation, Warning},
|
||||||
hydrator::Hydrator,
|
hydrator::Hydrator,
|
||||||
pattern::PatternTyper,
|
pattern::PatternTyper,
|
||||||
pipe::PipeTyper,
|
pipe::PipeTyper,
|
||||||
|
@ -941,7 +941,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
(_, value) => self.infer(value),
|
(_, value) => self.infer(value),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
self.unify(tipo, value.tipo(), value.location())?;
|
self.unify(tipo.clone(), value.tipo(), value.location())?;
|
||||||
|
|
||||||
|
if value.tipo().is_data() && !tipo.is_data() {
|
||||||
|
return Err(Error::CouldNotUnify {
|
||||||
|
location: value.location(),
|
||||||
|
expected: tipo,
|
||||||
|
given: value.tipo(),
|
||||||
|
situation: Some(UnifyErrorSituation::UnsafeCast),
|
||||||
|
rigid_type_names: HashMap::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue