Skip to content

Commit

Permalink
gets some error messages up and running
Browse files Browse the repository at this point in the history
  • Loading branch information
ityonemo committed Apr 28, 2024
1 parent b4cb211 commit 28fd3e7
Show file tree
Hide file tree
Showing 16 changed files with 75 additions and 73 deletions.
4 changes: 2 additions & 2 deletions lib/zig/error_prong.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Zig.ErrorProng do

# default parameter errors handling.

def argument_error_prong(:elixir, _) do
def argument_error_prong(:elixir) do
quote do
:error, {:argument_error, index, error_lines} ->
new_stacktrace =
Expand Down Expand Up @@ -61,7 +61,7 @@ defmodule Zig.ErrorProng do
end
end

def argument_error_prong(:erlang, _) do
def argument_error_prong(:erlang) do
[
"error:{error, badarg, _ExtraStacktrace}:Stacktrace -> erlang:raise(error, badarg, Stacktrace)"
]
Expand Down
12 changes: 12 additions & 0 deletions lib/zig/nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ defmodule Zig.Nif do
# end
# end

# COMMON TOOLS
# generates AST for parameters.
@spec elixir_parameters(arity, used :: boolean) :: [Macro.t]
def elixir_parameters(0, _), do: []
def elixir_parameters(arity, true), do: Enum.map(1..arity, &{:"arg#{&1}", [], Elixir})
def elixir_parameters(arity, false), do: Enum.map(1..arity, &{:"_arg#{&1}", [], Elixir})

@spec erlang_parameters(arity, used :: boolean) :: [{:var, atom()}]
def erlang_parameters(0, _), do: []
def erlang_parameters(arity, true), do: Enum.map(1..arity, &{:var, :"X#{&1}"})
def erlang_parameters(arity, false), do: Enum.map(1..arity, &{:var, :"_X#{&1}"})

# Access behaviour guards
@impl true
def get_and_update(_, _, _), do: raise("you should not update a function")
Expand Down
76 changes: 49 additions & 27 deletions lib/zig/nif/_basic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@ defmodule Zig.Nif.Basic do
alias Zig.Nif.DirtyIo
alias Zig.Nif.Synchronous
alias Zig.Type
alias Zig.Type.Integer

import Zig.QuoteErl

# marshalling setup
#
# for a basic function, a marshalling function is required unless the function ONLY
# has term or erl_nif_term parameters, because those parameters may emit `argumenterror`
# which needs to be trapped and converted.
#
# returning a value cannot error so they do not need to be marshalled, except for
# integers of size greater than 64 bits.

defp marshals_param?(:term), do: false
defp marshals_param?(:erl_nif_term), do: false
defp marshals_param?(_), do: true

defp marshals_return?(%Integer{bits: bits}), do: bits > 64
defp marshals_return?(_), do: false

defp needs_marshal?(nif) do
Enum.any?(nif.signature.params, &Type.marshals_param?/1) or
Type.marshals_return?(nif.signature.return)
Enum.any?(nif.signature.params, &marshals_param?/1) or marshals_return?(nif.signature.return)
end

defp marshal_name(nif), do: :"marshalled-#{nif.name}"
Expand All @@ -33,26 +47,16 @@ defmodule Zig.Nif.Basic do
end

def render_elixir(%{signature: signature} = nif) do
{empty_params, used_params} =
case signature.arity do
0 ->
{[], []}

n ->
1..n
|> Enum.map(&{{:"_arg#{&1}", [], Elixir}, {:"arg#{&1}", [], Elixir}})
|> Enum.unzip()
end

error_text = "nif for function #{signature.name}/#{signature.arity} not bound"

def_or_defp = if nif.export, do: :def, else: :defp

if needs_marshal?(nif) do
render_elixir_marshalled(nif, def_or_defp, empty_params, used_params, error_text)
render_elixir_marshalled(nif, def_or_defp, signature.arity, error_text)
else
unused_params = Nif.elixir_parameters(signature.arity, false)
quote context: Elixir do
unquote(def_or_defp)(unquote(signature.name)(unquote_splicing(empty_params))) do
unquote(def_or_defp)(unquote(signature.name)(unquote_splicing(unused_params))) do
:erlang.nif_error(unquote(error_text))
end
end
Expand All @@ -62,10 +66,13 @@ defmodule Zig.Nif.Basic do
defp render_elixir_marshalled(
%{signature: signature} = nif,
def_or_defp,
empty_params,
used_params,
arity,
error_text
) do

used_params = Nif.elixir_parameters(signature.arity, true)
unused_params = Nif.elixir_parameters(signature.arity, false)

marshal_name = marshal_name(nif)

