- 
                Notifications
    
You must be signed in to change notification settings  - Fork 3.5k
 
Description
Elixir and Erlang/OTP versions
$ elixir --version
Erlang/OTP 27 [erts-15.2.3] [source] [64-bit] [smp:32:32] [ds:32:32:10] [async-threads:1] [jit:ns]
Elixir 1.18.3 (compiled with Erlang/OTP 27)Operating system
Gentoo Linux with default/linux/amd64/23.0/desktop/plasma profile and 6.12.16 kernel version.
Current behavior
Defining a struct with default bitstring value raises ArgumentError with message argument error. It happens only if the size is not a multiple of 8.
The following examples are failing:
defmodule Example1, do: defstruct value: <<0::1>>
defmodule Example2, do: defstruct value: <<0::2>>
defmodule Example3, do: defstruct value: <<0::3>>
defmodule Example4, do: defstruct value: <<0::4>>
defmodule Example5, do: defstruct value: <<0::5>>
defmodule Example6, do: defstruct value: <<0::6>>
defmodule Example7, do: defstruct value: <<0::7>>
defmodule Example9, do: defstruct value: <<0::9>>
defmodule Example10, do: defstruct value: <<0::10>>
defmodule Example11, do: defstruct value: <<0::11>>
defmodule Example12, do: defstruct value: <<0::12>>
defmodule Example13, do: defstruct value: <<0::13>>
defmodule Example14, do: defstruct value: <<0::14>>
defmodule Example15, do: defstruct value: <<0::15>>The following examples are working:
defmodule Example8, do: defstruct value: <<0::8>>
defmodule Example16, do: defstruct value: <<0::16>>
defmodule Example24, do: defstruct value: <<0::24>>
defmodule Example32, do: defstruct value: <<0::32>>
defmodule Example40, do: defstruct value: <<0::40>>
defmodule Example48, do: defstruct value: <<0::48>>
defmodule Example56, do: defstruct value: <<0::56>>Here is a complete error with stacktrace for Example1:
** (ArgumentError) argument error
    (elixir 1.18.3) src/elixir_erl.erl:109: :elixir_erl.elixir_to_erl(<<0::size(1)>>, 0)
    (elixir 1.18.3) src/elixir_erl.erl:70: :elixir_erl."-elixir_to_erl/2-lc$^1/1-0-"/2
    (elixir 1.18.3) src/elixir_erl.erl:71: :elixir_erl.elixir_to_erl/2
    (elixir 1.18.3) src/elixir_erl.erl:111: :elixir_erl.elixir_to_erl_cons/2
    (elixir 1.18.3) src/elixir_erl.erl:339: :elixir_erl.struct_info/1
    (elixir 1.18.3) src/elixir_erl.erl:311: :elixir_erl.add_info_function/6
    (elixir 1.18.3) src/elixir_erl.erl:286: :elixir_erl.functions_form/8
    iex:15: (file)Expected behavior
I don't see why bitstring as a default value may be not supported especially that it works in version 1.17.3.
What's interesting is that the below call also does not work:
iex> :elixir_erl.elixir_to_erl(<<0::size(1)>>, 0)
** (ArgumentError) argument error
    (elixir 1.17.3) src/elixir_erl.erl:96: :elixir_erl.elixir_to_erl/2
    iex:1: (file)which means that either :elixir_erl.elixir_to_erl/2 does not support non UTF-8 bitstrings or if it's expected then this call is incorrectly used since Elixir version 1.18.0-rc.0.
I've tried to search for a root issue. I believe I found a commit that introduces this bug: cb2e036. To be even more precise it happened in the first commit of #13984 which is: 792e604
- Looks like a module paraller checker is now calling a 
:elixir_erl.debug_info/4:
elixir/lib/elixir/lib/module/parallel_checker.ex
Lines 65 to 79 in 66c5908
module_tuple = cond do is_tuple(info) -> info is_binary(info) -> with {:ok, binary} <- File.read(info), {:ok, {_, [debug_info: chunk]}} <- :beam_lib.chunks(binary, [:debug_info]), {:debug_info_v1, backend, data} = chunk, {:ok, module_map} <- backend.debug_info(:elixir_v1, module, data, []) do cache_from_module_map(table, module_map) else _ -> nil end end  - which calls 
:elixir_erl.dynamic_form/1:
elixir/lib/elixir/src/elixir_erl.erl
Lines 9 to 37 in 66c5908
%% debug_info callback debug_info(elixir_v1, _Module, none, _Opts) -> {error, missing}; debug_info(elixir_v1, _Module, {elixir_v1, Map, _Specs}, _Opts) -> {ok, Map}; debug_info(erlang_v1, _Module, {elixir_v1, Map, Specs}, _Opts) -> {Prefix, Forms, _, _, _} = dynamic_form(Map), {ok, Prefix ++ Specs ++ Forms}; debug_info(core_v1, _Module, {elixir_v1, Map, Specs}, Opts) -> {Prefix, Forms, _, _, _} = dynamic_form(Map), #{compile_opts := CompileOpts} = Map, AllOpts = CompileOpts ++ Opts, %% Do not rely on elixir_erl_compiler because we don't warn %% warnings nor the other functionality provided there. case elixir_erl_compiler:erl_to_core(Prefix ++ Specs ++ Forms, AllOpts) of {ok, CoreForms, _} -> try compile:noenv_forms(CoreForms, [no_spawn_compiler_process, from_core, to_core, return | AllOpts]) of {ok, _, Core, _} -> {ok, Core}; _What -> {error, failed_conversion} catch error:_ -> {error, failed_conversion} end; _ -> {error, failed_conversion} end; debug_info(_, _, _, _) -> {error, unknown_format}.  - which calls 
:elixir_erl.functions_form/8:
elixir/lib/elixir/src/elixir_erl.erl
Lines 156 to 178 in 66c5908
dynamic_form(#{module := Module, relative_file := RelativeFile, attributes := Attributes, definitions := Definitions, unreachable := Unreachable, deprecated := Deprecated, compile_opts := Opts} = Map) -> %% TODO: Match on anno directly in Elixir v1.22+ Line = case Map of #{anno := AnnoValue} -> erl_anno:line(AnnoValue); #{line := LineValue} -> LineValue end, {Def, Defmacro, Macros, Exports, Functions} = split_definition(Definitions, Unreachable, Line, [], [], [], [], {[], []}), FilteredOpts = lists:filter(fun({no_warn_undefined, _}) -> false; (_) -> true end, Opts), Location = {elixir_utils:characters_to_list(RelativeFile), Line}, Prefix = [{attribute, Line, file, Location}, {attribute, Line, module, Module}, {attribute, Line, compile, [no_auto_import | FilteredOpts]}], Struct = maps:get(struct, Map, nil), Forms0 = functions_form(Line, Module, Def, Defmacro, Exports, Functions, Deprecated, Struct), Forms1 = attributes_form(Line, Attributes, Forms0), {Prefix, Forms1, Def, Defmacro, Macros}.  - (rest is in the stacktrace above)
 
If I'm correct the changes in type system caused a bug in struct definition. Since we want to keep a backwards compatibility we have to add bitstring support for the  :elixir_erl.elixir_to_erl/2 function.
Related forum topic:
https://elixirforum.com/t/can-not-set-bitstring-value-in-defstruct-in-elixir-1-18-is-this-a-bug/70121?u=eiji