Skip to content

Commit 15e50a8

Browse files
committed
Typecheck let bindings using annotated type
1 parent fd4b266 commit 15e50a8

8 files changed

+130
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
and prevents publishing packages with empty modules to Hex.
1717
([Vitor Souza](https://github.com/vit0rr))
1818

19+
- Let bindings now continue to assume their annotated type after mismatches,
20+
preventing misleading follow-up errors.
21+
([Adi Salimgereyev](https://github.com/abs0luty))
22+
1923
### Build tool
2024

2125
- The help text displayed by `gleam dev --help`, `gleam test --help`, and

compiler-core/src/type_/expression.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,44 +1976,48 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
19761976
let type_ = value.type_();
19771977
let kind = self.infer_assignment_kind(kind.clone());
19781978

1979-
// Ensure the pattern matches the type of the value
1980-
let mut pattern_typer = pattern::PatternTyper::new(
1981-
self.environment,
1982-
&self.implementations,
1983-
&self.current_function_definition,
1984-
&self.hydrator,
1985-
self.problems,
1986-
PatternPosition::LetAssignment,
1987-
);
1979+
let mut annotation_type_override = None;
19881980

1989-
let pattern = pattern_typer.infer_single_pattern(pattern, &value);
1990-
1991-
let minimum_required_version = pattern_typer.minimum_required_version;
1992-
if minimum_required_version > self.minimum_required_version {
1993-
self.minimum_required_version = minimum_required_version;
1994-
}
1995-
1996-
let pattern_typechecked_successfully = !pattern_typer.error_encountered;
1997-
1998-
// Check that any type annotation is accurate.
19991981
if let Some(annotation) = &annotation {
20001982
match self
20011983
.type_from_ast(annotation)
20021984
.map(|type_| self.instantiate(type_, &mut hashmap![]))
20031985
{
20041986
Ok(annotated_type) => {
1987+
let annotation_type_for_pattern = annotated_type.clone();
20051988
if let Err(error) = unify(annotated_type, type_.clone())
20061989
.map_err(|e| convert_unify_error(e, value.type_defining_location()))
20071990
{
20081991
self.problems.error(error);
20091992
}
1993+
annotation_type_override = Some(annotation_type_for_pattern);
20101994
}
20111995
Err(error) => {
20121996
self.problems.error(error);
20131997
}
20141998
}
20151999
}
20162000

2001+
// Ensure the pattern matches the type of the value
2002+
let mut pattern_typer = pattern::PatternTyper::new(
2003+
self.environment,
2004+
&self.implementations,
2005+
&self.current_function_definition,
2006+
&self.hydrator,
2007+
self.problems,
2008+
PatternPosition::LetAssignment,
2009+
);
2010+
2011+
let pattern =
2012+
pattern_typer.infer_single_pattern(pattern, &value, annotation_type_override.clone());
2013+
2014+
let minimum_required_version = pattern_typer.minimum_required_version;
2015+
if minimum_required_version > self.minimum_required_version {
2016+
self.minimum_required_version = minimum_required_version;
2017+
}
2018+
2019+
let pattern_typechecked_successfully = !pattern_typer.error_encountered;
2020+
20172021
// The exhaustiveness checker expects patterns to be valid and to type check;
20182022
// if they are invalid, it will crash. Therefore, if any errors were found
20192023
// when type checking the pattern, we don't perform the exhaustiveness check.
@@ -2029,7 +2033,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
20292033
}
20302034

20312035
let (output, not_exhaustive_error) =
2032-
self.check_let_exhaustiveness(location, value.type_(), &pattern);
2036+
self.check_let_exhaustiveness(location, type_.clone(), &pattern);
20332037

20342038
match (&kind, not_exhaustive_error) {
20352039
// The pattern is exhaustive in a let assignment, there's no problem here.

compiler-core/src/type_/pattern.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,13 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
330330
&mut self,
331331
pattern: UntypedPattern,
332332
subject: &TypedExpr,
333+
subject_type_override: Option<Arc<Type>>,
333334
) -> TypedPattern {
334335
let subject_variable = Self::subject_variable(subject);
335336

336-
let typed_pattern = self.unify(pattern, subject.type_(), subject_variable);
337+
let subject_type = subject_type_override.unwrap_or_else(|| subject.type_());
338+
339+
let typed_pattern = self.unify(pattern, subject_type, subject_variable);
337340
self.register_variables();
338341
typed_pattern
339342
}

compiler-core/src/type_/tests/errors.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,19 @@ fn module_could_not_unify6() {
743743
assert_module_error!("fn main() { let x: String = 5 x }");
744744
}
745745

746+
#[test]
747+
fn module_could_not_unify_let_binding_annotation_follow_up() {
748+
assert_module_error!(
749+
"
750+
pub fn main() {
751+
let x: String = 5
752+
let y: Int = x
753+
let z: String = x
754+
}
755+
"
756+
);
757+
}
758+
746759
#[test]
747760
fn module_could_not_unify7() {
748761
assert_module_error!("fn main() { let assert 5 = \"\" }");

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__module_could_not_unify9.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ expression: "fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }"
66
fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }
77

88
----- ERROR
9+
error: Type mismatch
10+
┌─ /src/one/two.gleam:1:25
11+
12+
1fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }
13+
^
14+
15+
Expected type:
16+
17+
String
18+
19+
Found type:
20+
21+
Int
22+
23+
error: Type mismatch
24+
┌─ /src/one/two.gleam:1:28
25+
26+
1fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }
27+
^
28+
29+
Expected type:
30+
31+
String
32+
33+
Found type:
34+
35+
Int
36+
937
error: Type mismatch
1038
┌─ /src/one/two.gleam:1:52
1139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
source: compiler-core/src/type_/tests/errors.rs
3+
assertion_line: 748
4+
expression: "\npub fn main() {\n let x: String = 5\n let y: Int = x\n let z: String = x\n}\n"
5+
snapshot_kind: text
6+
---
7+
----- SOURCE CODE
8+
9+
pub fn main() {
10+
let x: String = 5
11+
let y: Int = x
12+
let z: String = x
13+
}
14+
15+
16+
----- ERROR
17+
error: Type mismatch
18+
┌─ /src/one/two.gleam:3:19
19+
20+
3let x: String = 5
21+
^
22+
23+
Expected type:
24+
25+
String
26+
27+
Found type:
28+
29+
Int
30+
31+
error: Type mismatch
32+
┌─ /src/one/two.gleam:4:16
33+
34+
4let y: Int = x
35+
^
36+
37+
Expected type:
38+
39+
Int
40+
41+
Found type:
42+
43+
String

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance.snap

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,3 @@ Expected type:
3939
Found type:
4040

4141
Int
42-
43-
error: Type mismatch
44-
┌─ /src/one/two.gleam:5:11
45-
46-
5let c = a + 2
47-
^
48-
49-
The + operator expects arguments of this type:
50-
51-
Int
52-
53-
But this argument has this type:
54-
55-
String
56-
57-
Hint: Strings can be joined using the `<>` operator.

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__use___typed_pattern_wrong_type.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,17 @@ Expected type:
3232
Found type:
3333

3434
Box(Int)
35+
36+
error: Type mismatch
37+
┌─ /src/one/two.gleam:4:3
38+
39+
4x + y + z
40+
^
41+
42+
The + operator expects arguments of this type:
43+
44+
Int
45+
46+
But this argument has this type:
47+
48+
Bool

0 commit comments

Comments
 (0)