diff --git a/CHANGELOG.md b/CHANGELOG.md index b292c688c0b..52ae12a9e55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,60 @@ # Changelog +## Unreleased + +### Build tool + +- Now build tool ignores type variables defined in other functions and types, + improving hints in errors. For example: + + ```gleam + pub type Wibble(a, b, c, d, e, f, g) { + Wibble(value: a) + } + + pub fn main() { + let x: Nil = [] + } + ``` + + Previously this code would have emit following error: + + ```text + error: Type mismatch + ┌─ C:\Users\user\projects\test_gleam\src\test_gleam.gleam:6:16 + │ + 6 │ let x: Nil = [] + │ ^^ + + Expected type: + + Nil + + Found type: + + List(h) + ``` + + Now it will emit following error: + + ```text + error: Type mismatch + ┌─ C:\Users\user\projects\test_gleam\src\test_gleam.gleam:6:16 + │ + 6 │ let x: Nil = [] + │ ^^ + + Expected type: + + Nil + + Found type: + + List(a) + ``` + + ([Andrey Kozhev](https://github.com/ankddev)) + ## v1.13.0-rc2 - 2025-10-06 ### Bug fixes diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 9b3fad97134..d8a3a385945 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -1929,7 +1929,7 @@ value has. It seems to be defined in terms of itself.", } TypeError::NotFn { location, type_ } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let text = format!( "This value is being called as a function but its type is:\n\n {}", printer.print_type(type_) @@ -1959,7 +1959,7 @@ value has. It seems to be defined in terms of itself.", fields, unknown_field: variants, } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); // Give a hint about what type this value has. let mut text = format!( @@ -2051,7 +2051,7 @@ to call a method on this value you may want to use the function syntax instead." given, situation: Some(UnifyErrorSituation::Operator(op)), } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let mut text = format!( "The {op} operator expects arguments of this type: @@ -2103,7 +2103,7 @@ But this argument has this type: .and_then(|(arguments, _)| arguments.first().cloned()) .unwrap_or_else(|| given.clone()); - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let text = format!( "The argument is: @@ -2143,7 +2143,7 @@ But function expects: given, situation, } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let mut text = if let Some(description) = situation.as_ref().and_then(|s| s.description()) { @@ -2386,7 +2386,7 @@ specify all fields explicitly instead of using the record update syntax." field_name, .. } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let expected_field_type = printer.print_type(expected_field_type); let record_field_type = printer.print_type(record_field_type); let record_variant = printer.print_type(record_variant); @@ -2530,7 +2530,7 @@ but no type in scope with that name." } TypeError::PrivateTypeLeak { location, leaked } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); // TODO: be more precise. // - is being returned by this public function @@ -2859,7 +2859,7 @@ tuple has {} elements so the highest valid index is {}.", } TypeError::NotATuple { location, given } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let text = format!( "To index into this value it needs to be a tuple, \ however it has this type: @@ -3518,7 +3518,7 @@ Rename or remove one of them.", } TypeError::NotFnInUse { location, type_ } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let text = wrap_format!( "In a use expression, there should be a function on \ the right hand side of `<-`, but this value has type: @@ -3643,7 +3643,7 @@ so it cannot take the `use` callback function as a final argument.\n", location, actual_type: Some(actual), } => { - let mut printer = Printer::new(names); + let mut printer = Printer::new_without_type_variables(names); let text = wrap_format!( "The function on the right hand side of `<-` \ has to take a callback function as its last argument. \ diff --git a/compiler-core/src/type_/tests/errors.rs b/compiler-core/src/type_/tests/errors.rs index 37084a6e083..7a1f2663fbe 100644 --- a/compiler-core/src/type_/tests/errors.rs +++ b/compiler-core/src/type_/tests/errors.rs @@ -3345,3 +3345,66 @@ fn type_used_as_a_constructor_with_more_arguments() { }" ); } + +#[test] +fn errors_type_printing_ignores_type_variables_from_other_function() { + assert_module_error!( + " +pub fn hello(a, b, c, d, e, f) { todo } + +pub fn main() { + let x: Nil = [] +} +" + ); +} + +#[test] +fn errors_type_printing_ignores_type_variables_from_constructor() { + assert_module_error!( + " +pub type Wibble(a, b, c, d, e, f) { + Wibble(value: a) +} + +pub fn main() { + let x: Nil = [] +} +" + ); +} + +#[test] +fn errors_type_printing_considers_type_variables_from_current_function() { + assert_module_error!( + " +pub fn wibble(a, b) { + let x: Nil = [] +} +" + ); +} + +#[test] +fn errors_type_printing_considers_type_variables_from_current_function_with_return_value() { + assert_module_error!( + " +pub fn wibble(a, b) -> a { + let x: Nil = [] +} +" + ); +} + +#[test] +fn errors_type_printing_considers_type_variables_from_current_function_and_ignores_from_other() { + assert_module_error!( + " +pub fn wobble(a, b, c, d, e) -> f { todo } + +pub fn wibble(a, b) -> a { + let x: Nil = [] +} +" + ); +} diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function.snap new file mode 100644 index 00000000000..09c00e4a42d --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/type_/tests/errors.rs +expression: "\npub fn wibble(a, b) {\n let x: Nil = []\n}\n" +--- +----- SOURCE CODE + +pub fn wibble(a, b) { + let x: Nil = [] +} + + +----- ERROR +error: Type mismatch + ┌─ /src/one/two.gleam:3:16 + │ +3 │ let x: Nil = [] + │ ^^ + +Expected type: + + Nil + +Found type: + + List(a) diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_and_ignores_from_other.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_and_ignores_from_other.snap new file mode 100644 index 00000000000..7786f9b8d13 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_and_ignores_from_other.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/type_/tests/errors.rs +expression: "\npub fn wobble(a, b, c, d, e) -> f { todo }\n\npub fn wibble(a, b) -> a {\n let x: Nil = []\n}\n" +--- +----- SOURCE CODE + +pub fn wobble(a, b, c, d, e) -> f { todo } + +pub fn wibble(a, b) -> a { + let x: Nil = [] +} + + +----- ERROR +error: Type mismatch + ┌─ /src/one/two.gleam:5:3 + │ +5 │ let x: Nil = [] + │ ^^^^^^^^^^^^^^^ + +The type of this returned value doesn't match the return type +annotation of this function. + +Expected type: + + a + +Found type: + + List(a) + +error: Type mismatch + ┌─ /src/one/two.gleam:5:16 + │ +5 │ let x: Nil = [] + │ ^^ + +Expected type: + + Nil + +Found type: + + List(a) diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_with_return_value.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_with_return_value.snap new file mode 100644 index 00000000000..726e54ae15a --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_considers_type_variables_from_current_function_with_return_value.snap @@ -0,0 +1,42 @@ +--- +source: compiler-core/src/type_/tests/errors.rs +expression: "\npub fn wibble(a, b) -> a {\n let x: Nil = []\n}\n" +--- +----- SOURCE CODE + +pub fn wibble(a, b) -> a { + let x: Nil = [] +} + + +----- ERROR +error: Type mismatch + ┌─ /src/one/two.gleam:3:3 + │ +3 │ let x: Nil = [] + │ ^^^^^^^^^^^^^^^ + +The type of this returned value doesn't match the return type +annotation of this function. + +Expected type: + + a + +Found type: + + List(a) + +error: Type mismatch + ┌─ /src/one/two.gleam:3:16 + │ +3 │ let x: Nil = [] + │ ^^ + +Expected type: + + Nil + +Found type: + + List(a) diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_constructor.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_constructor.snap new file mode 100644 index 00000000000..e9510163388 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_constructor.snap @@ -0,0 +1,29 @@ +--- +source: compiler-core/src/type_/tests/errors.rs +expression: "\npub type Wibble(a, b, c, d, e, f) {\n Wibble(value: a)\n}\n\npub fn main() {\n let x: Nil = []\n}\n" +--- +----- SOURCE CODE + +pub type Wibble(a, b, c, d, e, f) { + Wibble(value: a) +} + +pub fn main() { + let x: Nil = [] +} + + +----- ERROR +error: Type mismatch + ┌─ /src/one/two.gleam:7:16 + │ +7 │ let x: Nil = [] + │ ^^ + +Expected type: + + Nil + +Found type: + + List(a) diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_other_function.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_other_function.snap new file mode 100644 index 00000000000..edfdc7bd9af --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__errors_type_printing_ignores_type_variables_from_other_function.snap @@ -0,0 +1,27 @@ +--- +source: compiler-core/src/type_/tests/errors.rs +expression: "\npub fn hello(a, b, c, d, e, f) { todo }\n\npub fn main() {\n let x: Nil = []\n}\n" +--- +----- SOURCE CODE + +pub fn hello(a, b, c, d, e, f) { todo } + +pub fn main() { + let x: Nil = [] +} + + +----- ERROR +error: Type mismatch + ┌─ /src/one/two.gleam:5:16 + │ +5 │ let x: Nil = [] + │ ^^ + +Expected type: + + Nil + +Found type: + + List(a)