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] Not able to iterate over a String and print items (V24.6) #3890

Open
forFudan opened this issue Dec 17, 2024 · 3 comments
Open

[BUG] Not able to iterate over a String and print items (V24.6) #3890

forFudan opened this issue Dec 17, 2024 · 3 comments
Assignees
Labels
bug Something isn't working mojo-repo Tag all issues with this label

Comments

@forFudan
Copy link

Bug description

I am not able to iterate over a string and print the items in Mojo V24.6. For example, the following code is no longer working.

fn main() raises:
    var a: String = "abcdefg"
    for i in a:
        print(i)

It prints following error message:

/Users/ZHU/Programs/mojo/foo.mojo:4:14: error: argument of '__init__' call allows writing a memory location previously readable through another aliased argument
        print(i)
        ~~~~~^~~
/Users/ZHU/Programs/mojo/foo.mojo:4:14: note: 'a' memory accessed through reference embedded in value of type '!lit.ref.pack<:variadic<trait<_stdlib::_utils::_write::_Writable>> [[_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}> : anystruct<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>>, {"write_to" : !lit.signature<[2]<"W": trait<_stdlib::_utils::_write::_Writer>>("self": !lit.ref<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>, imm *[0,0]> read_mem, "writer": !lit.ref<:trait<_stdlib::_utils::_write::_Writer> *(0,0), mut *[0,1]> mut) -> !kgen.none> = rebind(:!lit.signature<[2]:{mut a}:<trait<_stdlib::_utils::_write::_Writer>>(!lit.ref<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>, imm *[0,0]> read_mem, !lit.ref<:trait<_stdlib::_utils::_write::_Writer> *(0,0), mut *[0,1]> mut) -> !kgen.none> _"fn[MutableOrigin, stdlib::utils::write::Writer](stdlib::utils::string_slice::StringSlice[{False}, {(muttoimm $0)}], mut $1, /) -> None|fn[stdlib::utils::write::Writer](stdlib::utils::string_slice::StringSlice[{False}, {(muttoimm $1|0)}], mut $0, /) -> None|8fTQE+byhg6Qw6nfSv/TtLQxg0t/d8eWdVvce6r4eu0=[MutableOrigin,stdlib::utils::write::Writer,fn[stdlib::utils::write::Writer](stdlib::utils::string_slice::StringSlice[{False}, {(muttoimm $1|0)}], mut $0, /) -> None](stdlib::utils::string_slice::StringSlice[{False}, {(muttoimm $0)}],$1&)"<:origin<1> a, :trait<_stdlib::_utils::_write::_Writer> ?, :!lit.signature<[1]<trait<_stdlib::_utils::_write::_Writer>>(!lit.struct<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>>, !lit.ref<:trait<_stdlib::_utils::_write::_Writer> *(0,0), mut *[0,0]> mut, |) -> !kgen.none> rebind(:!lit.signature<[1]<"W": trait<_stdlib::_utils::_write::_Writer>>("self": !lit.struct<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>>, "writer": !lit.ref<:trait<_stdlib::_utils::_write::_Writer> *(0,0), mut *[0,0]> mut) -> !kgen.none> _stdlib::_utils::_string_slice::_StringSlice::_"write_to[stdlib::utils::write::Writer](stdlib::utils::string_slice::StringSlice[$0, $1],$2&)"<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}, :trait<_stdlib::_utils::_write::_Writer> ?>)>), "__del__" : !lit.signature<[1]("self": !lit.ref<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>, mut *[0,0]> owned_in_mem, |) -> !kgen.none> = rebind(:!lit.signature<[1](!lit.ref<_stdlib::_utils::_string_slice::_StringSlice<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>, mut *[0,0]> owned_in_mem, |) -> !kgen.none> _stdlib::_utils::_string_slice::_StringSlice::_"__del__(stdlib::utils::string_slice::StringSlice[$0, $1])_thunk"<:_stdlib::_builtin::_bool::_Bool {:i1 0}, :_stdlib::_builtin::_type_aliases::_Origin<:_stdlib::_builtin::_bool::_Bool {:i1 0}> {_mlir_origin: origin<0> = (mutcast mut a)}>)}]], muttoimm i>'
        print(i)
             ^
/Users/ZHU/Programs/mojo/foo.mojo:4:14: error: argument of 'print' call allows writing a memory location previously readable through another aliased argument
        print(i)
             ^~
/Users/ZHU/Programs/mojo/foo.mojo:4:14: note: 'a' memory accessed through reference embedded in value of type 'VariadicPack[(muttoimm i), Writable, StringSlice[(muttoimm a)]]'
        print(i)
             ^
mojo: error: failed to parse the provided Mojo source module

Steps to reproduce

fn main() raises:
    var a: String = "abcdefg"
    for i in a:
        print(i)

System information

- MacOS 15.2
- mojo 24.6.0 (4487cd6e)
- magic 0.5.1 - (based on pixi 0.37.0)
- magic info as follows

Magic version: 0.5.1
System
------------
      Pixi version: 0.37.0
          Platform: osx-arm64
  Virtual packages: __unix=0=0
                  : __osx=15.2=0
                  : __archspec=1=m2
         Cache dir: /Users/ZHU/Library/Caches/rattler/cache
      Auth storage: /Users/ZHU/.rattler/credentials.json
  Config locations: No config files found