marshal_params =
Expand All @@ -74,7 +81,7 @@ defmodule Zig.Nif.Basic do
|> Enum.with_index()
|> Enum.flat_map(fn {{param_type, param}, index} ->
List.wrap(
if Type.marshals_param?(param_type) do
if marshals_param?(param_type) do
Type.marshal_param(param_type, param, index, :elixir)
end
)
Expand All @@ -86,20 +93,35 @@ defmodule Zig.Nif.Basic do
end

marshal_return =
if Type.marshals_return?(signature.return) do
if marshals_return?(signature.return) do
Type.marshal_return(signature.return, return, :elixir)
else
return
end

function_code = [do: quote do
unquote_splicing(marshal_params)
return = unquote(marshal_name)(unquote_splicing(used_params))
unquote(marshal_return)
end]

argument_error_prong = List.wrap(if true do
Zig.ErrorProng.argument_error_prong(:elixir)
end)

error_return_prong = []

error_prongs = case {argument_error_prong, error_return_prong} do
{[], []} -> []
_ -> [catch: argument_error_prong ++ error_return_prong]
end

function_block = function_code ++ error_prongs

quote do
unquote(def_or_defp)(unquote(nif.name)(unquote_splicing(used_params))) do
unquote_splicing(marshal_params)
return = unquote(marshal_name)(unquote_splicing(used_params))
unquote(marshal_return)
end
unquote(def_or_defp)(unquote(nif.name)(unquote_splicing(used_params)), unquote(function_block))

defp unquote(marshal_name)(unquote_splicing(empty_params)) do
defp unquote(marshal_name)(unquote_splicing(unused_params)) do
:erlang.nif_error(unquote(error_text))
end
end
Expand All @@ -124,15 +146,15 @@ defmodule Zig.Nif.Basic do
type.params
|> Enum.zip(used_vars)
|> Enum.map_reduce([], fn {param_type, {:var, var}}, so_far ->
if Type.marshals_param?(param_type) do
if marshals_param?(param_type) do
{{:var, :"#{var}_m"}, [so_far, Type.marshal_param(param_type, var, nil, :erlang)]}
else
{{:var, var}, so_far}
end
end)

result_code =
if Type.marshals_param?(type.return) do
if marshals_param?(type.return) do
Type.marshal_return(type.return, :Result, :erlang)
else
"Result"
Expand Down
2 changes: 1 addition & 1 deletion lib/zig/templates/basic.zig.eex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn <%= @name %>(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callc
var error_index: u8 = undefined;

const payload = beam.payload.build(nif.<%= @name %>, argc, args, &error_index, payload_opts) catch |err| {
return e.enif_raise_exception(env, beam.make(.{err, error_index, beam.make_empty_list(.{})}, .{}).v);
return e.enif_raise_exception(env, beam.make(.{err, error_index, error_info}, .{}).v);
};

// defer beam.payload.cleanup(payload, cleanup_opts);
Expand Down
20 changes: 5 additions & 15 deletions lib/zig/type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,14 @@ defprotocol Zig.Type do
alias Zig.Type.Resource

@type t ::
Bool.t() | Enum.t() | Float.t() | Integer.t() | Struct.t() | :env | :pid | :port | :term

@spec marshals_param?(t) :: boolean
@doc "beam-side type conversions that might be necessary to get an elixir parameter into a zig parameter"
def marshals_param?(type)
Array.t() | Bool.t() | Cpointer.t() | Error.t() | Zig.Type.Enum.t() | Float.t() |
Integer.t() | ManyPointer.t() | Optional.t() | Slice.t() | Struct.t() | :void |
:anyopaque_pointer | :env | :pid | :port | :term | :erl_nif_term | :erl_nif_binary |
:erl_nif_event | :erl_nif_binary_pointer | :stacktrace

@spec marshal_param(t, Macro.t(), non_neg_integer, :elixir | :erlang) :: Macro.t()
def marshal_param(type, variable, index, platform)

@spec marshals_return?(t) :: boolean
@doc "beam-side type conversions that might be necessary to get a zig return into an elixir return"
def marshals_return?(type)

@spec marshal_return(t, Macro.t(), Elixir | :erlang) :: Macro.t()
def marshal_return(type, variable, platform)

Expand Down Expand Up @@ -230,17 +225,12 @@ after

def _default_payload_options, do: ".{.error_info = &error_info},"
def _default_return, do: "break :result_block beam.make(result, .{}).v;"
def _default_marshal, do: []
end

defimpl Zig.Type, for: Atom do

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

function marshal_param/4 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

function marshal_return/3 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

function render_zig/1 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

function marshal_param/4 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

function marshal_return/3 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

function render_zig/1 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function marshal_param/4 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function marshal_return/3 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function render_zig/1 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function marshal_param/4 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function marshal_return/3 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)

Check warning on line 231 in lib/zig/type.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

function render_zig/1 required by protocol Zig.Type is not implemented (in module Zig.Type.Atom)
alias Zig.Type

def marshals_param?(_), do: false
def marshals_return?(_), do: false

def marshal_param(_, _, _, _), do: raise("unreachable")
def marshal_return(_, _, _), do: raise("unreachable")

def return_allowed?(type), do: type in ~w(term erl_nif_term pid void)a

def render_return(:void), do: "_ = result; break :result_block beam.make(.ok, .{}).v;"
Expand Down
3 changes: 0 additions & 3 deletions lib/zig/type/bool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,5 @@ defmodule Zig.Type.Bool do
def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()

def marshals_param?(_), do: false
def marshals_return?(_), do: false

def spec(_, _, _), do: Type.spec(:boolean)
end
3 changes: 0 additions & 3 deletions lib/zig/type/cpointer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ defmodule Zig.Type.Cpointer do
end
end

def marshals_param?(_), do: false
def marshals_return?(_), do: false

def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()

Expand Down
8 changes: 4 additions & 4 deletions lib/zig/type/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ defmodule Zig.Type.Enum do
~s(%Zig.Type.Enum{name: "#{enum.name}", tags: #{Kernel.inspect(enum.tags, opts)}})
end


def return_allowed?(_), do: true

def marshals_param?(_), do: false
def marshals_return?(_), do: false


def marshal_param(_, _, _, _), do: Type._default_marshal()
def marshal_return(_, _, _), do: Type._default_marshal()
def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()

Expand Down
2 changes: 0 additions & 2 deletions lib/zig/type/float.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ defmodule Zig.Type.Float do
def spec(_, _, _), do: Type.spec(:float)

def return_allowed?(_), do: true
def marshals_param?(_), do: false
def marshals_return?(_), do: false
def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()
end
6 changes: 0 additions & 6 deletions lib/zig/type/integer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ defmodule Zig.Type.Integer do
concat(["~t(", to_string(type), ")"])
end

def marshals_param?(%{bits: bits}), do: bits > 64
def marshals_return?(%{bits: bits}), do: bits > 64

def marshal_param(type, variable, index, :elixir) do
marshal_param_elixir(type, variable, index)
end
Expand Down Expand Up @@ -224,9 +221,6 @@ defmodule Zig.Type.Integer do

def render_return(type), do: Type._default_return()

Check warning on line 222 in lib/zig/type/integer.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

def marshals_param?(%{bits: bits}), do: bits > 64
def marshals_return?(%{bits: bits}), do: bits > 64

def render_payload_options(type, index, _), do: Type._default_payload_options()

Check warning on line 224 in lib/zig/type/integer.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 224 in lib/zig/type/integer.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)
def render_return(type), do: Type._default_return()
end
2 changes: 0 additions & 2 deletions lib/zig/type/manypointer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ defmodule Zig.Type.Manypointer do
def return_allowed?(pointer), do: pointer.has_sentinel? and Type.return_allowed?(pointer.child)
def missing_size?(_), do: true

def marshals_param?(_), do: false
def marshals_return?(_), do: false
def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()

Expand Down
3 changes: 0 additions & 3 deletions lib/zig/type/optional.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ defmodule Zig.Type.Optional do

def return_allowed?(optional), do: Type.return_allowed?(optional.child)

def marshals_param?(_), do: false
def marshals_return?(_), do: false

def render_payload_options(type, index, _), do: Type._default_payload_options()

Check warning on line 14 in lib/zig/type/optional.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 14 in lib/zig/type/optional.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 14 in lib/zig/type/optional.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 14 in lib/zig/type/optional.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)
def render_return(type), do: Type._default_return()

Expand Down
2 changes: 0 additions & 2 deletions lib/zig/type/slice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ defmodule Zig.Type.Slice do
# ETC

def return_allowed?(slice), do: Type.return_allowed?(slice.child)
def marshals_param?(_), do: false
def marshals_return?(_), do: false
def render_payload_options(type, index, _), do: Type._default_payload_options()
def render_return(type), do: Type._default_return()

Expand Down
2 changes: 0 additions & 2 deletions lib/zig/type/struct.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ defmodule Zig.Type.Struct do
|> Enum.all?()
end

def marshals_param?(_), do: false
def marshals_return?(_), do: false
def render_payload_options(type, index, _), do: Type._default_payload_options()

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "index" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 122 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)
def render_return(type), do: Type._default_return()

