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

Make Tuple and NamedTuples .types to return tuples of metaclasses #4962

Merged
merged 4 commits into from
Sep 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions spec/compiler/codegen/named_tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,27 @@ describe "Code gen: named tuple" do
x = {a: 0, b: z}
))
end

it "accesses T and creates instance from it" do
run("
struct NamedTuple
def named_args
T
end
end

class Foo
def initialize(@x : Int32)
end

def x
@x
end
end

t = {a: Foo.new(1)}
f = t.named_args[:a].new(2)
f.x
").to_i.should eq(2)
end
end
6 changes: 3 additions & 3 deletions spec/compiler/codegen/tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ describe "Code gen: tuple" do
").to_i.should eq(2)
end

it "accesses a tuple type and creates instance from it" do
it "accesses T and creates instance from it" do
run("
struct Tuple
def types
def type_args
T
end
end
Expand All @@ -53,7 +53,7 @@ describe "Code gen: tuple" do
end

t = {Foo.new(1)}
f = t.types[0].new(2)
f = t.type_args[0].new(2)
f.x
").to_i.should eq(2)
end
Expand Down
17 changes: 17 additions & 0 deletions spec/compiler/semantic/named_tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,21 @@ describe "Semantic: named tuples" do
),
"Can't convert an empty NamedTuple to a Hash"
end

it "types T as a tuple of metaclasses" do
assert_type("
struct NamedTuple
def named_args
T
end
end

x = {a: 1, b: 1.5, c: 'a'}
x.named_args
") do
meta = named_tuple_of({"a": int32, "b": float64, "c": char}).metaclass
meta.metaclass?.should be_true
meta
end
end
end
6 changes: 3 additions & 3 deletions spec/compiler/semantic/tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ describe "Semantic: tuples" do
assert_type("Tuple(Int32, Float64)") { tuple_of([int32, float64]).metaclass }
end

it "types T as a tuple of metalcasses" do
it "types T as a tuple of metaclasses" do
assert_type("
struct Tuple
def types
def type_args
T
end
end

x = {1, 1.5, 'a'}
x.types
x.type_args
") do
meta = tuple_of([int32, float64, char]).metaclass
meta.metaclass?.should be_true
Expand Down
5 changes: 5 additions & 0 deletions spec/std/named_tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,9 @@ describe "NamedTuple" do
tup = {a: 1, b: 'a'}
tup.values.should eq({1, 'a'})
end

it "does types" do
tuple = {a: 1, b: 'a', c: "hello"}
tuple.class.types.to_s.should eq("{a: Int32, b: Char, c: String}")
end
end
2 changes: 1 addition & 1 deletion spec/std/tuple_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ describe "Tuple" do

it "does types" do
tuple = {1, 'a', "hello"}
tuple.types.to_s.should eq("Tuple(Int32, Char, String)")
tuple.class.types.to_s.should eq("{Int32, Char, String}")
end

it "does ===" do
Expand Down
11 changes: 9 additions & 2 deletions src/compiler/crystal/codegen/primitives.cr
Original file line number Diff line number Diff line change
Expand Up @@ -783,8 +783,15 @@ class Crystal::CodeGenVisitor
ptr = aggregate_index value, index
to_lhs ptr, type.entries[index].type
else
type = (type.instance_type.as(TupleInstanceType))
type_id(type.tuple_types[index].as(Type).metaclass)
type = type.instance_type
case type
when TupleInstanceType
type_id(type.tuple_types[index].as(Type).metaclass)
when NamedTupleInstanceType
type_id(type.entries[index].type.as(Type).metaclass)
else
raise "BUG: unsupported codegen for tuple_indexer"
end
end
end

Expand Down
7 changes: 6 additions & 1 deletion src/compiler/crystal/semantic/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,15 @@ class Crystal::Call
instance_type.tuple_metaclass_indexer(index)
end
elsif owner.is_a?(NamedTupleInstanceType)
# Check named tuple inexer
# Check named tuple indexer
named_tuple_indexer_helper(args, arg_types, owner, owner, nilable) do |instance_type, index|
instance_type.tuple_indexer(index)
end
elsif owner.metaclass? && (instance_type = owner.instance_type).is_a?(NamedTupleInstanceType)
# Check named tuple metaclass indexer
named_tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) do |instance_type, index|
instance_type.tuple_metaclass_indexer(index)
end
end
end

Expand Down
8 changes: 6 additions & 2 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2703,8 +2703,12 @@ module Crystal
node.type = scope.tuple_types[node.index].as(Type)
elsif scope.is_a?(NamedTupleInstanceType)
node.type = scope.entries[node.index].type
elsif scope
node.type = (scope.instance_type.as(TupleInstanceType).tuple_types[node.index].as(Type)).metaclass
elsif scope && (instance_type = scope.instance_type).is_a?(TupleInstanceType)
node.type = instance_type.tuple_types[node.index].as(Type).metaclass
elsif scope && (instance_type = scope.instance_type).is_a?(NamedTupleInstanceType)
node.type = instance_type.entries[node.index].type.metaclass
else
node.raise "unsupported TupleIndexer scope"
end
false
end
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2179,6 +2179,11 @@ module Crystal
tuple_indexer(indexers, index)
end

def tuple_metaclass_indexer(index)
indexers = @tuple_metaclass_indexers ||= {} of Int32 => Def
tuple_indexer(indexers, index)
end

private def tuple_indexer(indexers, index)
indexers[index] ||= begin
body = index == -1 ? NilLiteral.new : TupleIndexer.new(index)
Expand Down
10 changes: 10 additions & 0 deletions src/named_tuple.cr
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ struct NamedTuple
hasher
end

# Returns the types of this named tuple type.
#
# ```
# tuple = {a: 1, b: "hello", c: 'x'}
# tuple.class.types # => {a: Int32, b: String, c: Char}
# ```
def self.types
NamedTuple.new(**{{T}})
end

# Same as `to_s`.
def inspect
to_s
Expand Down
8 changes: 4 additions & 4 deletions src/tuple.cr
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,14 @@ struct Tuple
{{T.size}}
end

# Returns the types of this tuple.
# Returns the types of this tuple type.
#
# ```
# tuple = {1, "hello", 'x'}
# tuple.types # => Tuple(Int32, String, Char)
# tuple.class.types # => {Int32, String, Char}
# ```
def types
T
def self.types
Tuple.new(*{{T}})
end

# Same as `to_s`.
Expand Down