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

[mojo-stdlib] Create InlineArray type #2294

Closed
wants to merge 17 commits into from

Conversation

lsh
Copy link
Contributor

@lsh lsh commented Apr 13, 2024

This PR creates an InlineArray type that takes any CollectionElement rather than just AnyRegType. See also this thread on Discord.

@lsh lsh changed the base branch from main to nightly April 13, 2024 22:26
@lsh lsh force-pushed the lsh/static-tuple-anytype branch 2 times, most recently from bb96de2 to 05ce12f Compare April 13, 2024 22:29
@lsh lsh force-pushed the lsh/static-tuple-anytype branch from c21d061 to 308aeec Compare April 16, 2024 02:30
@lsh lsh marked this pull request as ready for review April 16, 2024 02:31
@lsh lsh requested a review from a team as a code owner April 16, 2024 02:31
Copy link
Collaborator

@JoeLoser JoeLoser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, looks pretty good! Thanks for pushing on this. This will be a critical core type and will simplify several things. 🎉

stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/src/utils/static_tuple.mojo Outdated Show resolved Hide resolved
stdlib/test/utils/test_tuple.mojo Outdated Show resolved Hide resolved
@lattner
Copy link
Collaborator

lattner commented Apr 19, 2024

This is super cool @lsh great work!

@lsh lsh force-pushed the lsh/static-tuple-anytype branch from 308aeec to 8df0770 Compare April 19, 2024 01:21
@mikowals
Copy link
Contributor

There is a problem with how __refitem__ provides __getitem__ when InlineArray is used as an alias. Maybe since in this case self is immutable in __refitem__? A failing test to demonstrate this is:

alias arr_const = InlineArray[Int, 3](1, 2, 3)
assert_equal(arr_const[0], 1)

A workaround is adding a __getitem__ with immutable self.

fn __getitem__(self, idx: Int) -> Self.ElementType:
    return self.__refitem__(idx)

@JoeLoser
Copy link
Collaborator

JoeLoser commented Apr 21, 2024

There is a problem with how __refitem__ provides __getitem__ when InlineArray is used as an alias. Maybe since in this case self is immutable in __refitem__? A failing test to demonstrate this is:

alias arr_const = InlineArray[Int, 3](1, 2, 3)
assert_equal(arr_const[0], 1)

A workaround is adding a __getitem__ with immutable self.

fn __getitem__(self, idx: Int) -> Self.ElementType:
    return self.__refitem__(idx)

Thanks for calling this out. @lattner would this sort of thing work with the latest top-of-tree mojo based on your changes yesterday with handling the case when both __getitem__ and __refitem__ are present for a type? I worry that we may be able to make it work with the latest public nightly/mojo but it may be rejected on top-of-tree (unreleased). I have not verified that though.

Edit: confirmed this won't compile on top-of-tree internally. You get an error:

stdlib/utils/static_tuple.mojo:389:32: error: cannot implicitly convert 'Reference[ElementType, 0, self, 0]' value to 'ElementType' in return value
        return self.__refitem__(idx)

So, to move forward with this PR, I recommend we file a GitHub issue and (potentially) a commented out failing test case with this GitHub issue number. To make this work at comptime, it can't be solved in the library alone AFAICT — some compiler work is needed. So, we'll handle that internally if that works.

Copy link
Collaborator

@JoeLoser JoeLoser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! Just one minor comment about the use of VariadicList, and then I'd be happy to merge this.

stdlib/test/utils/test_tuple.mojo Outdated Show resolved Hide resolved
@lsh lsh force-pushed the lsh/static-tuple-anytype branch from 92d5a3c to b17f87a Compare April 21, 2024 21:37
@lsh lsh changed the title [mojo-stdlib] Create Array type [mojo-stdlib] Create InlineArray type Apr 21, 2024
@lsh lsh force-pushed the lsh/static-tuple-anytype branch 3 times, most recently from ac0b52f to 4bcc86f Compare April 27, 2024 04:33
lsh added 7 commits April 26, 2024 21:33
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
lsh added 9 commits April 26, 2024 21:33
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
@lsh lsh force-pushed the lsh/static-tuple-anytype branch from 4bcc86f to e52f174 Compare April 27, 2024 04:33
@lsh lsh requested a review from JoeLoser April 27, 2024 04:33
# This constructor will always cause a compile time error if used.
# It is used to steer users away from uninitialized memory.
@always_inline
fn __init__(inout self):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to flag this constructor, which I'm using as a way to enforce safety guarantees. What do you think about this API? @JoeLoser @ConnorGray

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, since otherwise a zero-argument call like so:

