Skip to content

Commit b82d2df

Browse files
committed
Reduce allocation on Inspect.Algebra by using cons cells
1 parent 443d96a commit b82d2df

File tree

5 files changed

+63
-41
lines changed

5 files changed

+63
-41
lines changed

lib/elixir/lib/code/formatter.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2500,16 +2500,16 @@ defmodule Code.Formatter do
25002500

25012501
# Relying on the inner document is brittle and error prone.
25022502
# It would be best if we had a mechanism to apply this.
2503-
defp concat_to_last_group({:doc_cons, left, right}, concat) do
2504-
{:doc_cons, left, concat_to_last_group(right, concat)}
2503+
defp concat_to_last_group([left | right], concat) do
2504+
[left | concat_to_last_group(right, concat)]
25052505
end
25062506

25072507
defp concat_to_last_group({:doc_group, group, mode}, concat) do
2508-
{:doc_group, {:doc_cons, group, concat}, mode}
2508+
{:doc_group, concat(group, concat), mode}
25092509
end
25102510

25112511
defp concat_to_last_group(other, concat) do
2512-
{:doc_cons, other, concat}
2512+
concat(other, concat)
25132513
end
25142514

25152515
defp ungroup_if_group({:doc_group, group, _mode}), do: group

lib/elixir/lib/inspect.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ defimpl Inspect, for: BitString do
326326
end
327327

328328
defp each_bit(<<>>, _counter, _opts) do
329-
:doc_nil
329+
Inspect.Algebra.empty()
330330
end
331331

332332
defp each_bit(<<h::8>>, _counter, opts) do

lib/elixir/lib/inspect/algebra.ex

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ defmodule Inspect.Algebra do
181181
additions, like support for binary nodes and a break mode that
182182
maximises use of horizontal space.
183183
184-
iex> Inspect.Algebra.empty()
185-
:doc_nil
184+
iex> Inspect.Algebra.line()
185+
:doc_line
186186
187187
iex> "foo"
188188
"foo"
@@ -251,19 +251,34 @@ defmodule Inspect.Algebra do
251251

252252
@type t ::
253253
binary
254-
| :doc_line
255-
| :doc_nil
254+
| doc_nil
255+
| doc_cons
256+
| doc_line
256257
| doc_break
257258
| doc_collapse
258259
| doc_color
259-
| doc_cons
260260
| doc_fits
261261
| doc_force
262262
| doc_group
263263
| doc_nest
264264
| doc_string
265265
| doc_limit
266266

267+
@typep doc_nil :: []
268+
defmacrop doc_nil do
269+
[]
270+
end
271+
272+
@typep doc_line :: :doc_line
273+
defmacrop doc_line do
274+
:doc_line
275+
end
276+
277+
@typep doc_cons :: nonempty_improper_list(t, t)
278+
defmacrop doc_cons(left, right) do
279+
quote do: [unquote(left) | unquote(right)]
280+
end
281+
267282
@typep doc_string :: {:doc_string, t, non_neg_integer}
268283
defmacrop doc_string(string, length) do
269284
quote do: {:doc_string, unquote(string), unquote(length)}
@@ -274,11 +289,6 @@ defmodule Inspect.Algebra do
274289
quote do: {:doc_limit, unquote(doc), unquote(limit)}
275290
end
276291

277-
@typep doc_cons :: {:doc_cons, t, t}
278-
defmacrop doc_cons(left, right) do
279-
quote do: {:doc_cons, unquote(left), unquote(right)}
280-
end
281-
282292
@typep doc_nest :: {:doc_nest, t, :cursor | :reset | non_neg_integer, :always | :break}
283293
defmacrop doc_nest(doc, indent, always_or_break) do
284294
quote do: {:doc_nest, unquote(doc), unquote(indent), unquote(always_or_break)}
@@ -328,7 +338,7 @@ defmodule Inspect.Algebra do
328338
]
329339

330340
defguard is_doc(doc)
331-
when is_binary(doc) or doc in [:doc_nil, :doc_line] or
341+
when is_list(doc) or is_binary(doc) or doc == doc_line() or
332342
(is_tuple(doc) and elem(doc, 0) in @docs)
333343

334344
defguardp is_width(width) when width == :infinity or (is_integer(width) and width >= 0)
@@ -508,8 +518,8 @@ defmodule Inspect.Algebra do
508518

509519
group =
510520
case flex? do
511-
true -> group(concat(concat(left, nest(docs, 1)), right))
512-
false -> group(glue(nest(glue(left, "", docs), 2), "", right))
521+
true -> doc_group(concat(concat(left, nest(docs, 1)), right), :normal)
522+
false -> doc_group(glue(nest(glue(left, "", docs), 2), "", right), :normal)
513523
end
514524

