gleam-lang / gleam

⭐️ A friendly language for building type-safe, scalable systems!
https://gleam.run
Apache License 2.0
17.67k stars 737 forks source link

Unexpected argument order on calls with labelled parameters #3671

Open mdarse opened 2 days ago

mdarse commented 2 days ago

Hi!

I encounter what I believe to be a bug if the two following properties hold:

Here is a reduction of my original code that motivated me to digg into this issue. The bug is kind of sneaky because two String parameters are silently reversed when a third (labelled) argument corresponding to the first function parameter is added.

// This compiles fine
pub fn heading_test() {
  heading("Hello", "/world", size: 1)
}

fn heading(size size: Int, text text: String, href href: String) {
  // Crashes here
  let assert "Hello" = text
  let assert "/world" = href
}

Using different types to make the issue more obvious. Every line below with a Error expecting XXX comment doesn’t type check.

type A { A }
type B { B }
type C { C }
type D { D }

fn foo(a a: A, b b: B, c c: C, d d: D) {
  Nil
}

pub fn labelled_call_issue_test() {
  // Calls with a single labelled argument
  foo(A, B, C, d: D)
  foo(A, B, D, c: C)
  foo(A, C, D, b: B) // Error expecting A, D, C, b: B
  foo(B, C, D, a: A) // Error expecting D, B, C, a: A

  // Calls with two labelled arguments
  foo(A, B, c: C, d: D)
  foo(A, B, d: D, c: C)
  foo(A, C, b: B, d: D)
  foo(A, C, d: D, b: B)
  foo(A, D, b: B, c: C)
  foo(A, D, c: C, b: B)
  foo(B, C, a: A, d: D) // Error expecting C, B, a: A, d: D
  foo(B, C, d: D, a: A) // Error expecting C, B, d: D, a: A
  foo(B, D, a: A, c: C) // Error expecting D, B, a: A, c: C
  foo(B, D, c: C, a: A) // Error expecting D, B, c: C, a: A
  foo(C, D, a: A, b: B)
  foo(C, D, b: B, a: A) // Error expecting D, C, b: B, a: A -> labelled argument order matters here

  // No issue with any permutation of a 3 labelled arguments call
  foo(A, b: B, c: C, d: D)
  foo(A, b: B, d: D, c: C)
  foo(A, c: C, b: B, d: D)
  foo(A, c: C, d: D, b: B)
  foo(A, d: D, b: B, c: C)
  foo(A, d: D, c: C, b: B)
  foo(B, a: A, c: C, d: D)
  foo(B, a: A, d: D, c: C)
  foo(B, c: C, a: A, d: D)
  foo(B, c: C, d: D, a: A)
  foo(B, d: D, a: A, c: C)
  foo(B, d: D, c: C, a: A)
  foo(C, a: A, b: B, d: D)
  foo(C, a: A, d: D, b: B)
  foo(C, b: B, a: A, d: D)
  foo(C, b: B, d: D, a: A)
  foo(C, d: D, a: A, b: B)
  foo(C, d: D, b: B, a: A)
}
❯ gleam --version && uname -v
gleam 1.5.0
Darwin Kernel Version 23.6.0: Wed Jul 31 20:48:52 PDT 2024; root:xnu-10063.141.1.700.5~1/RELEASE_ARM64_T6020

Sonoma 14.7 (23H124)

giacomocavalieri commented 2 days ago

Ooh this is really spooky! I've checked and v1 seems to behave the same. Since it compiles just fine when the types still line up, I'm not sure if fixing this would be considered a breaking change... we'll have to wait for Louis to weigh in on this

lpil commented 12 hours ago

Let's fix this, @mdarse's assumptions on the intended behaviour are correct.