Skip to content

lib/string: pad_left / pad_right hang forever when the pad character is an empty string #637

Description

@InauguralPhysicist

Finding

pad_left and pad_right loop until the string reaches width, growing it by the pad character each pass. An empty pad character never grows it — so the loop never terminates.

lib/string.eigs:33:

# pad_left of [string, width, pad_char] -> string
define pad_left(s, width, ch) as:
    loop while (len of s) < width:
        s is ch + s
    return s

pad_right (:40) has the same shape.

Reproduction

load_file of "lib/string.eigs"
print of pad_left of ["x", 5, ""]
$ timeout 5 ./src/eigenscript probe.eigs
Terminated          <- never returns

Control: a non-empty pad char works normally. Only "" hangs.

Invariant violated

A stdlib string helper must terminate on every input. docs/STDLIB.md documents pad_left of [string, width, char] as "Left-pad to width" with no domain restriction on char, so "" is an input a caller can reach — trivially, by passing a variable that happens to be empty.

This is worse than a wrong answer: it's an unkillable spin with no output and no diagnostic. A "" arriving from user input, a config file, or a char_at that fell off the end takes the whole program down with no clue why.

Fix

Guard the degenerate pad and decide the contract deliberately:

define pad_left(s, width, ch) as:
    if (len of ch) == 0:
        return s          # or raise — but never spin
    loop while (len of s) < width:
        s is ch + s
    return s

Returning s unchanged is the honest reading of "pad with nothing". Raising is also defensible. Either is fine; spinning is not. Whatever is chosen, pin it in docs/STDLIB.md.

Worth also considering a multi-character pad: pad_left of ["x", 5, "ab"] currently overshoots to width 7, which is unspecified today. Same guard site.

Test to add

pad_left of ["x", 5, ""] and pad_right of ["x", 5, ""] return (rather than hang), asserted under the suite's normal runner so a regression shows up as a timeout rather than a silent hang.


Found by Grok Build in a delegated read-only stdlib review; reproduced and verified independently before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions