Repository: JuliaLang/julia
File: JuliaLowering/src/desugaring.jl (Julia)

Code before:
                                  kwcall_arg_names, kwcall_arg_types, first_default, arg_defaults)
    end

    positional_forwarding_args = if has_slurp
        a = copy(arg_names)
        a[end] = @ast ctx a[end] [K"..." a[end]]
        a
    else
        arg_names
    end

    #--------------------------------------------------
    # Construct the "main kwcall overload" which unpacks keywords and checks
    # their consistency before dispatching to the user's code in the body
    # method.
    defaults_depend_on_kw_names = any(val->contains_identifier(val, kw_names), kw_defaults)
    defaults_have_assign = any(val->contains_unquoted(e->kind(e) == K"=", val), kw_defaults)
    use_ssa_kw_temps = !defaults_depend_on_kw_names && !defaults_have_assign

    if use_ssa_kw_temps
        kw_val_stmts = SyntaxList(ctx)
        for n in kw_names
            # If not using slots for the keyword argument values, still declare
            # them for reflection purposes.
            local_n = @ast ctx n [K"local" n]
            push!(kw_val_stmts, setmeta!(local_n, :is_internal, true))
        end
        kw_val_vars = SyntaxList(ctx)
        for val in kw_values
            v = emit_assign_tmp(kw_val_stmts, ctx, val, "kwval")
            push!(kw_val_vars, v)
        end
    else
        # Use kw_names directly as the values, but exclude slurp if present
        # because slurp is passed via remaining_kws
        if has_kw_slurp
            kw_val_vars = SyntaxList(ctx)
            for i in 1:length(kw_names)-1
                push!(kw_val_vars, kw_names[i])
            end
        else
            kw_val_vars = kw_names
        end
    end

    kwcall_body_tail = @ast ctx keywords [K"block"
        if has_kw_slurp
            # Slurp remaining keywords into last arg
            remaining_kws := [K"call"
                "pairs"::K"top"
                if isempty(kw_name_syms)

Review comment (refactor):
Why the mutating variant?

In general I feel we should avoid expression mutation in lowering if possible outside of utilities in ast.jl. Yes, even in trivial/local cases because using mutation just makes everything harder to reason about.

I see there's quite a lot now... Can we do less of it, not more?  If it's just a matter of annoying `@ast` syntax or inconvenient utility functions I'd much prefer to fix those rather than do more mutation.

Code after:
        optional_positional_defs!(ctx, kwcall_method_defs, srcref, callex_srcref,
                                  kwcall_mtable, typevar_names, typevar_stmts,
                                  kwcall_arg_names, kwcall_arg_types, first_default, arg_defaults)
    end

    positional_forwarding_args = if has_slurp
        a = copy(arg_names)
        a[end] = @ast ctx a[end] [K"..." a[end]]
        a
    else
        arg_names
    end

    #--------------------------------------------------
    # Construct the "main kwcall overload" which unpacks keywords and checks
    # their consistency before dispatching to the user's code in the body
    # method.
    defaults_depend_on_kw_names = any(val->contains_identifier(val, kw_names), kw_defaults)
    defaults_have_assign = any(val->contains_unquoted(e->kind(e) == K"=", val), kw_defaults)
    use_ssa_kw_temps = !defaults_depend_on_kw_names && !defaults_have_assign

    if use_ssa_kw_temps
        kw_val_stmts = SyntaxList(ctx)
        for n in kw_names
            # If not using slots for the keyword argument values, still declare
            # them for reflection purposes.
            local_n = @ast ctx n [K"local" setmeta(n, :is_internal, true)]
            push!(kw_val_stmts, local_n)
        end
        kw_val_vars = SyntaxList(ctx)
        for val in kw_values
            v = emit_assign_tmp(kw_val_stmts, ctx, val, "kwval")
            push!(kw_val_vars, v)
        end
    else
        # Use kw_names directly as the values, but exclude slurp if present
        # because slurp is passed via remaining_kws
        if has_kw_slurp
            kw_val_vars = SyntaxList(ctx)
            for i in 1:length(kw_names)-1
                push!(kw_val_vars, kw_names[i])
            end
        else
            kw_val_vars = kw_names
        end
    end

    kwcall_body_tail = @ast ctx keywords [K"block"
        if has_kw_slurp
            # Slurp remaining keywords into last arg
            remaining_kws := [K"call"