Elixir v1.19.0-rc.2 released

1. Enhancements

Elixir

  • [Regex] Raise error message when regexes are used as default values in struct fields for compatibility with Erlang/OTP 28
  • [Registry] Add key-based partitioning of duplicate registries

2. Bug fixes

Elixir

  • [Kernel] Address issue with type checking not completing on protocol consolidation

ExUnit

  • [ExUnit] Do not crash on empty test unit groups

Mix

  • [mix help] Add mix help app:APP
  • [mix test] Fix module preloading in mix test --slowest-modules=N
32 Likes

Alright folks, in a nutshell, this release addresses all feedback from the previous RCs:

  1. Fully compatible with Erlang/OTP 28.1+

  2. Struct update syntax has been adapted into a type assertion operator

  3. Addresses issues with projecting taking too long to compile

This should be our final stop before v1.19, please give it a try!

46 Likes

Looking good!

2 Likes

While compiling the following functions I’m getting a warning that did not show in Elixir 1.18.x.

@impl Event
def to_payload(%GameTransactionCompleted{} = e) do
  # ...
end

def to_payload(%GameTransactionCompleted.ExternalTransaction{} = e) do
  # ...
end

def to_payload(%GameTransactionCompleted.InternalTransaction{} = e) do
  # ...
end

def to_payload(%GameTransactionCompleted.Stake{} = s) do
  # ...
end

def to_payload(%Game{} = g) do
  # ...
end

The Event protocol defines the callback as:

@spec to_payload(struct()) :: map()
def to_payload(struct)

And this is the warning that I get:

Compiling 1 file (.ex)
     warning: the 1st pattern in clause will never match:

         %Dragonara.Outbox.GameTransactionCompleted.ExternalTransaction{} = e

     because it is expected to receive type:

         dynamic(%Dragonara.Outbox.GameTransactionCompleted{})

     typing violation found at:
     │
 101 │     def to_payload(%GameTransactionCompleted.ExternalTransaction{} = e) do
     │                                                                    ~
     │
     └─ lib/dragonara/outbox/game_transaction_completed.ex:101:68: Dragonara.Outbox.Event.Dragonara.Outbox.GameTransactionCompleted.to_payload/1

     warning: the 1st pattern in clause will never match:

         %Dragonara.Outbox.GameTransactionCompleted.InternalTransaction{} = e

     because it is expected to receive type:

         dynamic(%Dragonara.Outbox.GameTransactionCompleted{})

     typing violation found at:
     │
 120 │     def to_payload(%GameTransactionCompleted.InternalTransaction{} = e) do
     │                                                                    ~
     │
     └─ lib/dragonara/outbox/game_transaction_completed.ex:120:68: Dragonara.Outbox.Event.Dragonara.Outbox.GameTransactionCompleted.to_payload/1

     warning: the 1st pattern in clause will never match:

         %Dragonara.Outbox.GameTransactionCompleted.Stake{} = s

     because it is expected to receive type:

         dynamic(%Dragonara.Outbox.GameTransactionCompleted{})

     typing violation found at:
     │
 140 │     def to_payload(%GameTransactionCompleted.Stake{} = s) do
     │                                                      ~
     │
     └─ lib/dragonara/outbox/game_transaction_completed.ex:140:54: Dragonara.Outbox.Event.Dragonara.Outbox.GameTransactionCompleted.to_payload/1

     warning: the 1st pattern in clause will never match:

         %Bluelabs.Game{} = g

     because it is expected to receive type:

         dynamic(%Dragonara.Outbox.GameTransactionCompleted{})

     typing violation found at:
     │
 144 │     def to_payload(%Game{} = g) do
     │                            ~
     │
     └─ lib/dragonara/outbox/game_transaction_completed.ex:144:28: Dragonara.Outbox.Event.Dragonara.Outbox.GameTransactionCompleted.to_payload/1

     warning: the 1st pattern in clause will never match:

         nil

     because it is expected to receive type:

         dynamic(%Dragonara.Outbox.GameTransactionCompleted{})

     typing violation found at:
     │
 171 │     def to_payload(nil) do
     │     ~~~~~~~~~~~~~~~~~~~~~~
     │
     └─ lib/dragonara/outbox/game_transaction_completed.ex:171: Dragonara.Outbox.Event.Dragonara.Outbox.GameTransactionCompleted.to_payload/1

Generated dragonara app

Looks like the compiler is not able to understand that this is a multiclause function that acts differently depending on the struct that it receives. It is true that from the outside this function is only called with the %GameTransactionCompleted{} struct but that call also uses the other clauses for nested structs.

EDIT: I also want to note that if I rename the problematic clauses so they are not part of the same function (for example payload instead of to_payload) the warnings go away. While this workaround fixes the issue I still wonder if this should happen and why the compiler is not able to detect that the other clauses are also being called with the proper structs.

2 Likes

Can you please provide a code snippet that reproduces the issue? Thank you.

1 Like

Oh, I think I got it. You literally have a protocol and then in one of the implementations, you define additional clauses.