var arr = InlinedArray[Int, 10]()

would call the variadic constructor. Doing it as an explicit constructor rather than a @parameter if in the variadic constructor makes sense for now. I don't have a good gauge as to the compile time cost difference between the additional overload (constructor) vs. a @parameter if in the variadic constructor case. Nothing to worry about for now as we're not at the level of compile time perf problems right now 😄

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this constructor will need public doc strings since it's a public entity still. We have a PR in-flight internally that adds a decorator (such as @doc_private) that we could apply to things like this in the future. The API doc string checker wouldn't apply in such cases, and the constructor decl would be hidden in our generated docs too.

I moved your comment from above the constructor to be the doc string internally when I just imported this PR. We also have a PR in-flight in the public mojo repo so the external CI here can do API doc string validation.

self._array = __mlir_op.`kgen.undef`[_type = Self.type]()

@always_inline
fn __init__(inout self, fill: Self.ElementType):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bikeshed: is fill the right word here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SIMD just uses value for this kind of constructor. Not sure if that's a great precedent or not, although it's probably clear enough. From the name I wouldn't be sure if fill was a value or a boolean. Maybe fill_value or fill_with?

Also, have you tried to create an InlineArray of size 1? It looks like the two constructors would be ambiguous in that case. Making this a keyword-only argument would avoid that potential issue.

InlineArray[Int, 10](fill_with=0xFF)

Copy link
Contributor Author

@lsh lsh Apr 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least on my machine, the constructor of size 1 seems to work fine. I'll add a test to make that clear.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're not ambiguous as @lsh points out. Consider the following smaller program that shows this is the case:

struct S:
    fn __init__(inout self, value: Int):
        print("single argument constructor")

    fn __init__(inout self, *values: Int):
        print("variadic argument constructor")

fn main():
    var s1 = S(42) # prints single argument constructor
    var s2 = S(42, 100) # prints variadic argument constructor

Re the name, I'm fine with fill, default_value, value, or any of them. I don't feel strongly and we can easily change it at any time since this isn't a keyword-argument-only constructor (therefore it's just an implementation detail at this point).

Signed-off-by: Lukas Hermann <lukashermann28@gmail.com>
Copy link
Collaborator

@JoeLoser JoeLoser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, @lsh! Thanks so much for your patience and working on this, even amidst us making you rebase and rework the code a bit as we changed several things across nightly versions.

I've imported this PR internally and I'll keep you updated on the progress of it.

self._array = __mlir_op.`kgen.undef`[_type = Self.type]()

@always_inline
fn __init__(inout self, fill: Self.ElementType):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're not ambiguous as @lsh points out. Consider the following smaller program that shows this is the case:

struct S:
    fn __init__(inout self, value: Int):
        print("single argument constructor")

    fn __init__(inout self, *values: Int):
        print("variadic argument constructor")

fn main():
    var s1 = S(42) # prints single argument constructor
    var s2 = S(42, 100) # prints variadic argument constructor

Re the name, I'm fine with fill, default_value, value, or any of them. I don't feel strongly and we can easily change it at any time since this isn't a keyword-argument-only constructor (therefore it's just an implementation detail at this point).

# This constructor will always cause a compile time error if used.
# It is used to steer users away from uninitialized memory.
@always_inline
fn __init__(inout self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, since otherwise a zero-argument call like so:

var arr = InlinedArray[Int, 10]()

would call the variadic constructor. Doing it as an explicit constructor rather than a @parameter if in the variadic constructor makes sense for now. I don't have a good gauge as to the compile time cost difference between the additional overload (constructor) vs. a @parameter if in the variadic constructor case. Nothing to worry about for now as we're not at the level of compile time perf problems right now 😄

alias i = int(index)
constrained[-size <= i < size, "Index must be within bounds."]()

var normalized_idx = i
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quibble I think we could do this arithmetic at compile time since size is a compile time parameter. But totally fine for now. 😄

# This constructor will always cause a compile time error if used.
# It is used to steer users away from uninitialized memory.
@always_inline
fn __init__(inout self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this constructor will need public doc strings since it's a public entity still. We have a PR in-flight internally that adds a decorator (such as @doc_private) that we could apply to things like this in the future. The API doc string checker wouldn't apply in such cases, and the constructor decl would be hidden in our generated docs too.

I moved your comment from above the constructor to be the doc string internally when I just imported this PR. We also have a PR in-flight in the public mojo repo so the external CI here can do API doc string validation.

@JoeLoser JoeLoser added imported-internally Signals that a given pull request has been imported internally. merged-internally Indicates that this pull request has been merged internally labels Apr 27, 2024
@JoeLoser
Copy link
Collaborator

✅🟣 This contribution has been merged 🟣✅

Hey @lsh,

As mentioned, we're moving to a new infrastructure for merging contributions to Mojo (we're using a tool called Copybara), and your contribution has now been merged into our internal copy of the Mojo Standard Library. I've added the "merged-internally" label on this PR (FYI @ConnorGray).

The changes in this PR will appear here in the mojo repo nightly branch when we do our next outbound synchronization at the time that the next Mojo nightly is released. That should happen on this coming Monday.

Please let me know if you have any questions or concerns.

@JoeLoser JoeLoser removed the imported-internally Signals that a given pull request has been imported internally. label Apr 27, 2024
JoeLoser pushed a commit to JoeLoser/mojo that referenced this pull request Apr 30, 2024
[External] [mojo-stdlib] Create `InlineArray` type

This PR creates an `InlineArray` type that takes any `CollectionElement`
rather than just `AnyRegType`. See also [this
thread](https://discord.com/channels/1087530497313357884/1228515158234501231)
on Discord.

---------

Co-authored-by: Lukas Hermann <lukashermann28@gmail.com>
Closes modularml#2294
MODULAR_ORIG_COMMIT_REV_ID: d53b6de6f9ecdf035df951ce679cea2aed1b8e65
JoeLoser pushed a commit that referenced this pull request Apr 30, 2024
[External] [mojo-stdlib] Create `InlineArray` type

This PR creates an `InlineArray` type that takes any `CollectionElement`
rather than just `AnyRegType`. See also [this
thread](https://discord.com/channels/1087530497313357884/1228515158234501231)
on Discord.

---------

Co-authored-by: Lukas Hermann <lukashermann28@gmail.com>
Closes #2294
MODULAR_ORIG_COMMIT_REV_ID: d53b6de6f9ecdf035df951ce679cea2aed1b8e65
@JoeLoser
Copy link
Collaborator

Closing as this got merged into the latest nightly as 904ac4e

@JoeLoser JoeLoser closed this Apr 30, 2024
StandinKP pushed a commit to StandinKP/mojo that referenced this pull request Apr 30, 2024
[External] [mojo-stdlib] Create `InlineArray` type

This PR creates an `InlineArray` type that takes any `CollectionElement`
rather than just `AnyRegType`. See also [this
thread](https://discord.com/channels/1087530497313357884/1228515158234501231)
on Discord.

---------

Co-authored-by: Lukas Hermann <lukashermann28@gmail.com>
Closes modularml#2294
MODULAR_ORIG_COMMIT_REV_ID: d53b6de6f9ecdf035df951ce679cea2aed1b8e65
@JoeLoser JoeLoser added imported-internally Signals that a given pull request has been imported internally. merged-externally Merged externally in public mojo repo labels May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
imported-internally Signals that a given pull request has been imported internally. merged-externally Merged externally in public mojo repo merged-internally Indicates that this pull request has been merged internally
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants