Repository: gleam-lang/gleam (rust)

Issue:
Compiler crash when checking exhaustiveness of a pattern on a type with duplicate variants
Hello, thank you for all the wonderful work on Gleam! On v1.4.1 and the current main branch, but not on v1.2.1, running on Ubuntu and compiling to BEAM, I noticed the following small program will crash the compiler:

```gleam
pub type Bug {
  Ant
  Bee
  Bee
  Caterpillar
}

pub fn get_bugs(bug: Bug) {
  case bug {
    Ant -> "And"
    Bee -> "Bee"
    Caterpillar -> "Caterpillar"
  }
}
```
The error msg given by the compiler on v1.4.1 is:
```
Panic: compiler-core/src/exhaustiveness.rs:746
        Custom type variants must exist: Type { name: "Bug", hint: AlternativeTypes(["Bool", "Int", "List", "UtfCodepoint", "Result", "BitArray", "Nil", "String", "Bug", "Float"]) }
Gleam version: 1.4.1
Operating system: linux
```
The error msg given by the compiler on the current main branch is:
```
Panic: compiler-core/src/exhaustiveness.rs:539
        Case must exist
Gleam version: 1.4.1
Operating system: linux
```

On v1.2.1 and v1.4.1:
This code will compile and throw an error about the duplicate Caterpillar definition:
```gleam
pub type Bug {
  Ant
  Bee
  Caterpillar
  Caterpillar
}

pub fn get_bugs(bug: Bug) {
  "Bug!"
}
```
On v1.2.1 and v1.4.1 (but not the current main branch) this code will crash the compiler:
```gleam
pub type Bug {
  Ant
  Bee
  Caterpillar
  Caterpillar
}

pub fn get_bugs(bug: Bug) {
  case bug {
    Ant -> "And"
    Bee -> "Bee"
    Caterpillar -> "Caterpillar"
  }
}
```
on v1.4.1 it will give the following error:
```
Panic: compiler-core/src/exhaustiveness.rs:746
        Custom type variants must exist: Type { name: "Bug", hint: AlternativeTypes(["Bool", "Int", "List", "UtfCodepoint", "Result", "BitArray", "Nil", "String", "Bug", "Float"]) }
Gleam version: 1.4.1
Operating system: linux
```

So it seems that when the exhaustiveness check is run for the case statement in get_bugs, it will crash when there is _some_ set of duplicate variants. It seems like #3490 fixed a subset of the issue, but there is still a case where this pattern crashes the compiler.

Hope this helps! Let me know if any more info is needed.



Pull request description:
Fix another compiler crash for duplicate constructor definition
Fixes #3506 

This is a similar issue to #3489; this crash was caused by a mismatch of `constructor_index`s and actual registered constructors. If a duplicate constructor existed, it would not be registered but the index was still incremented, which broke an assumption made in another place in the compiler. I've just changed it to manually increment an index instead of using `Iterator::enumerate`.

I didn't add another changelog entry since this is just another edge-case of the aforementioned issue, and doesn't really require a separate entry in my opinion.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bdb4bc984..eebf131a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,11 @@
 
 - Fixed a bug where incorrect syntax error message were shown,
   when using `:` or `=` in wrong possitions in expressions.
+  ([Ankit Goel](https://github.com/crazymerlyn))
+
+- Fixed a bug where the compiler would crash when pattern matching on a type
+  which had constructors of duplicate names.
+  ([Surya Rose](https://github.com/gearsdatapacks))
 
 ## v1.4.1 - 2024-08-04
 
diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs
index 6054a17b5..715d2fdb7 100644
--- a/compiler-core/src/analyse.rs
+++ b/compiler-core/src/analyse.rs
@@ -866,7 +866,8 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
 
         let mut constructors_data = vec![];
 
-        for (index, constructor) in constructors.iter().enumerate() {
+        let mut index = 0;
+        for constructor in constructors.iter() {
             if let Err(error) = assert_unique_name(
                 &mut self.value_names,
                 &constructor.name,
@@ -926,6 +927,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
                 module: self.module_name.clone(),
                 constructor_index: index as u16,
             };
+            index += 1;
 
             // If the contructor belongs to an opaque type then it's going to be
             // considered as private.


Test patch:
diff --git a/compiler-core/src/type_/tests/errors.rs b/compiler-core/src/type_/tests/errors.rs
index c0a4d7366..f187523f6 100644
--- a/compiler-core/src/type_/tests/errors.rs
+++ b/compiler-core/src/type_/tests/errors.rs
@@ -2178,7 +2178,7 @@ fn no_crash_on_duplicate_definition() {
     // This previous caused the compiler to crash
     assert_module_error!(
         "
-type Wibble {
+pub type Wibble {
   Wobble
   Wobble
 }
@@ -2192,3 +2192,27 @@ pub fn main() {
 "
     );
 }
+
+#[test]
+fn no_crash_on_duplicate_definition2() {
+    // This also caused a compiler crash, separate to the above test
+    assert_module_error!(
+        "
+pub type Wibble {
+  Wibble
+  Wobble
+  Wobble
+  Wubble
+}
+
+pub fn main() {
+  let wibble = Wobble
+  case wibble {
+    Wibble -> Nil
+    Wobble -> Nil
+    Wubble -> Nil
+  }
+}
+"
+    );
+}
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition2.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition2.snap
new file mode 100644
index 000000000..d3b838e12
--- /dev/null
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition2.snap
@@ -0,0 +1,14 @@
+---
+source: compiler-core/src/type_/tests/errors.rs
+expression: "\npub type Wibble {\n  Wibble\n  Wobble\n  Wobble\n  Wubble\n}\n\npub fn main() {\n  let wibble = Wobble\n  case wibble {\n    Wibble -> Nil\n    Wobble -> Nil\n    Wubble -> Nil\n  }\n}\n"
+---
+error: Duplicate definition
+  ┌─ /src/one/two.gleam:4:3
+  │
+4 │   Wobble
+  │   ^^^^^^ First defined here
+5 │   Wobble
+  │   ^^^^^^ Redefined here
+
+`Wobble` has been defined multiple times.
+Names in a Gleam module must be unique so one will need to be renamed.