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

Support Tuple#[](Range) with compile-time range literals #10379

Merged

Conversation

HertzDevil
Copy link
Contributor

@HertzDevil HertzDevil commented Feb 9, 2021

Tuple#[](index : Int) returns the element in the proper type if index is an integer literal known at compile-time. This PR adds a Tuple#[](range : Range) pseudo-overload that only supports compile-time range literals, and returns the elements in a proper sub-tuple type:

tuple = {1, "hello", 'x'}
v = tuple[0..1] # => {1, "hello"}
typeof(v)       # => Tuple(Int32, String)

i = 0
tuple[i..1] # Error: Tuple#[](Range) can only be called with range literals known at compile-time

i = 0..1
tuple[i] # Error: Tuple#[](Range) can only be called with range literals known at compile-time

The motivation is to enable #132 for Tuple values on the RHS, without creating temporary Arrays: (only one layer of decomposition is considered here)

a, *b, c = {1, 2, 3, 4, 5}
a # => 1
b # => {2, 3, 4}
c # => 5

It is assumed that the Crystal compiler should transform the above multi-assign expression into the following, in a purely syntactic manner:

__temp = {1, 2, 3, 4, 5}
a = __temp[0]
b = __temp[1..-2] # => {2, 3, 4}
c = __temp[-1]

The method supports incomplete and exclusive ranges to match the behaviour of other #[](Range) methods, even though the expansion here doesn't require them.

The range index corresponding to the splat variable is always known at compile-time, because it can be deduced from the number of non-splat variables before and after it; thus it suffices to implement the special case for Tuples where this range is a constant expression. (The same syntactic transformation already works out of the box for types that implement #[](Range), like Array.)

On the other hand, the only sensible return type of Tuple(*T)#[](Range) for runtime range arguments is Array(Union(*T)). This PR does not address this discrepancy at all, that's left to #7619. Method lookup bypasses prelude definitions of (Named)Tuple#[] if a compile-time indexer is found, so the compile-time error seen in the top example will not be triggered by constant range literals.

The documentation for this method does not take #10243 into account.

Also see the Gitter discussion that led up to this.

@HertzDevil HertzDevil closed this Feb 9, 2021
@HertzDevil HertzDevil reopened this Feb 9, 2021
def codegen_tuple_indexer(type, value, index : Range)
case type
when TupleInstanceType
tuple_types = type.tuple_types[index].map &.as(Type)
Copy link
Member

Choose a reason for hiding this comment

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

It's so convenient that implementing [] with range is actually done by using [] with a range :-)

When I initially thought about this I thought it would be more complex because of all the indexing logic involved... but it's already there!

Comment on lines +209 to +211
def [](range : Range)
{% raise "Tuple#[](Range) can only be called with range literals known at compile-time" %}
end
Copy link
Member

Choose a reason for hiding this comment

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

I think this is fine for now. But eventually returning an Array is fine too.

Copy link
Member

@asterite asterite 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!! I left a couple of optional suggestions.

@asterite asterite added this to the 1.0.0 milestone Feb 17, 2021
@asterite asterite merged commit cef547b into crystal-lang:master Feb 17, 2021
@HertzDevil HertzDevil deleted the feature/tuple-index-range-literal branch February 18, 2021 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants