Skip to content

Commit 670b410

Browse files
committed
Fix crash on record update if constructor definition is invalid
Fixes #5296
1 parent 4bcd14f commit 670b410

4 files changed

Lines changed: 64 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,8 @@
325325
detected when rebuilding the root project, causing the compiler to report
326326
errors about missing modules.
327327
([daniellionel01](https://github.com/daniellionel01))
328+
329+
- Fixed a bug where the compiler would crash when type-checking record
330+
updates if the constructor definition has a positional field defined after
331+
labelled fields.
332+
([Hari Mohan](https://gituhub.com/seafoamteal))

compiler-core/src/type_/expression.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3251,10 +3251,19 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
32513251
}
32523252
.expect("Variant has already checked to be valid");
32533253

3254+
// Positional fields must always be defined before any labelled fields.
3255+
// As such, we expect that field indices 0..(positional_fields.length)
3256+
// all correspond to positional fields. If this is not the case, then the
3257+
// user currently has a mistake in the definition of their custom type.
3258+
//
3259+
// For example, when a user starts adding a new labelled field to a custom
3260+
// type constructor, the field label will be parsed as a custom type name
3261+
// until they type the colon i.e. as a new positional field.
32543262
let type_ = positional_fields
32553263
.get(index as usize)
3256-
.expect("Field exists")
3264+
.ok_or_else(|| Error::UnlabelledAfterlabelled { location })?
32573265
.clone();
3266+
32583267
let mut type_vars = im::HashMap::new();
32593268
let accessor_type = self.instantiate(accessor_type, &mut type_vars);
32603269
let type_ = self.instantiate(type_, &mut type_vars);

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,3 +3424,20 @@ pub fn main() {
34243424
"
34253425
);
34263426
}
3427+
3428+
// https://github.com/gleam-lang/gleam/issues/5296
3429+
#[test]
3430+
fn no_crash_on_record_update_when_constructor_definition_is_invalid() {
3431+
assert_module_error!(
3432+
"
3433+
pub type Wibble {
3434+
Wibble(a: Int, b: Int, Bool)
3435+
}
3436+
3437+
pub fn main() {
3438+
let one = Wibble(False, a: 5, b: 6)
3439+
let two = Wibble(..one, b: 1)
3440+
}
3441+
"
3442+
);
3443+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
source: compiler-core/src/type_/tests/errors.rs
3+
expression: "\npub type Wibble {\n Wibble(a: Int, b: Int, Bool)\n}\n\npub fn main() {\n let one = Wibble(False, a: 5, b: 6)\n let two = Wibble(..one, b: 1)\n}\n "
4+
---
5+
----- SOURCE CODE
6+
7+
pub type Wibble {
8+
Wibble(a: Int, b: Int, Bool)
9+
}
10+
11+
pub fn main() {
12+
let one = Wibble(False, a: 5, b: 6)
13+
let two = Wibble(..one, b: 1)
14+
}
15+
16+
17+
----- ERROR
18+
error: Unlabelled argument after labelled argument
19+
┌─ /src/one/two.gleam:3:26
20+
21+
3Wibble(a: Int, b: Int, Bool)
22+
│ ^^^^
23+
24+
All unlabelled arguments must come before any labelled arguments.
25+
26+
error: Unlabelled argument after labelled argument
27+
┌─ /src/one/two.gleam:8:13
28+
29+
8let two = Wibble(..one, b: 1)
30+
^^^^^^^^^^^^^^^^^^^
31+
32+
All unlabelled arguments must come before any labelled arguments.

0 commit comments

Comments
 (0)