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
	
	 rvcas
						rvcas