Yes, the warning is expected. The type system is enforcing that you are implementing the protocol exactly for the type of that implementation. We could not emit any warning, but then it means someone may accidentally define additional clauses, and they won’t get any help from the type system. So we chose to enforce that the first argument matches the implementation.

If the description above is not correct, then please provide a way to reproduce this or post a larger code snippet. Thanks.

6 Likes

I have verified across multiple projects now. We did hit the issue in rc.1 :wink:
But rc.2 seems solid.

I am deploying to some QA ENVs right now but things look really good.
Probably 20k unit tests across the projects we’ve updated. All passing.
Huge improvements.

THANK YOU SO MUCH!!!

4 Likes

1.19.0-rc.2 is working great for me, thanks everyone for their amazing work on this!

Note: I too was having trouble with rc.1 where our test suite would just hang, not having that issue with rc.2

2 Likes

Exactly the same here. rc2 seems solid.

3 Likes

Someone should probably ask the hex team to push a docker image of 1.19.0-rc.2 to docker hub so we can test the image as well…

I don’t expect any issues, but I can’t fully test end-to-end without that.

They have an Erlang 28.1 image already.

2 Likes

I just updated NervesHub (UI and API) and the test suite is passing. Elixir 1.19 helped pick up a few things that needed addressing, which was nice.

I then ran dialyzer but got a bunch of errors. Should I report any of the errors here?

1 Like

We run this as part of our CI:

mix xref graph --format cycles --label compile-connected --min-cycle-size 2

Running with 1.19.0-rc.2 detected a huge cycle (length 171) that was not there before (1.18.4). I notice that only some modules in the cycle have the “compile” annotation, which seems suspicious.

an excerpt of the output
2 cycles found. Showing them in decreasing size:

Cycle of length 171:

    lib/fonar/aanvullen.ex
    lib/fonar/aanvullen/aanvullen_vanuit_wenslijst_worker.ex
    lib/fonar/boeksuggesties.ex
    lib/fonar/bronnen.ex
    lib/fonar/daisy_cd_leveringen.ex
    lib/fonar/daisy_cd_leveringen/daisy_cd_leveren_worker.ex
    lib/fonar/email_activities.ex
    lib/fonar/instanties.ex
    lib/fonar/lezers.ex
    lib/fonar/lezers/lezer_notifier.ex
    lib/fonar/lezers/lezer_notifier_worker.ex
    lib/fonar/notifier.ex (export)
    lib/fonar/organisaties.ex
    lib/fonar/organisaties/medewerker_notifier.ex
    lib/fonar/reservaties.ex (export)
    lib/fonar/reservaties/mail_reservatie_beschikbaar_worker.ex
    lib/fonar/reservaties/reservatie.ex
    lib/fonar/reservaties/verwerk_reservaties_worker.ex
    lib/fonar/uitleningen.ex
    lib/fonar/uitleningen/start_uitlening.ex
    lib/fonar/uitleningen/uitlening_state_change.ex
    lib/fonar/verwervingen.ex (export)
    lib/fonar/verwervingen/formaat_verwerving.ex (compile)
    lib/fonar/verwervingen/formaat_verwerving_states.ex
    lib/fonar/verwervingen/productiebestelling.ex
    lib/fonar/verwervingen/productiebestelling/item.ex
    ...

I doubt this is an actual compile-connected cycle. Could this be regression?

It could be a regression. I’d start by comparing the compile-connected across both versions.

1 Like

Not sure how helpful this will be, but I tried compiling our largish project (3729 files; 119 kloc), and the overall elapsed time was noticeably slower than it was with 1.18 (this was the case with both Erlang 27 and 28):

elixir          1.18.4-otp-27
erlang          27.3.4.3

rm -rf _build && mix deps.get && mix compile && mix clean && time mix compile
...
Compiling 3728 files (.ex)
Generated premonition app

real	0m41.884s
user	1m32.467s
sys	0m33.326s

elixir          1.19.0-rc.2-otp-27
erlang          27.3.4.3

real	0m41.573s
user	2m37.958s
sys	1m16.684s

elixir          1.19.0-rc.2-otp-28
erlang          28.1

real	0m41.010s
user	2m39.051s
sys	1m5.844s

According to your output they used the same amount of wall-time but different amount of CPU time if I read this correctly?

I assume the increased CPU time is due to more work being done and that is done between more CPU cores.

But the actual elapsed time is «the same» ? :slight_smile:

1 Like

According to your output they used the same amount of wall-time but different amount of CPU time if I read this correctly?

You’re right – I was reading the output wrong :man_facepalming:t2:. The first time I ran it the real time was slower too (by around 10–20% iirc), but when I ran it while not doing anything else at the same time it evened out. Still seems slightly odd that it’s doing that much more work overall though, even if it’s making better use of all the cores.

It will do more work because the type system is smarter. :slight_smile: So that bit is expected too!

4 Likes

Ah, that makes sense – thanks! :slight_smile:

Sorry @josevalim , just thought I’d check with you regarding the dialyzer errors, is this something you would like reported here or in a GH issue, or maybe not at all?

Sorry, I missed it indeed. If you can isolate them, please do an issues tracker report, we can see if it is something we can address!