Skip to content

Commit

Permalink
Allow named tuple metaclass to be indexed
Browse files Browse the repository at this point in the history
mimic behavior from tuple
  • Loading branch information
Brian J. Cardiff committed Sep 15, 2017
1 parent 8e135da commit 4df8ead
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 5 deletions.
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
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
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

0 comments on commit 4df8ead

Please sign in to comment.