Skip to content

Commit

Permalink
Translate with/1 as a closure (elixir-lang#13299)
Browse files Browse the repository at this point in the history
* Translate with/1 as a closure

* Emit leaner code on with var <-
  • Loading branch information
sabiwara authored Jan 30, 2024
1 parent 5e13403 commit 0b95ca4
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 36 deletions.
9 changes: 7 additions & 2 deletions lib/elixir/src/elixir_clauses.erl
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,16 @@ with(Meta, Args, S, E) ->

{{with, Meta, EExprs ++ [[{do, EDo} | EOpts]]}, S3, E}.

expand_with({'<-', Meta, [Left, Right]}, {S, E, _HasMatch}) ->
expand_with({'<-', Meta, [Left, Right]}, {S, E, HasMatch}) ->
{ERight, SR, ER} = elixir_expand:expand(Right, S, E),
SM = elixir_env:reset_read(SR, S),
{[ELeft], SL, EL} = head([Left], SM, ER),
{{'<-', Meta, [ELeft, ERight]}, {SL, EL, true}};
NewHasMatch =
case ELeft of
{Var, _, Ctx} when is_atom(Var), is_atom(Ctx) -> HasMatch;
_ -> true
end,
{{'<-', Meta, [ELeft, ERight]}, {SL, EL, NewHasMatch}};
expand_with(Expr, {S, E, HasMatch}) ->
{EExpr, SE, EE} = elixir_expand:expand(Expr, S, E),
{EExpr, {SE, EE, HasMatch}}.
Expand Down
62 changes: 28 additions & 34 deletions lib/elixir/src/elixir_erl_pass.erl
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,15 @@ translate({for, Meta, [_ | _] = Args}, _Ann, S) ->
elixir_erl_for:translate(Meta, Args, S);

translate({with, Meta, [_ | _] = Args}, _Ann, S) ->
Ann = ?ann(Meta),
{Exprs, [{do, Do} | Opts]} = elixir_utils:split_last(Args),
{ElseClause, SE} = translate_with_else(Meta, Opts, S),
translate_with_do(Exprs, ?ann(Meta), Do, ElseClause, SE);
{ElseClause, MaybeFun, SE} = translate_with_else(Meta, Opts, S),
{Case, SD} = translate_with_do(Exprs, Ann, Do, ElseClause, SE),

case MaybeFun of
nil -> {Case, SD};
FunAssign -> {{block, Ann, [FunAssign, Case]}, SD}
end;

%% Variables

Expand Down Expand Up @@ -400,58 +406,46 @@ returns_boolean(Condition, Body) ->
%% with

translate_with_else(Meta, [], S) ->
Generated = ?ann(?generated(Meta)),
Ann = ?ann(Meta),
{VarName, SC} = elixir_erl_var:build('_', S),
Var = {var, Generated, VarName},
{{clause, Generated, [Var], [], [Var]}, SC};
Var = {var, Ann, VarName},
{{clause, Ann, [Var], [], [Var]}, nil, SC};
translate_with_else(Meta, [{'else', [{'->', _, [[{Var, VarMeta, Kind}], Clause]}]}], S) when is_atom(Var), is_atom(Kind) ->
Ann = ?ann(Meta),
Generated = erl_anno:set_generated(true, Ann),
{ElseVarErl, SV} = elixir_erl_var:translate(VarMeta, Var, Kind, S#elixir_erl{context=match}),
{TranslatedClause, SC} = translate(Clause, Ann, SV#elixir_erl{context=nil}),
{{clause, Generated, [ElseVarErl], [], [TranslatedClause]}, SC};
Clauses = [{clause, Ann, [ElseVarErl], [], [TranslatedClause]}],
with_else_closure(Meta, Clauses, SC);
translate_with_else(Meta, [{'else', Else}], S) ->
Generated = ?generated(Meta),
{ElseVarEx, ElseVarErl, SE} = elixir_erl_var:assign(Generated, S),
{RaiseVar, _, SV} = elixir_erl_var:assign(Generated, SE),
{RaiseVar, _, SV} = elixir_erl_var:assign(Generated, S),

RaiseExpr = {{'.', Generated, [erlang, error]}, Generated, [{else_clause, RaiseVar}]},
RaiseClause = {'->', Generated, [[RaiseVar], RaiseExpr]},
GeneratedElse = [build_generated_clause(Generated, ElseClause) || ElseClause <- Else],

Case = {'case', Generated, [ElseVarEx, [{do, GeneratedElse ++ [RaiseClause]}]]},
{TranslatedCase, SC} = translate(Case, ?ann(Meta), SV),
{{clause, ?ann(Generated), [ElseVarErl], [], [TranslatedCase]}, SC}.

build_generated_clause(Generated, {'->', _, [Args, Clause]}) ->
NewArgs = [build_generated_clause_arg(Generated, Arg) || Arg <- Args],
{'->', Generated, [NewArgs, Clause]}.

build_generated_clause_arg(Generated, Arg) ->
{Expr, Guards} = elixir_utils:extract_guards(Arg),
NewGuards = [build_generated_guard(Generated, Guard) || Guard <- Guards],
concat_guards(Generated, Expr, NewGuards).

build_generated_guard(Generated, {{'.', _, _} = Call, _, Args}) ->
{Call, Generated, [build_generated_guard(Generated, Arg) || Arg <- Args]};
build_generated_guard(_, Expr) ->
Expr.

concat_guards(_Meta, Expr, []) ->
Expr;
concat_guards(Meta, Expr, [Guard | Tail]) ->
{'when', Meta, [Expr, concat_guards(Meta, Guard, Tail)]}.
Clauses = elixir_erl_clauses:get_clauses('else', [{'else', Else ++ [RaiseClause]}], match),
{TranslatedClauses, SC} = elixir_erl_clauses:clauses(Clauses, SV),
with_else_closure(Meta, TranslatedClauses, SC).

with_else_closure(Meta, TranslatedClauses, S) ->
Ann = ?ann(Meta),
{_, FunErlVar, SC} = elixir_erl_var:assign(Meta, S),
{_, ArgErlVar, SA} = elixir_erl_var:assign(Meta, SC),
FunAssign = {match, Ann, FunErlVar, {'fun', Ann, {clauses, TranslatedClauses}}},
FunCall = {call, Ann, FunErlVar, [ArgErlVar]},
{{clause, Ann, [ArgErlVar], [], [FunCall]}, FunAssign, SA}.

translate_with_do([{'<-', Meta, [{Var, _, Ctx} = Left, Expr]} | Rest], Ann, Do, Else, S) when is_atom(Var), is_atom(Ctx) ->
translate_with_do([{'=', Meta, [Left, Expr]} | Rest], Ann, Do, Else, S);
translate_with_do([{'<-', Meta, [Left, Expr]} | Rest], _Ann, Do, Else, S) ->
Ann = ?ann(Meta),
{Args, Guards} = elixir_utils:extract_guards(Left),
{TExpr, SR} = translate(Expr, Ann, S),
{TArgs, SA} = elixir_erl_clauses:match(Ann, fun translate/3, Args, SR),
TGuards = elixir_erl_clauses:guards(Ann, Guards, SA#elixir_erl.extra_guards, SA),
{TBody, SB} = translate_with_do(Rest, Ann, Do, Else, SA#elixir_erl{extra_guards=[]}),

Clause = {clause, Ann, [TArgs], TGuards, unblock(TBody)},
{{'case', erl_anno:set_generated(true, Ann), TExpr, [Clause, Else]}, SB};
{{'case', Ann, TExpr, [Clause, Else]}, SB};
translate_with_do([Expr | Rest], Ann, Do, Else, S) ->
{TExpr, TS} = translate(Expr, Ann, S),
{TRest, RS} = translate_with_do(Rest, Ann, Do, Else, TS),
Expand Down

0 comments on commit 0b95ca4

Please sign in to comment.