Skip to content

Commit 3f63139

Browse files
author
José Valim
committed
Avoid range creation in Calendar.ISO.valid_date?/3
1 parent 0089a40 commit 3f63139

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

lib/elixir/lib/calendar/iso.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ defmodule Calendar.ISO do
2828
@seconds_per_hour 60 * 60
2929
# Note that this does _not_ handle leap seconds.
3030
@seconds_per_day 24 * 60 * 60
31+
@last_second_of_the_day @seconds_per_day - 1
3132
@microseconds_per_second 1_000_000
3233
@parts_per_day @seconds_per_day * @microseconds_per_second
3334

@@ -499,7 +500,8 @@ defmodule Calendar.ISO do
499500
@impl true
500501
@spec valid_date?(year, month, day) :: boolean
501502
def valid_date?(year, month, day) do
502-
month in 1..12 and year in -9999..9999 and day in 1..days_in_month(year, month)
503+
month in 1..12 and year in -9999..9999 and
504+
(is_integer(day) and day >= 1 and day <= days_in_month(year, month))
503505
end
504506

505507
@doc """
@@ -840,7 +842,7 @@ defmodule Calendar.ISO do
840842
{date, time}
841843
end
842844

843-
defp seconds_to_time(seconds) when seconds in 0..(@seconds_per_day - 1) do
845+
defp seconds_to_time(seconds) when seconds in 0..@last_second_of_the_day do
844846
{hour, rest_seconds} = div_mod(seconds, @seconds_per_hour)
845847
{minute, second} = div_mod(rest_seconds, @seconds_per_minute)
846848

lib/elixir/lib/kernel.ex

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,26 +3027,30 @@ defmodule Kernel do
30273027
true
30283028
30293029
"""
3030-
defmacro first..last when is_integer(first) and is_integer(last) do
3030+
defmacro first..last do
3031+
first = Macro.expand(first, __CALLER__)
3032+
last = Macro.expand(last, __CALLER__)
3033+
range(__CALLER__.context, first, last)
3034+
end
3035+
3036+
defp range(_context, first, last) when is_integer(first) and is_integer(last) do
30313037
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]}
30323038
end
30333039

3034-
defmacro first..last
3040+
defp range(_context, first, last)
30353041
when is_float(first) or is_float(last) or is_atom(first) or is_atom(last) or
30363042
is_binary(first) or is_binary(last) or is_list(first) or is_list(last) do
30373043
raise ArgumentError,
30383044
"ranges (first..last) expect both sides to be integers, " <>
30393045
"got: #{Macro.to_string({:.., [], [first, last]})}"
30403046
end
30413047

3042-
defmacro first..last do
3043-
case __CALLER__.context do
3044-
nil ->
3045-
quote(do: Elixir.Range.new(unquote(first), unquote(last)))
3048+
defp range(nil, first, last) do
3049+
quote(do: Elixir.Range.new(unquote(first), unquote(last)))
3050+
end
30463051

3047-
_ ->
3048-
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]}
3049-
end
3052+
defp range(_, first, last) do
3053+
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]}
30503054
end
30513055

30523056
@doc """

lib/elixir/lib/macro.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,12 @@ defmodule Macro do
12551255
next = :erlang.unique_integer()
12561256
{:elixir_quote.linify_with_context_counter(0, {receiver, next}, quoted), true}
12571257

1258+
{:ok, Kernel, op, [arg]} when op in [:+, :-] ->
1259+
case expand_once(arg, env) do
1260+
integer when is_integer(integer) -> {apply(Kernel, op, [integer]), true}
1261+
_ -> {original, false}
1262+
end
1263+
12581264
{:ok, _receiver, _name, _args} ->
12591265
{original, false}
12601266

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,7 @@ defmodule KernelTest do
440440
end
441441
end
442442

443-
test "is optimized" do
444-
assert expand_to_string(quote(do: foo in [])) == "Enum.member?([], foo)"
445-
443+
test "hoists variables" do
446444
result = expand_to_string(quote(do: rand() in 1..2))
447445
assert result =~ "var = rand()"
448446

@@ -463,6 +461,16 @@ defmodule KernelTest do
463461
assert result =~ ":erlang.orelse(:erlang.\"=:=\"(var, 1), :lists.member(var, var0))"
464462
end
465463

464+
test "is optimized" do
465+
assert expand_to_string(quote(do: foo in [])) == "Enum.member?([], foo)"
466+
467+
assert expand_to_string(quote(do: foo in 0..1)) ==
468+
":erlang.andalso(:erlang.is_integer(foo), :erlang.andalso(:erlang.>=(foo, 0), :erlang.\"=<\"(foo, 1)))"
469+
470+
assert expand_to_string(quote(do: foo in -1..0)) ==
471+
":erlang.andalso(:erlang.is_integer(foo), :erlang.andalso(:erlang.>=(foo, -1), :erlang.\"=<\"(foo, 0)))"
472+
end
473+
466474
defp expand_to_string(ast) do
467475
ast
468476
|> Macro.prewalk(&Macro.expand(&1, __ENV__))

0 commit comments

Comments
 (0)