From 807962c52be4e0563a821ea1eaf0b89acb44f3a2 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 13 Sep 2017 20:23:20 -0300 Subject: [PATCH 1/4] Refactor: change spec code to avoid confusion with std --- spec/compiler/codegen/tuple_spec.cr | 6 +++--- spec/compiler/semantic/tuple_spec.cr | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/compiler/codegen/tuple_spec.cr b/spec/compiler/codegen/tuple_spec.cr index 1a5451347b25..d2e49183d09b 100644 --- a/spec/compiler/codegen/tuple_spec.cr +++ b/spec/compiler/codegen/tuple_spec.cr @@ -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 @@ -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 diff --git a/spec/compiler/semantic/tuple_spec.cr b/spec/compiler/semantic/tuple_spec.cr index 93d1b1df15f4..56afb4a0dd36 100644 --- a/spec/compiler/semantic/tuple_spec.cr +++ b/spec/compiler/semantic/tuple_spec.cr @@ -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 From 8a00ab47e58ac23835c8fd60e1aed4ad19a9aaf9 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 13 Sep 2017 20:26:25 -0300 Subject: [PATCH 2/4] Add Tuple.types to return tuple of types Remove unusable Tuple#types --- spec/std/tuple_spec.cr | 2 +- src/tuple.cr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/std/tuple_spec.cr b/spec/std/tuple_spec.cr index 2d95a745a3a5..dc1c2b049555 100644 --- a/spec/std/tuple_spec.cr +++ b/spec/std/tuple_spec.cr @@ -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 diff --git a/src/tuple.cr b/src/tuple.cr index 711aca2a013b..6aa9c3fcf29d 100644 --- a/src/tuple.cr +++ b/src/tuple.cr @@ -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`. From 8e135daae2de68db89397b12245d78a1570d3823 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 13 Sep 2017 20:28:32 -0300 Subject: [PATCH 3/4] Add NamedTuple.types to return named tuple of types --- spec/std/named_tuple_spec.cr | 5 +++++ src/named_tuple.cr | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/spec/std/named_tuple_spec.cr b/spec/std/named_tuple_spec.cr index 2a243c32001f..080dd7ae927f 100644 --- a/spec/std/named_tuple_spec.cr +++ b/spec/std/named_tuple_spec.cr @@ -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 diff --git a/src/named_tuple.cr b/src/named_tuple.cr index b1c9d0d436ce..ceef6642e373 100644 --- a/src/named_tuple.cr +++ b/src/named_tuple.cr @@ -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 From 4df8ead569252c925f7209e71a7b661e9ea34171 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 13 Sep 2017 20:32:27 -0300 Subject: [PATCH 4/4] Allow named tuple metaclass to be indexed mimic behavior from tuple --- spec/compiler/codegen/named_tuple_spec.cr | 23 +++++++++++++++++++ spec/compiler/semantic/named_tuple_spec.cr | 17 ++++++++++++++ src/compiler/crystal/codegen/primitives.cr | 11 +++++++-- src/compiler/crystal/semantic/call.cr | 7 +++++- src/compiler/crystal/semantic/main_visitor.cr | 8 +++++-- src/compiler/crystal/types.cr | 5 ++++ 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/spec/compiler/codegen/named_tuple_spec.cr b/spec/compiler/codegen/named_tuple_spec.cr index 3b9477a0aa36..b581e70bfe7b 100644 --- a/spec/compiler/codegen/named_tuple_spec.cr +++ b/spec/compiler/codegen/named_tuple_spec.cr @@ -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 diff --git a/spec/compiler/semantic/named_tuple_spec.cr b/spec/compiler/semantic/named_tuple_spec.cr index 28f0ad3f2c06..3527ea28c0e2 100644 --- a/spec/compiler/semantic/named_tuple_spec.cr +++ b/spec/compiler/semantic/named_tuple_spec.cr @@ -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 diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index 0b17494aea58..8fdd696f7344 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -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 diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index 0b52885eb205..7d857efc4b6b 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -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 diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index b8f573ff784b..93638313d454 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -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 diff --git a/src/compiler/crystal/types.cr b/src/compiler/crystal/types.cr index 79c06de8f57e..8668fd688f57 100644 --- a/src/compiler/crystal/types.cr +++ b/src/compiler/crystal/types.cr @@ -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)