This test:
defmodule ThrowawayTest do
use ExUnit.Case
{:module, _name, bytecode, _exports} =
defmodule TestModule do
@type my_type() :: :a | :b | :c | :d
def hello, do: 1
end
@bytecode bytecode
test "generates the expected types" do
assert {:ok, [type: {:my_type, _, []}]} = Code.Typespec.fetch_types(@bytecode)
end
end
works in 1.13.4-otp-25
but is broken in 1.14.0-otp-25
It appears that nested module are no longer written to disk.
Is this a known change in the 1.14 release or a side effect?
1 Like
Code.Typespec.fetch_types(@bytecode ) returns :error
I suspect that the inner module is no longer written to disk.
Just shooting in the dark: does this post help?
Hello! Is it possible to get the typespecs for a module that is compiled at runtime via Code.complie_string()? For example, if I run
compiled_module, _bytecode = Code.compile_string(
"""
defmodule UserDefinedGreeter do
@spec call(name :: String.t()) :: {:return, String.t()}
def call(name) do
{:return, "Hey " <> name}
end
end
"""
)
I end up with a UserDefinedGreeter module and I can e.g. call UserDefinedGreeter.call("foo"). However, I canāt seem to access that call typā¦
I have a fix for the problem (move to a real .ex module and using Code.compile_file/2, itās more that I canāt trace the source of the problem to the 1.14 release notes.
This is a minimal reconstruction of the issue, not the full test file that I am working with.
hst337
October 6, 2022, 3:18pm
6
This is strange, because @bytecode
is an actual bytecode of a module. In this form, module doesnāt have to be persisted
Yet the test still fails.
Its easy to test with an asdf setup.
hst337
October 6, 2022, 3:25pm
8
$ elixir x.ex
{:ok,
[
type: {:my_type,
{:type, 5, :union,
[{:atom, 0, :a}, {:atom, 0, :b}, {:atom, 0, :c}, {:atom, 0, :d}]}, []}
]}
$ cat x.ex
defmodule ThrowawayTest do
{:module, _name, bytecode, _exports} =
defmodule TestModule do
@type my_type() :: :a | :b | :c | :d
def hello, do: 1
end
@bytecode bytecode
def test do
Code.Typespec.fetch_types(@bytecode)
end
end
IO.inspect ThrowawayTest.test
$ elixir --version
Erlang/OTP 24 [erts-12.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]
Elixir 1.14.0 (compiled with Erlang/OTP 24)
For me it works correctly.
hst337
October 6, 2022, 3:28pm
9
And in ExUnit
test this works correctly too
$ mix test
Compiling 1 file (.ex)
Generated xxx app
.
Finished in 0.03 seconds (0.00s async, 0.03s sync)
1 test, 0 failures
Randomized with seed 488666
$ cat test/xxx_test.exs
defmodule ThrowawayTest do
use ExUnit.Case
{:module, _name, bytecode, _exports} =
defmodule TestModule do
@type my_type() :: :a | :b | :c | :d
def hello, do: 1
end
@bytecode bytecode
test "generates the expected types" do
assert {:ok, [type: {:my_type, _, []}]} = Code.Typespec.fetch_types(@bytecode)
end
end
hst337
October 6, 2022, 3:44pm
11
hst337
October 6, 2022, 3:46pm
13
This works correctly on both OTP 25 and OTP 24 for me
FWIW Code.Typespec
is a private module (@moduledoc false
) so thereās no backwards compatibility guarantee.
3 Likes
chriseyre:
Code.compile_file/
What do you suggest instead of? because I can use this code in my iex
and ex
file without any problem, but inside elixir test file it returns :error
{:module, name, bytecode_test, _exports} =
defmodule TestOne do
end
Code.Typespec.fetch_types(bytecode_test)
MacOs last version
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Elixir 1.15.1 (compiled with Erlang/OTP 26)
@hst337 Please test it inside your unit test mix test
in a elixir project, for example test this project GitHub - ejpcmac/typed_struct: An Elixir library for defining structs with a type without writing boilerplate code. , this project uses this function inside its test
To clarify this doesnāt just work in test, it works everywhere else
Update:
I check it for Protocol and it works, but for module it just returns :error
assert [{:type, 23, :fun, args}] = get_callbacks(@with_any_binary, :ok, 1)
assert args == [{:type, 23, :product, [{:user_type, 23, :t, []}]}, {:type, 23, :term, []}]
end
The problem is because of test_elixirc_options: [debug_info: true]
you can see this issue
opened 06:34PM - 13 Jul 23 UTC
closed 07:39PM - 13 Jul 23 UTC
Note:Needs more info
### Elixir and Erlang/OTP versions
```
Erlang/OTP 26 [erts-14.0.2] [source] ā¦ [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Elixir 1.15.1 (compiled with Erlang/OTP 26)
```
### Operating system
MacOS
### Current behavior
Hello, `Code.Typespec.fetch_types` function works very well for `Protocol`, but it does not work with module binary in Elixir 1.15.1 and otp 26, it seems works in pervious Elixir and otp version.
# Example:
```elixir
{:module, _name, bytecode, _exports} =
defmodule OpaqueTestStruct do
@type t :: any
end
Code.Typespec.fetch_types(bytecode)
# It returns :error
# OR
```
But if you test it with `protocol`, it works and returns the type.
```elixir
{_, _, sample_binary, _} =
defprotocol Sample do
@type t :: any
@doc "Ok"
@deprecated "Reason"
@spec ok(t) :: boolean
def ok(term)
end
Code.Typespec.fetch_types(sample_binary)
```
> ### It should be noted, all the thing works in `ex` and `iex`, but it does not work in `mix test` and test files
---
### Expected behavior
I know it is false doc module and thereās no backwards compatibility guarantee, but I saw you are using it inside elixir code and there is no another way to check module types, Am I right?
https://github.com/elixir-lang/elixir/blob/main/lib/elixir/test/elixir/typespec_test.exs#L10-L28
So, I think this erlang function is used in elixir code can be match with the pattern
```elixir
:beam_lib.chunks(bytecode, [:debug_info])
```
So it does not passed and returns `:error`, I found it here
https://github.com/elixir-lang/elixir/blob/7858bac32b80d887fd4e672288a2fa43b40daeb3/lib/elixir/lib/code/typespec.ex#L156-L174
I have no knowledge from erlang to fix it and send pull request, sorry! ā¹ļø
For reproducing this problem you can test this library one of the useful library in elixir for struct and it is simple project
https://github.com/ejpcmac/typed_struct
----
## I could not be able to find another function to check is there the type we put it inside module or not? I really need a function to pass it to test my project
Thank you in advance
> I would really like to be able to solve this problem, if it really is a problem, I would do something to help improve it, but my Erlang literacy is very low.