515525
{group, inspect_opts}
@@ -550,21 +560,21 @@ defmodule Inspect.Algebra do
550560

551561
case fun.(term, changed_opts) do
552562
{doc, %Inspect.Opts{} = opts} -> {doc, opts}
553-
:doc_nil -> {:doc_nil, opts}
563+
doc_nil() -> {doc_nil(), opts}
554564
doc -> {doc, changed_opts}
555565
end
556566
end
557567

558-
defp join(:doc_nil, :doc_nil, _, _), do: :doc_nil
559-
defp join(left, :doc_nil, _, _), do: left
560-
defp join(:doc_nil, right, _, _), do: right
568+
defp join(doc_nil(), doc_nil(), _, _), do: doc_nil()
569+
defp join(left, doc_nil(), _, _), do: left
570+
defp join(doc_nil(), right, _, _), do: right
561571
defp join(left, right, true, sep), do: flex_glue(concat(left, sep), right)
562572
defp join(left, right, false, sep), do: glue(concat(left, sep), right)
563573

564574
defp simple?(doc_cons(left, right)), do: simple?(left) and simple?(right)
565575
defp simple?(doc_color(doc, _)), do: simple?(doc)
566576
defp simple?(doc_string(_, _)), do: true
567-
defp simple?(:doc_nil), do: true
577+
defp simple?(doc_nil()), do: true
568578
defp simple?(other), do: is_binary(other)
569579

570580
@doc false
@@ -615,17 +625,29 @@ defmodule Inspect.Algebra do
615625

616626
# Algebra API
617627

628+
@compile {:inline,
629+
empty: 0,
630+
concat: 2,
631+
break: 0,
632+
break: 1,
633+
glue: 2,
634+
glue: 3,
635+
flex_break: 0,
636+
flex_break: 1,
637+
flex_glue: 2,
638+
flex_glue: 3}
639+
618640
@doc """
619641
Returns a document entity used to represent nothingness.
620642
621643
## Examples
622644
623645
iex> Inspect.Algebra.empty()
624-
:doc_nil
646+
[]
625647
626648
"""
627-
@spec empty() :: :doc_nil
628-
def empty, do: :doc_nil
649+
@spec empty() :: doc_nil()
650+
def empty, do: doc_nil()
629651

630652
@doc ~S"""
631653
Creates a document represented by string.
@@ -1023,7 +1045,7 @@ defmodule Inspect.Algebra do
10231045
"""
10241046
@doc since: "1.6.0"
10251047
@spec line() :: t
1026-
def line(), do: :doc_line
1048+
def line(), do: doc_line()
10271049

