Repository: gleam-lang/gleam (Rust)

Issue:
Constants are failing when used with bit_strings
Giving this code:
```gleam
pub fn get_next(index_file: IndexFile) -> Result(Index, file.Reason) {
  case fs.read(index_file.handler, index_size_bytes) {
    read.Ok(<<
      offset:size(offset_size_bits),
      size:size(event_size_bits),
      file_id:size(file_id_size_bits),
    >>) -> Ok(Index(offset, size, file_id))
    read.Eof -> Error(file.Espipe)
    _ -> Error(file.Einval)
  }
}
```
and defined the following constants:
```gleam
const offset_size_bits = 48
const event_size_bits = 24
const file_id_size_bits = 128
```
Gleam compiler isn't giving any issue but the Erlang compiler is failing and generating this code:
```erlang
get_next(Index_file) ->
    case file:read(erlang:element(2, Index_file), 25) of
        {ok,
            <<Offset:Offset_size_bits,
                Size:Event_size_bits,
                File_id:File_id_size_bits>>} ->
            {ok, {index, Offset, Size, File_id}};

        eof ->
            {error, espipe};

        _ ->
            {error, einval}
    end.
```
As you can see, `Offset_size_bits`, `Event_size_bits`, `File_id_size_bits` are the constants but they are not defined in the scope of the Erlang function.


Discussion:
Thank you
hi i would like to work on this issue
Wonderful, thank you @macroby ! 
@lpil @macroby I'm afraid it's still not solved.

Checking this code:

```gleam
pub const value_size_bits = 32

pub fn read() -> BitString {
  let assert Ok(<<size:size(value_size_bits), deleted:8>>) =
    Ok(<<"HOLA":utf8, 0:8>>)
  <<size:size(value_size_bits), deleted:8>>
}
```

It's giving:
```
%    9|     {ok, <<Size:Value_size_bits, Deleted:8>>} = case _assert_subject of
%     |                 ^

/Users/manuel/Projects/Mediussa/ream/testing/build/dev/erlang/testing/_gleam_artefacts/testing.erl:10:18: variable 'Value_size_bits' is unbound
%   10|         {ok, <<_:Value_size_bits, _:8>>} -> _assert_subject;
%     |                  ^
```
Thank you @manuel-rubio .

@macroby would you like to take on the bug? Thanls
Yes I can work on this.
Thank you
I pulled the most recent state of the main branch of gleam, then ran ```make install```

I built the following gleam code:

const_bug_codegen.gleam
```
pub fn main() {
  read()
}

pub const value_size_bits = 32

pub fn read() -> BitString {
  let assert Ok(<<size:size(value_size_bits), deleted:8>>) =
    Ok(<<"HOLA":utf8, 0:8>>)
  <<size:size(value_size_bits), deleted:8>>
}
```

and got back the following relevant erlang output with no errors:

const_bug_codegen.erl
```
read() ->
    _assert_subject = {ok, <<"HOLA"/utf8, 0:8>>},
    {ok, <<Size:32, Deleted:8>>} = case _assert_subject of
        {ok, <<_:32, _:8>>} -> _assert_subject;
```

It seems that the constant is inlined properly.




Oh yes! Thank you for fixing and verifying this @macroby 💜 



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3fedc76748..82b4913438a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
   updated for the latest syntax.
 - Fixed a bug where mutually recursive functions could be incorrectly inferred
   as having an overly general type.
+- Fix a bug where constants where not being correctly inlined when used in the 
+  size option of a bitstring pattern match.
 
 ## v0.30.4 - 2023-07-26
 
diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs
index 972de8cd082..fef4cadbbea 100644
--- a/compiler-core/src/ast.rs
+++ b/compiler-core/src/ast.rs
@@ -1087,6 +1087,7 @@ pub enum Pattern<Type> {
     VarUsage {
         location: SrcSpan,
         name: SmolStr,
+        constructor: Option<ValueConstructor>,
         type_: Type,
     },
 
diff --git a/compiler-core/src/erlang/pattern.rs b/compiler-core/src/erlang/pattern.rs
index 163ca9120e6..5a48247ad3d 100644
--- a/compiler-core/src/erlang/pattern.rs
+++ b/compiler-core/src/erlang/pattern.rs
@@ -31,7 +31,20 @@ fn print<'a>(
 
         Pattern::Discard { .. } => "_".to_doc(),
 
-        Pattern::VarUsage { name, .. } => env.local_var_name(name),
+        Pattern::VarUsage {
+            name, constructor, ..
+        } => {
+            let v = &constructor
+                .as_ref()
+                .expect("Constructor not found for variable usage")
+                .variant;
+            match v {
+                ValueConstructorVariant::ModuleConstant { literal, .. } => {
+                    const_inline(literal, env)
+                }
+                _ => env.local_var_name(name),
+            }
+        }
 
         Pattern::Var { name, .. } if define_variables => {
             vars.push(name);
diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs
index 1f0b97a38d4..47755bc289a 100644
--- a/compiler-core/src/parse.rs
+++ b/compiler-core/src/parse.rs
@@ -2571,6 +2571,7 @@ where
             Some((start, Token::Name { name }, end)) => Ok(Pattern::VarUsage {
                 location: SrcSpan { start, end },
                 name,
+                constructor: None,
                 type_: (),
             }),
             Some((start, Token::Int { value }, end)) => Ok(Pattern::Int {
diff --git a/compiler-core/src/type_/pattern.rs b/compiler-core/src/type_/pattern.rs
index 0b2fb4d6a09..7c13ab2f468 100644
--- a/compiler-core/src/type_/pattern.rs
+++ b/compiler-core/src/type_/pattern.rs
@@ -228,7 +228,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
             }
 
             Pattern::VarUsage { name, location, .. } => {
-                let ValueConstructor { type_, .. } = self
+                let vc = self
                     .environment
                     .get_variable(&name)
                     .cloned()
@@ -238,14 +238,15 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
                         variables: self.environment.local_value_names(),
                     })?;
                 self.environment.increment_usage(&name);
-                let typ = self
-                    .environment
-                    .instantiate(type_, &mut hashmap![], self.hydrator);
+                let typ =
+                    self.environment
+                        .instantiate(vc.type_.clone(), &mut hashmap![], self.hydrator);
                 unify(int(), typ.clone()).map_err(|e| convert_unify_error(e, location))?;
 
                 Ok(Pattern::VarUsage {
                     name,
                     location,
+                    constructor: Some(vc),
                     type_: typ,
                 })
             }


Test patch:
diff --git a/compiler-core/src/erlang/snapshots/gleam_core__erlang__tests__inline_const_pattern_option.snap b/compiler-core/src/erlang/snapshots/gleam_core__erlang__tests__inline_const_pattern_option.snap
new file mode 100644
index 00000000000..d36a391035b
--- /dev/null
+++ b/compiler-core/src/erlang/snapshots/gleam_core__erlang__tests__inline_const_pattern_option.snap
@@ -0,0 +1,22 @@
+---
+source: compiler-core/src/erlang/tests.rs
+assertion_line: 574
+expression: "pub fn main() {\n            let x = <<5:size(sixteen)>>\n            case x {\n              <<5:size(sixteen)>> -> <<5:size(sixteen)>>\n              <<6:size(15)>> -> <<5:size(15)>>\n            }\n          }\n          \n          pub const sixteen = 16"
+---
+-module(my@mod).
+-compile([no_auto_import, nowarn_unused_vars]).
+
+-export([main/0]).
+
+-spec main() -> bitstring().
+main() ->
+    Fifteen = 15,
+    X = <<5:(lists:max([(16), 0]))>>,
+    case X of
+        <<5:16>> ->
+            <<5:(lists:max([(16), 0]))>>;
+
+        <<6:Fifteen>> ->
+            <<5:(lists:max([(Fifteen), 0]))>>
+    end.
+
diff --git a/compiler-core/src/erlang/tests.rs b/compiler-core/src/erlang/tests.rs
index 6672a9b50b5..536d4f42060 100644
--- a/compiler-core/src/erlang/tests.rs
+++ b/compiler-core/src/erlang/tests.rs
@@ -567,3 +567,20 @@ pub type Box {
 fn dynamic() {
     assert_erl!("pub external type Dynamic")
 }
+
+// https://github.com/gleam-lang/gleam/issues/2166
+#[test]
+fn inline_const_pattern_option() {
+    assert_erl!(
+        "pub fn main() {
+            let fifteen = 15
+            let x = <<5:size(sixteen)>>
+            case x {
+              <<5:size(sixteen)>> -> <<5:size(sixteen)>>
+              <<6:size(fifteen)>> -> <<5:size(fifteen)>>
+            }
+          }
+          
+          pub const sixteen = 16"
+    )
+}