Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Unexpected behavior of str(String()); printing corrupts future prints of String() #2392

Open
siitron opened this issue Apr 24, 2024 · 2 comments
Labels
bug Something isn't working mojo Issues that are related to mojo mojo-repo Tag all issues with this label mojo-stdlib Tag for issues related to standard library

Comments

@siitron
Copy link

siitron commented Apr 24, 2024

Bug description

Unexpected behavior:

  • str(String()) isn't an empty string, i.e. all of the following assertions will fail:
def main():
    assert_equal(str(String()), String())
    assert_equal(str(String()), String(""))
    assert_equal(str(String()), str(String("")))
    assert_equal(str(String()), "")
    assert_equal(str(String()), str(""))
  • The length of str(String()) is -1.

  • Printing str(String()) will corrupt all future prints of the value String().

Steps to reproduce

This code:

from testing import assert_equal, assert_not_equal, assert_true

def main():
    assert_equal(String(), "")
    assert_equal("", str(""))
    assert_not_equal(String(), str(String()))  # ??
    assert_equal(len(str(String())), -1)       # ??

    # prints empty strings:
    print("printing empty strings:")
    print(String())
    print(String())
    print(String())

    # prints gibberish:
    print("\ncorrupting String():")
    print(str(String()))  # 🤔

    print(String())
    print(String())
    print(String())
    print(String())
    print(String())
    print(String())

    # can print other things in-between:
    print("\ncan print other things in-between too, String() still corrupt")
    print(String())
    print(len(String()))  # ok
    print(String())
    print(String("hello"), String(), "world")
    print(3)
    
    # flushing doesn't help
    print("\nflushing", String(), flush=True)
    print(String())

    # Nope:
    var a = String()
    print(len(a))  # ok
    print(a)       # not ok
    fn f(s: String):
        print(s)
    f(a)

Produces the following output:

printing empty strings:



corrupting String():
 ���U
 ���U
 ���U
 ���U
 ���U
 ���U
 ���U

can print other things in-between too, String() still corrupt
 ���U
0
 ���U
hello  ���U world
3

flushing ���U
 ���U
0
 ���U
 ���U

System information

- OS: Debian (WSL)
- Mojo version: mojo 2024.4.1618 (37a3e965)
- Modular CLI version: modular 0.7.2 (d0adc668)
@siitron siitron added bug Something isn't working mojo Issues that are related to mojo labels Apr 24, 2024
@jayzhan211
Copy link
Contributor

jayzhan211 commented Apr 25, 2024

The reason len(str(String()) is -1 is that unintilized string String() buffer has len 0, so you got -1.

 @always_inline
    fn __init__(inout self):
        """Construct an uninitialized string."""
        self._buffer = Self._buffer_type()

    @always_inline
    fn __init__(inout self, str: StringRef):
        """Construct a string from a StringRef object.

        Args:
            str: The StringRef from which to construct this string object.
        """
        var length = len(str)
        var buffer = Self._buffer_type()
        buffer.resize(length + 1, 0)
        memcpy(rebind[DTypePointer[DType.int8]](buffer.data), str.data, length)
        buffer[length] = 0
        self._buffer = buffer^

The corruption is also reasonable if len is -1.

        elif self._value.is_str():
            var str = self._value.get_as_string()
            _printf("'%.*s'", str.length, str.data)

I'm not sure why we need uninitilized string, let it be the same as String("") is probably a good idea.

@siitron
Copy link
Author

siitron commented Apr 25, 2024

The output from my code above has changed in the new nightly release. On my machine (before updating nightly) print(String()) used to correctly print an empty string (until I do print(str(String()))), but on the playground print(String()) always prints gibberish. On stable only the lines print(str(String())) and print(String("hello"), String(), "world") (from above) fail to print as empty strings.

On the new nightly release I get mostly the same result as the previous nightly version, except that the print between the len-print and hello world, as well as all prints after print(len(a)), no longer fails to print empty strings (consistently).

The weird thing with len(str(String())) is that String's __len__ normally handles uninitilized strings as empty strings (assert_equal(len(String()), 0)).

I agree with @jayzhan211 that an uninitialized string (as an initialized value) is unintuitive. Just as Int() defaults to 0, String() should default to an empty string.

@ematejska ematejska added the mojo-repo Tag all issues with this label label Apr 29, 2024
@ematejska ematejska added the mojo-stdlib Tag for issues related to standard library label May 3, 2024 — with Linear
@ematejska ematejska removed the mojo-stdlib Tag for issues related to standard library label May 6, 2024
@ematejska ematejska added the mojo-stdlib Tag for issues related to standard library label May 6, 2024 — with Linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working mojo Issues that are related to mojo mojo-repo Tag all issues with this label mojo-stdlib Tag for issues related to standard library
Projects
None yet
Development

No branches or pull requests

3 participants