Check warning on line 123 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 123 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 123 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 123 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 123 in lib/zig/type/struct.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "type" is unused (if the variable is not meant to be used, prefix it with an underscore)
end
1 change: 1 addition & 0 deletions priv/beam/get.zig
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ inline fn error_line(msg: anytype, opts: anytype) void {
};
@compileError(error_msg);
}

opts.error_info.v = e.enif_make_list_cell(options.env(opts), beam.make(msg, opts).v, opts.error_info.v);
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/integration/types/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ defmodule ZiglerTest.Types.EnumTest do

test "if you try to use an invalid number" do
assert_raise ArgumentError,
"errors were found at the given arguments:\n\n * 1st argument: \n\n note: not an integer value for EnumType (should be one of `[0, 1]`)\n expected: 0 | 1 | :foo | :bar (for `EnumType`)\n got: `42`\n",
"errors were found at the given arguments:\n\n * 1st argument: \n\n note: not an integer value for EnumType (should be one of `[0, 1]`)\n expected: integer (for `u1`)\n got: `42`\n note: out of bounds (0..1)\n expected: 0 | 1 | :foo | :bar (for `EnumType`)\n got: `42`\n",
fn -> untagged_swap(42) end
end
end
Expand Down

0 comments on commit 28fd3e7

Please sign in to comment.