Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5001,3 +5001,69 @@ defimpl Enumerable, for: Function do
description: "only anonymous functions of arity 2 are enumerable"
end
end

defimpl Enumerable, for: Range do
def reduce(first..last//step, acc, fun) do
reduce(first, last, acc, fun, step)
end

# TODO: Remove me on v2.0
def reduce(%{__struct__: Range, first: first, last: last} = range, acc, fun) do
step = if first <= last, do: 1, else: -1
reduce(Map.put(range, :step, step), acc, fun)
end

defp reduce(_first, _last, {:halt, acc}, _fun, _step) do
{:halted, acc}
end

defp reduce(first, last, {:suspend, acc}, fun, step) do
{:suspended, acc, &reduce(first, last, &1, fun, step)}
end

defp reduce(first, last, {:cont, acc}, fun, step)
when step > 0 and first <= last
when step < 0 and first >= last do
reduce(first + step, last, fun.(first, acc), fun, step)
end

defp reduce(_, _, {:cont, acc}, _fun, _up) do
{:done, acc}
end

def member?(first..last//step, value) when is_integer(value) do
if step > 0 do
{:ok, first <= value and value <= last and rem(value - first, step) == 0}
else
{:ok, last <= value and value <= first and rem(value - first, step) == 0}
end
end

# TODO: Remove me on v2.0
def member?(%{__struct__: Range, first: first, last: last} = range, value)
when is_integer(value) do
step = if first <= last, do: 1, else: -1
member?(Map.put(range, :step, step), value)
end

def member?(_, _value) do
{:ok, false}
end

def count(range) do
{:ok, Range.size(range)}
end

def slice(first.._//step = range) do
{:ok, Range.size(range), &slice(first + &1 * step, step + &3 - 1, &2)}
end

# TODO: Remove me on v2.0
def slice(%{__struct__: Range, first: first, last: last} = range) do
step = if first <= last, do: 1, else: -1
slice(Map.put(range, :step, step))
end

defp slice(_current, _step, 0), do: []
defp slice(current, step, remaining), do: [current | slice(current + step, step, remaining - 1)]
end
68 changes: 54 additions & 14 deletions lib/elixir/lib/inspect.ex
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,13 @@ defprotocol Inspect do
do: var!(info)

var!(name) = Macro.inspect_atom(:literal, unquote(module))
unquote(inspect_module).inspect(var!(struct), var!(name), var!(infos), var!(opts))

unquote(inspect_module).inspect_as_struct(
var!(struct),
var!(name),
var!(infos),
var!(opts)
)
end
end
end
Expand Down Expand Up @@ -390,6 +396,10 @@ end

defimpl Inspect, for: Map do
def inspect(map, opts) do
inspect_as_map(map, opts)
end

def inspect_as_map(map, opts) do
list =
if Keyword.get(opts.custom_options, :sort_maps) do
map |> Map.to_list() |> :lists.sort()
Expand All @@ -408,7 +418,7 @@ defimpl Inspect, for: Map do
map_container_doc(list, "", opts, fun)
end

def inspect(map, name, infos, opts) do
def inspect_as_struct(map, name, infos, opts) do
fun = fn %{field: field}, opts -> Inspect.List.keyword({field, Map.get(map, field)}, opts) end
map_container_doc(infos, name, opts, fun)
end
Expand Down Expand Up @@ -599,25 +609,36 @@ end
defimpl Inspect, for: Any do
def inspect(%module{} = struct, opts) do
try do
{module.__struct__(), module.__info__(:struct)}
module.__info__(:struct)
rescue
_ -> Inspect.Map.inspect(struct, opts)
_ -> Inspect.Map.inspect_as_map(struct, opts)
else
{dunder, fields} ->
if Map.keys(dunder) == Map.keys(struct) do
infos =
for %{field: field} = info <- fields,
field not in [:__struct__, :__exception__],
do: info

Inspect.Map.inspect(struct, Macro.inspect_atom(:literal, module), infos, opts)
info ->
if valid_struct?(info, struct) do
info =
for %{field: field} = map <- info,
field != :__exception__,
do: map

Inspect.Map.inspect_as_struct(struct, Macro.inspect_atom(:literal, module), info, opts)
else
Inspect.Map.inspect(struct, opts)
Inspect.Map.inspect_as_map(struct, opts)
end
end
end

def inspect(map, name, infos, opts) do
defp valid_struct?(info, struct), do: valid_struct?(info, struct, map_size(struct) - 1)

defp valid_struct?([%{field: field} | info], struct, count) when is_map_key(struct, field),
do: valid_struct?(info, struct, count - 1)

defp valid_struct?([], _struct, 0),
do: true

defp valid_struct?(_fields, _struct, _count),
do: false

def inspect_as_struct(map, name, infos, opts) do
open = color_doc("#" <> name <> "<", :map, opts)
sep = color_doc(",", :map, opts)
close = color_doc(">", :map, opts)
Expand All @@ -631,6 +652,25 @@ defimpl Inspect, for: Any do
end
end

defimpl Inspect, for: Range do
import Inspect.Algebra
import Kernel, except: [inspect: 2]

def inspect(first..last//1, opts) when last >= first do
concat([to_doc(first, opts), "..", to_doc(last, opts)])
end

def inspect(first..last//step, opts) do
concat([to_doc(first, opts), "..", to_doc(last, opts), "//", to_doc(step, opts)])
end

# TODO: Remove me on v2.0
def inspect(%{__struct__: Range, first: first, last: last} = range, opts) do
step = if first <= last, do: 1, else: -1
inspect(Map.put(range, :step, step), opts)
end
end

require Protocol

Protocol.derive(
Expand Down
6 changes: 3 additions & 3 deletions lib/elixir/lib/inspect/algebra.ex
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,14 @@ defmodule Inspect.Algebra do
# we won't try to render any failed instruct when building
# the error message.
if Process.get(:inspect_trap) do
Inspect.Map.inspect(struct, opts)
Inspect.Map.inspect_as_map(struct, opts)
else
try do
Process.put(:inspect_trap, true)

inspected_struct =
struct
|> Inspect.Map.inspect(%{
|> Inspect.Map.inspect_as_map(%{
opts
| syntax_colors: [],
inspect_fun: Inspect.Opts.default_inspect_fun()
Expand All @@ -389,7 +389,7 @@ defmodule Inspect.Algebra do
end
end
else
Inspect.Map.inspect(struct, opts)
Inspect.Map.inspect_as_map(struct, opts)
end
end

Expand Down
25 changes: 14 additions & 11 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3409,7 +3409,7 @@ defmodule Kernel do

"""
defmacro to_charlist(term) do
quote(do: List.Chars.to_charlist(unquote(term)))
quote(do: :"Elixir.List.Chars".to_charlist(unquote(term)))
end

@doc """
Expand Down Expand Up @@ -4064,7 +4064,7 @@ defmodule Kernel do
-1
end

{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]}
{:%, [], [Elixir.Range, {:%{}, [], [first: first, last: last, step: step]}]}
end

defp stepless_range(nil, first, last, _caller) do
Expand All @@ -4090,7 +4090,7 @@ defmodule Kernel do
Macro.Env.stacktrace(caller)
)

{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]}
{:%, [], [Elixir.Range, {:%{}, [], [first: first, last: last, step: step]}]}
end

defp stepless_range(:match, first, last, caller) do
Expand All @@ -4103,7 +4103,7 @@ defmodule Kernel do
Macro.Env.stacktrace(caller)
)

{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]}
{:%, [], [Elixir.Range, {:%{}, [], [first: first, last: last]}]}
end

@doc """
Expand Down Expand Up @@ -4142,14 +4142,14 @@ defmodule Kernel do
range(__CALLER__.context, first, last, step)

false ->
range(__CALLER__.context, first, last, step)
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]}
end
end

defp range(context, first, last, step)
when is_integer(first) and is_integer(last) and is_integer(step)
when context != nil do
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last, step: step]}
{:%, [], [Elixir.Range, {:%{}, [], [first: first, last: last, step: step]}]}
end

defp range(nil, first, last, step) do
Expand Down Expand Up @@ -4553,11 +4553,10 @@ defmodule Kernel do
raise ArgumentError, "found unescaped value on the right side of in/2: #{inspect(right)}"

right ->
with {:%{}, _meta, fields} <- right,
[__struct__: Elixir.Range, first: first, last: last, step: step] <-
:lists.usort(fields) do
in_var(in_body?, left, &in_range(&1, expand.(first), expand.(last), expand.(step)))
else
case range_fields(right) do
[first: first, last: last, step: step] ->
in_var(in_body?, left, &in_range(&1, expand.(first), expand.(last), expand.(step)))

_ when in_body? ->
quote(do: Elixir.Enum.member?(unquote(right), unquote(left)))

Expand All @@ -4567,6 +4566,10 @@ defmodule Kernel do
end
end

defp range_fields({:%, _, [Elixir.Range, {:%{}, _, fields}]}), do: :lists.usort(fields)
defp range_fields({:%{}, _, [__struct__: Elixir.Range] ++ fields}), do: :lists.usort(fields)
defp range_fields(_), do: []

defp raise_on_invalid_args_in_2(right) do
raise ArgumentError, <<
"invalid right argument for operator \"in\", it expects a compile-time proper list ",
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/module/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ defmodule Module.Types do
defp default_domain({_, arity} = fun_arity, impl) do
with {for, callbacks} <- impl,
true <- fun_arity in callbacks do
[Module.Types.Of.impl(for) | List.duplicate(Descr.dynamic(), arity - 1)]
[Descr.dynamic(Module.Types.Of.impl(for)) | List.duplicate(Descr.dynamic(), arity - 1)]
else
_ -> List.duplicate(Descr.dynamic(), arity)
end
Expand Down
Loading
Loading