Repository: gleam-lang/gleam (2025-08-14T11:37:01Z)

Issue:
Error compiling pog
Hello! I was compiling `pog` using the main branch of the compiler and it's no longer compiling with the following error:
```
Compiling pog
src/pog.gleam:322:65: variable 'Ssl' is unbound, did you mean 'Result'?
error: Shell command failure

There was a problem when running the shell command `escript`.
```
Nothing has changed in `pog` recently, and I have tested that this doesn't happen with the commit right before inlining was merged, it looks like this is an inlining bug


Discussion:
Could you share the code please 🙏 
Minimum reproducible example I can find is this:
```gleam
import gleam/result

pub type Wibble {
  Wibble(a: Int, b: Int)
}

pub fn main() {
  let w = Wibble(1, 2)
  use b <- result.map(Ok(3))
  Wibble(..w, b:)
}
```

Seems to be a problem with inlining and record updates



Patch:
diff --git a/compiler-core/src/inline.rs b/compiler-core/src/inline.rs
index 81df9e9d568..a689111de25 100644
--- a/compiler-core/src/inline.rs
+++ b/compiler-core/src/inline.rs
@@ -479,11 +479,30 @@ impl Inliner<'_> {
                 record_assignment: record_assignment
                     .map(|assignment| Box::new(self.assignment(*assignment))),
                 constructor: self.boxed_expression(constructor),
-                arguments,
+                arguments: self.arguments(arguments),
             },
         }
     }
 
+    fn arguments(&mut self, arguments: Vec<TypedCallArg>) -> Vec<TypedCallArg> {
+        arguments
+            .into_iter()
+            .map(
+                |TypedCallArg {
+                     label,
+                     location,
+                     value,
+                     implicit,
+                 }| TypedCallArg {
+                    label,
+                    location,
+                    value: self.expression(value),
+                    implicit,
+                },
+            )
+            .collect()
+    }
+
     /// Where the magic happens. First, we check the left-hand side of the call
     /// to see if it's something we can inline. If not, we continue to walk the
     /// tree like all the other expressions do. If it can be inlined, we follow
@@ -561,22 +580,7 @@ impl Inliner<'_> {
         function: Box<TypedExpr>,
         arguments: Vec<TypedCallArg>,
     ) -> TypedExpr {
-        let arguments = arguments
-            .into_iter()
-            .map(
-                |TypedCallArg {
-                     label,
-                     location,
-                     value,
-                     implicit,
-                 }| TypedCallArg {
-                    label,
-                    location,
-                    value: self.expression(value),
-                    implicit,
-                },
-            )
-            .collect();
+        let arguments = self.arguments(arguments);
 
         // First, we traverse the left-hand side of this call. If this is called
         // inside another inlined function, this could potentially inline an


Test patch:
diff --git a/compiler-core/src/erlang/tests/inlining.rs b/compiler-core/src/erlang/tests/inlining.rs
index df9ef86f65c..3a85c0a9299 100644
--- a/compiler-core/src/erlang/tests/inlining.rs
+++ b/compiler-core/src/erlang/tests/inlining.rs
@@ -312,3 +312,24 @@ fn divide(a, b) {
 "
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/4852
+#[test]
+fn inlining_works_properly_with_record_updates() {
+    assert_erl!(
+        ("gleam_stdlib", "gleam/result", RESULT_MODULE),
+        "
+import gleam/result
+
+pub type Wibble {
+  Wibble(a: Int, b: Int)
+}
+
+pub fn main() {
+  let w = Wibble(1, 2)
+  use b <- result.map(Ok(3))
+  Wibble(..w, b:)
+}
+"
+    );
+}
diff --git a/compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inlining_works_properly_with_record_updates.snap b/compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inlining_works_properly_with_record_updates.snap
new file mode 100644
index 00000000000..849d2da4571
--- /dev/null
+++ b/compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inlining_works_properly_with_record_updates.snap
@@ -0,0 +1,39 @@
+---
+source: compiler-core/src/erlang/tests/inlining.rs
+expression: "\nimport gleam/result\n\npub type Wibble {\n  Wibble(a: Int, b: Int)\n}\n\npub fn main() {\n  let w = Wibble(1, 2)\n  use b <- result.map(Ok(3))\n  Wibble(..w, b:)\n}\n"
+---
+----- SOURCE CODE
+
+import gleam/result
+
+pub type Wibble {
+  Wibble(a: Int, b: Int)
+}
+
+pub fn main() {
+  let w = Wibble(1, 2)
+  use b <- result.map(Ok(3))
+  Wibble(..w, b:)
+}
+
+
+----- COMPILED ERLANG
+-module(my@mod).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
+-define(FILEPATH, "project/test/my/mod.gleam").
+-export([main/0]).
+-export_type([wibble/0]).
+
+-type wibble() :: {wibble, integer(), integer()}.
+
+-file("project/test/my/mod.gleam", 8).
+-spec main() -> {ok, wibble()} | {error, any()}.
+main() ->
+    W = {wibble, 1, 2},
+    case {ok, 3} of
+        {ok, Value} ->
+            {ok, {wibble, erlang:element(2, W), Value}};
+
+        {error, Error} ->
+            {error, Error}
+    end.
diff --git a/compiler-core/src/javascript/tests/inlining.rs b/compiler-core/src/javascript/tests/inlining.rs
index 3b691ce8e07..783686cfb8e 100644
--- a/compiler-core/src/javascript/tests/inlining.rs
+++ b/compiler-core/src/javascript/tests/inlining.rs
@@ -312,3 +312,24 @@ fn divide(a, b) {
 "
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/4852
+#[test]
+fn inlining_works_properly_with_record_updates() {
+    assert_js!(
+        ("gleam_stdlib", "gleam/result", RESULT_MODULE),
+        "
+import gleam/result
+
+pub type Wibble {
+  Wibble(a: Int, b: Int)
+}
+
+pub fn main() {
+  let w = Wibble(1, 2)
+  use b <- result.map(Ok(3))
+  Wibble(..w, b:)
+}
+"
+    );
+}
diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inlining_works_properly_with_record_updates.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inlining_works_properly_with_record_updates.snap
new file mode 100644
index 00000000000..9fcae287fed
--- /dev/null
+++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inlining_works_properly_with_record_updates.snap
@@ -0,0 +1,41 @@
+---
+source: compiler-core/src/javascript/tests/inlining.rs
+expression: "\nimport gleam/result\n\npub type Wibble {\n  Wibble(a: Int, b: Int)\n}\n\npub fn main() {\n  let w = Wibble(1, 2)\n  use b <- result.map(Ok(3))\n  Wibble(..w, b:)\n}\n"
+---
+----- SOURCE CODE
+
+import gleam/result
+
+pub type Wibble {
+  Wibble(a: Int, b: Int)
+}
+
+pub fn main() {
+  let w = Wibble(1, 2)
+  use b <- result.map(Ok(3))
+  Wibble(..w, b:)
+}
+
+
+----- COMPILED JAVASCRIPT
+import * as $result from "../../gleam_stdlib/gleam/result.mjs";
+import { Ok, CustomType as $CustomType } from "../gleam.mjs";
+
+export class Wibble extends $CustomType {
+  constructor(a, b) {
+    super();
+    this.a = a;
+    this.b = b;
+  }
+}
+
+export function main() {
+  let w = new Wibble(1, 2);
+  let $ = new Ok(3);
+  if ($ instanceof Ok) {
+    let value = $[0];
+    return new Ok(new Wibble(w.a, value));
+  } else {
+    return $;
+  }
+}