10281050
@doc ~S"""
10291051
Inserts a mandatory linebreak between two documents.
@@ -1163,20 +1185,20 @@ defmodule Inspect.Algebra do
11631185
do: fits?(w, k, b?, [{i, :break_no_flat, x} | t])
11641186

11651187
defp fits?(_, _, _, [{_, :break_no_flat, doc_break(_, _)} | _]), do: true
1166-
defp fits?(_, _, _, [{_, :break_no_flat, :doc_line} | _]), do: true
1188+
defp fits?(_, _, _, [{_, :break_no_flat, doc_line()} | _]), do: true
11671189

11681190
## Breaks
11691191

11701192
defp fits?(_, _, _, [{_, :break, doc_break(_, _)} | _]), do: true
1171-
defp fits?(_, _, _, [{_, :break, :doc_line} | _]), do: true
1193+
defp fits?(_, _, _, [{_, :break, doc_line()} | _]), do: true
11721194

11731195
defp fits?(w, k, b?, [{i, :break, doc_group(x, _)} | t]),
11741196
do: fits?(w, k, b?, [{i, :flat, x} | {:tail, b?, t}])
11751197

11761198
## Catch all
11771199

1178-
defp fits?(w, _, _, [{i, _, :doc_line} | t]), do: fits?(w, i, false, t)
1179-
defp fits?(w, k, b?, [{_, _, :doc_nil} | t]), do: fits?(w, k, b?, t)
1200+
defp fits?(w, _, _, [{i, _, doc_line()} | t]), do: fits?(w, i, false, t)
1201+
defp fits?(w, k, b?, [{_, _, doc_nil()} | t]), do: fits?(w, k, b?, t)
11801202
defp fits?(w, _, b?, [{i, _, doc_collapse(_)} | t]), do: fits?(w, i, b?, t)
11811203
defp fits?(w, k, b?, [{i, m, doc_color(x, _)} | t]), do: fits?(w, k, b?, [{i, m, x} | t])
11821204
defp fits?(w, k, b?, [{_, _, doc_string(_, l)} | t]), do: fits?(w, k + l, b?, t)
@@ -1206,8 +1228,8 @@ defmodule Inspect.Algebra do
12061228
[{integer, mode, t} | :group_over]
12071229
) :: [binary]
12081230
defp format(_, _, []), do: []
1209-
defp format(w, k, [{_, _, :doc_nil} | t]), do: format(w, k, t)
1210-
defp format(w, _, [{i, _, :doc_line} | t]), do: [indent(i) | format(w, i, t)]
1231+
defp format(w, k, [{_, _, doc_nil()} | t]), do: format(w, k, t)
1232+
defp format(w, _, [{i, _, doc_line()} | t]), do: [indent(i) | format(w, i, t)]
12111233
defp format(w, k, [{i, m, doc_cons(x, y)} | t]), do: format(w, k, [{i, m, x}, {i, m, y} | t])
12121234
defp format(w, k, [{i, m, doc_color(x, c)} | t]), do: [c | format(w, k, [{i, m, x} | t])]
12131235
defp format(w, k, [{_, _, doc_string(s, l)} | t]), do: [s | format(w, k + l, t)]

lib/elixir/test/elixir/inspect/algebra_test.exs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule Inspect.AlgebraTest do
4040

4141
test "empty doc" do
4242
# Consistent with definitions
43-
assert empty() == :doc_nil
43+
assert empty() == []
4444

4545
# Consistent formatting
4646
assert render(empty(), 80) == ""
@@ -74,7 +74,7 @@ defmodule Inspect.AlgebraTest do
7474

7575
test "glue doc" do
7676
# Consistent with definitions
77-
assert glue("a", "->", "b") == {:doc_cons, "a", {:doc_cons, {:doc_break, "->", :strict}, "b"}}
77+
assert glue("a", "->", "b") == ["a", {:doc_break, "->", :strict} | "b"]
7878
assert glue("a", "b") == glue("a", " ", "b")
7979

8080
# Wrong argument type
@@ -84,7 +84,7 @@ defmodule Inspect.AlgebraTest do
8484
test "flex glue doc" do
8585
# Consistent with definitions
8686
assert flex_glue("a", "->", "b") ==
87-
{:doc_cons, "a", {:doc_cons, {:doc_break, "->", :flex}, "b"}}
87+
["a", {:doc_break, "->", :flex} | "b"]
8888

8989
assert flex_glue("a", "b") == flex_glue("a", " ", "b")
9090

@@ -107,13 +107,13 @@ defmodule Inspect.AlgebraTest do
107107

108108
test "space doc" do
109109
# Consistent with definitions
110-
assert space("a", "b") == {:doc_cons, "a", {:doc_cons, " ", "b"}}
110+
assert space("a", "b") == ["a", " " | "b"]
111111
end
112112

113113
test "always nest doc" do
114114
# Consistent with definitions
115115
assert nest(empty(), 1) == {:doc_nest, empty(), 1, :always}
116-
assert nest(empty(), 0) == :doc_nil
116+
assert nest(empty(), 0) == []
117117

118118
# Wrong argument type
119119
assert_raise FunctionClauseError, fn -> nest("foo", empty()) end
@@ -127,7 +127,7 @@ defmodule Inspect.AlgebraTest do
127127
test "break nest doc" do
128128
# Consistent with definitions
129129
assert nest(empty(), 1, :break) == {:doc_nest, empty(), 1, :break}
130-
assert nest(empty(), 0, :break) == :doc_nil
130+
assert nest(empty(), 0, :break) == []
131131

132132
# Wrong argument type
133133
assert_raise FunctionClauseError, fn -> nest("foo", empty(), :break) end
@@ -188,7 +188,7 @@ defmodule Inspect.AlgebraTest do
188188

189189
test "line doc" do
190190
# Consistent with definitions
191-
assert line("a", "b") == {:doc_cons, "a", {:doc_cons, :doc_line, "b"}}
191+
assert line("a", "b") == ["a", :doc_line | "b"]
192192

193193
# Consistent formatting
194194
assert render(line(glue("aaa", "bbb"), glue("ccc", "ddd")), 10) == "aaa bbb\nccc ddd"

lib/elixir/test/elixir/kernel/alias_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ defmodule Kernel.AliasTest do
6262
result = alias unquote(Inspect).{Opts, Algebra}
6363
assert result == [Inspect.Opts, Inspect.Algebra]
6464
assert %Opts{} == %Inspect.Opts{}
65-
assert Algebra.empty() == :doc_nil
65+
assert Algebra.empty() == []
6666
end
6767

6868
test "alias removal" do

0 commit comments

Comments
 (0)