From 4902bee69fdf8cfbac263e1e8fe522efbe177f53 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 13 Sep 2026 14:05:38 +0200 Subject: [PATCH] Fix typechecker crash on non elixir struct module in defimpl Ensure the module exports __info__/1 Mimic check from struct_info/6 --- lib/elixir/lib/module/types/of.ex | 6 ++++- .../elixir/module/types/integration_test.exs | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/elixir/lib/module/types/of.ex b/lib/elixir/lib/module/types/of.ex index ed1de24a80..44599256cf 100644 --- a/lib/elixir/lib/module/types/of.ex +++ b/lib/elixir/lib/module/types/of.ex @@ -291,7 +291,11 @@ defmodule Module.Types.Of do # so we need to deal with such cases accordingly. # TODO: Assume implementation is available on Elixir v2.0. # A warning is emitted since v1.19+. - if info = mode == :closed && Code.ensure_loaded?(struct) && struct.__info__(:struct) do + info = + mode == :closed and Code.ensure_loaded?(struct) and + function_exported?(struct, :__info__, 1) and struct.__info__(:struct) + + if info do struct_type(struct, info) else open_map(__struct__: {atom([struct]), false}) diff --git a/lib/elixir/test/elixir/module/types/integration_test.exs b/lib/elixir/test/elixir/module/types/integration_test.exs index a9cca324da..8da5403acc 100644 --- a/lib/elixir/test/elixir/module/types/integration_test.exs +++ b/lib/elixir/test/elixir/module/types/integration_test.exs @@ -163,6 +163,31 @@ defmodule Module.Types.IntegrationTest do dynamic(open_map(__struct__: {atom([Unknown]), false})) end + test "writes exports for implementations of Erlang modules" do + files = %{ + "pe.ex" => """ + defprotocol Erl do + def erl(data) + end + + defimpl Erl, for: :gen_server do + def erl(data), do: data + end + """ + } + + modules = compile_modules(files) + + {_, %{sig: {:infer, nil, [{[domain], _return}]}}} = + List.keyfind( + read_chunk(modules[Module.concat(Erl, :gen_server)]).exports, + {:erl, 1}, + 0 + ) + + assert domain == open_map(__struct__: {atom([:gen_server]), false}) + end + test "ignores additional callbacks on implementations" do files = %{ "p.ex" => """