Global
------------
           Bin dir: /Users/ZHU/.modular/bin
   Environment dir: /Users/ZHU/.modular/envs
      Manifest dir: /Users/ZHU/.modular/manifests/pixi-global.toml

Project
------------
              Name: Mojo
     Manifest file: /Users/ZHU/Programs/mojo/pixi.toml
      Last updated: 17-12-2024 15:06:28

Environments
------------
       Environment: default
          Features: default
          Channels: conda-forge, https://conda.modular.com/max
  Dependency count: 2
      Dependencies: python, max
  Target platforms: osx-arm64, linux-64, linux-aarch64
             Tasks: benchmarks, tests, examples
@forFudan forFudan added bug Something isn't working mojo-repo Tag all issues with this label labels Dec 17, 2024
@martinvuyk
Copy link
Contributor

martinvuyk commented Dec 17, 2024

Reproducible with mojo 25.1.0.dev2024121705

@ConnorGray pulling you in here, because I think we need to merge #3823 to solve this. Then add self: Self.immut_self to methods like write_to() that should be readonly.

@forFudan meanwhile you can use

fn main() raises:
    var a: String = "abcdefg"
    for i in a.as_string_slice():
        print(i)

@ConnorGray ConnorGray self-assigned this Dec 18, 2024
Copy link
Collaborator

ConnorGray commented Dec 19, 2024

Hey Martin, thanks for the analysis. And thank you @forFudan for bringing this to our attention! 🙂

I've been investigating this as well. I don't have any conclusions yet, and I'm still digesting your suggestion Martin. I just wanted to share this "minimal" self-contained repro I found that illustrates the problem:

from os import abort


trait MyWritable:
    fn my_write_to(self):
        pass


@register_passable("trivial")
struct MyStringSlice[mut: Bool, //, origin: Origin[mut]](
    MyWritable,
):
    fn my_write_to(self):
        pass


struct MySliceIter[mut: Bool, //, origin: Origin[mut]]:
    fn __next__(mut self) -> MyStringSlice[origin]:
        return abort[MyStringSlice[origin]]()

    fn __has_next__(self) -> Bool:
        return abort[Bool]()


@value
struct MyString:
    fn __iter__(self) -> MySliceIter[__origin_of(self)]:
        return abort[MySliceIter[__origin_of(self)]]()


fn main() raises:
    var a: MyString = MyString()

    # for i in a:
    #     # print(i)
    #     foo(i)

    alias a_imm_origin = ImmutableOrigin.cast_from[__origin_of(a)].result
    var iter = a.__iter__() # MySliceIter[a_imm_origin]
    var i = iter.__next__() # MyStringSlice[a_imm_origin]

    foo(i)


fn foo[*Args: MyWritable](*args: *Args):
    pass

One minor comment: The error seems to go away if you change fn __iter__(self) to fn __iter__(ref self). Something strange with the mutcast maybe introduced by calling .__iter__() taking an immutable self on a, which is naturally mutable? (Just thinking out loud there, not confident in that theory.)

@ConnorGray ConnorGray removed the bug Something isn't working label Dec 19, 2024
@ConnorGray ConnorGray added the bug Something isn't working label Dec 19, 2024 — with Linear
@martinvuyk
Copy link
Contributor

@ConnorGray nice repro

I'm still digesting your suggestion Martin

I tried setting it up with your repro

trait MyWritable:
    alias ImmutSelf: MyWritable
    fn my_write_to(self: Self.ImmutSelf):
        pass


@register_passable("trivial")
struct MyStringSlice[mut: Bool, //, origin: Origin[mut]](
    MyWritable,
):
    alias ImmutSelf = MyStringSlice[ImmutableOrigin.cast_from[origin].result]
    fn my_write_to(self: Self.ImmutSelf):
        pass

but the compiler doesn't like the trait

error: 'self' argument must have type 'Self' in trait method declaration,
but actually has type 'get_vtable_entry(:trait<_asd::_MyWritable> _Self, "ImmutSelf")'
    fn my_write_to(self: Self.ImmutSelf):
                   ^     ~~~~~~~~~~~~~~

So we come again into the problem of traits for non-owning types :( (those with parametric origins). Because for owning types it would just be a matter of making the trait signature fn my_write_to(read self) (which is the default)

I also tried doing

struct MyStringSlice[mut: Bool, //, origin: Origin[mut]](
    MyWritable,
):

    alias ImmutSelf = MyStringSlice[ImmutableOrigin.cast_from[origin].result]

    fn immut(self) -> Self.ImmutSelf:
        return rebind[Self.ImmutSelf](self)
...
    foo(i.immut())
note: 'a' memory accessed through reference embedded in value of type
'VariadicPack[(muttoimm *"anonymous*"), MyWritable, MyStringSlice[(muttoimm a)]]'

so it's definitely a problem of the muttoimm not being interpreted as immutable in the
compiler front end at some stage

The error seems to go away if you change fn iter(self) to fn iter(ref self). Something strange with the mutcast maybe introduced by calling .iter() taking an immutable self on a, which is naturally mutable? (Just thinking out loud there, not confident in that theory.)

I actually get

  • fn __iter__(ref self) -> MySliceIter[__origin_of(self)]:: segfault
  • fn __iter__(read self) -> MySliceIter[__origin_of(self)]:: same error

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-repo Tag all issues with this label
Projects
None yet
Development

No branches or pull requests

3 participants