From c3e40ebac7dae634d3d1217565c24e3a1134f54d Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Mon, 5 Oct 2020 23:53:29 -0400 Subject: [PATCH 1/7] Expose the base type of an enum --- spec/compiler/macro/macro_methods_spec.cr | 23 ++++++++++++++ spec/std/enum_spec.cr | 5 +++ src/compiler/crystal/macros.cr | 22 +++++++++++++ src/compiler/crystal/macros/methods.cr | 8 +++++ src/enum.cr | 38 +++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 0739ad312fe6..a08f0349e0a4 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -1941,6 +1941,7 @@ module Crystal end end end + describe "#nilable?" do it false do assert_macro("x", "{{x.nilable?}}", "false") do |program| @@ -1955,6 +1956,28 @@ module Crystal end end + describe "#base_type" do + it "with Int32" do + assert_macro("e", "{{e.base_type}}", "Int32") do |program| + [TypeNode.new(EnumType.new(program, program, "Color", program.int32))] of ASTNode + end + end + + it "with UInt16" do + assert_macro("e", "{{e.base_type}}", "UInt16") do |program| + [TypeNode.new(EnumType.new(program, program, "Color", program.uint16))] of ASTNode + end + end + + it "with Non enum type" do + expect_raises(Crystal::TypeException, "undefined method 'base_type' for TypeNode of type String (must be an enum type)") do + assert_macro("e", "{{e.base_type}}", "UInt16") do |program| + [TypeNode.new(program.string)] of ASTNode + end + end + end + end + it "executes resolve" do assert_macro("x", "{{x.resolve}}", "String") do |program| [TypeNode.new(program.string)] of ASTNode diff --git a/spec/std/enum_spec.cr b/spec/std/enum_spec.cr index 9495303edee8..4d667a1d47af 100644 --- a/spec/std/enum_spec.cr +++ b/spec/std/enum_spec.cr @@ -37,6 +37,11 @@ enum SpecBigEnum : Int64 end describe Enum do + describe ".base_type" do + it { SpecEnum2.base_type.should eq Int32 } + it { SpecEnum.base_type.should eq Int8 } + end + describe "to_s" do it "for simple enum" do SpecEnum::One.to_s.should eq("One") diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index eaa57510e2a4..f7183e916287 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -1860,6 +1860,28 @@ module Crystal::Macros def name(*, generic_args : BoolLiteral = true) : MacroId end + # Returns the base type of this enum. + # If the type is not an enum, an error is raised. + # + # ``` + # enum Color + # Red + # Green + # Blue + # end + # + # enum IOMode : UInt16 + # Read + # Write + # Async + # end + # + # {{Color.base_type}} # => Int32 + # {{IOMode.base_type}} # => UInt16 + # ``` + def base_type : TypeNode + end + # Returns the type variables of the generic type. If the type is not # generic, an empty array is returned. def type_vars : ArrayLiteral(TypeNode) diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 4bf8f5e91560..4ddad66d799f 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -1603,6 +1603,14 @@ module Crystal interpret_argless_method(method, args) { TypeNode.all_subclasses(type) } when "includers" interpret_argless_method(method, args) { TypeNode.includers(type) } + when "base_type" + interpret_argless_method(method, args) do + type = self.type.instance_type + + raise "undefined method 'base_type' for TypeNode of type #{type} (must be an enum type)" unless type.is_a? EnumType + + TypeNode.new type.base_type + end when "constants" interpret_argless_method(method, args) { TypeNode.constants(type) } when "constant" diff --git a/src/enum.cr b/src/enum.cr index a8716d686aa5..7f4d9038d451 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -86,6 +86,22 @@ # puts "Got blue" # end # ``` +# +# ### Changing the Base Type +# +# The type of the underlying enum value is `Int32` by default, but it can be changed: +# +# ``` +# enum Color : UInt8 +# Red +# Green +# Blue +# end +# +# Color::Red.value # => UInt8 +# ``` +# +# NOTE: Only `Int` based types are allowed. struct Enum include Comparable(self) @@ -94,6 +110,28 @@ struct Enum value end + # Returns the type of the underlying enum value. + # + # ``` + # enum Color + # Red + # Green + # Blue + # end + # + # enum IOMode : UInt16 + # Read + # Write + # Async + # end + # + # Color.base_type # => Int32 + # IOMode.base_type # => UInt16 + # ``` + def self.base_type : Int.class + {{@type.base_type}} + end + # Appends a `String` representation of this enum member to the given *io*. # # See also: `to_s`. From 837883c5c0f777cf84dc649be58149a096320588 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Tue, 6 Oct 2020 09:09:56 -0400 Subject: [PATCH 2/7] Rename macro method to enum_base_type --- spec/compiler/macro/macro_methods_spec.cr | 10 +++++----- src/compiler/crystal/macros.cr | 6 +++--- src/compiler/crystal/macros/methods.cr | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index a08f0349e0a4..fb8e1a346740 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -1956,22 +1956,22 @@ module Crystal end end - describe "#base_type" do + describe "#enum_base_type" do it "with Int32" do - assert_macro("e", "{{e.base_type}}", "Int32") do |program| + assert_macro("e", "{{e.enum_base_type}}", "Int32") do |program| [TypeNode.new(EnumType.new(program, program, "Color", program.int32))] of ASTNode end end it "with UInt16" do - assert_macro("e", "{{e.base_type}}", "UInt16") do |program| + assert_macro("e", "{{e.enum_base_type}}", "UInt16") do |program| [TypeNode.new(EnumType.new(program, program, "Color", program.uint16))] of ASTNode end end it "with Non enum type" do - expect_raises(Crystal::TypeException, "undefined method 'base_type' for TypeNode of type String (must be an enum type)") do - assert_macro("e", "{{e.base_type}}", "UInt16") do |program| + expect_raises(Crystal::TypeException, "undefined method 'enum_base_type' for TypeNode of type String (must be an enum type)") do + assert_macro("e", "{{e.enum_base_type}}", "UInt16") do |program| [TypeNode.new(program.string)] of ASTNode end end diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index f7183e916287..31ca01fd0b80 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -1876,10 +1876,10 @@ module Crystal::Macros # Async # end # - # {{Color.base_type}} # => Int32 - # {{IOMode.base_type}} # => UInt16 + # {{Color.enum_base_type}} # => Int32 + # {{IOMode.enum_base_type}} # => UInt16 # ``` - def base_type : TypeNode + def enum_base_type : TypeNode end # Returns the type variables of the generic type. If the type is not diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 4ddad66d799f..254c1ad7192d 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -1603,11 +1603,11 @@ module Crystal interpret_argless_method(method, args) { TypeNode.all_subclasses(type) } when "includers" interpret_argless_method(method, args) { TypeNode.includers(type) } - when "base_type" + when "enum_base_type" interpret_argless_method(method, args) do type = self.type.instance_type - raise "undefined method 'base_type' for TypeNode of type #{type} (must be an enum type)" unless type.is_a? EnumType + raise "undefined method 'enum_base_type' for TypeNode of type #{type} (must be an enum type)" unless type.is_a? EnumType TypeNode.new type.base_type end From 690033f692778c0980b98a242bb572dbce558131 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Tue, 6 Oct 2020 09:14:07 -0400 Subject: [PATCH 3/7] Update implementation of base_type to not rely on the macro method --- src/enum.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/enum.cr b/src/enum.cr index 7f4d9038d451..acb23a1b3091 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -129,7 +129,8 @@ struct Enum # IOMode.base_type # => UInt16 # ``` def self.base_type : Int.class - {{@type.base_type}} + # TODO: Update this to leverage `{{@type.enum_base_type}}` after the next release. + typeof(self.values.first.value) end # Appends a `String` representation of this enum member to the given *io*. From c3a7d6e6d9877bf443e4234dc2be0821117e1658 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 13 Aug 2021 11:05:16 -0400 Subject: [PATCH 4/7] Revert Enum#base_type and related macro method --- spec/compiler/macro/macro_methods_spec.cr | 23 ----------------------- spec/std/enum_spec.cr | 5 ----- src/compiler/crystal/macros.cr | 22 ---------------------- src/compiler/crystal/macros/methods.cr | 8 -------- src/enum.cr | 23 ----------------------- 5 files changed, 81 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 1b36b38877eb..20367bfdbb27 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -1986,7 +1986,6 @@ module Crystal end end end - describe "#nilable?" do it false do assert_macro("x", "{{x.nilable?}}", "false") do |program| @@ -2001,28 +2000,6 @@ module Crystal end end - describe "#enum_base_type" do - it "with Int32" do - assert_macro("e", "{{e.enum_base_type}}", "Int32") do |program| - [TypeNode.new(EnumType.new(program, program, "Color", program.int32))] of ASTNode - end - end - - it "with UInt16" do - assert_macro("e", "{{e.enum_base_type}}", "UInt16") do |program| - [TypeNode.new(EnumType.new(program, program, "Color", program.uint16))] of ASTNode - end - end - - it "with Non enum type" do - expect_raises(Crystal::TypeException, "undefined method 'enum_base_type' for TypeNode of type String (must be an enum type)") do - assert_macro("e", "{{e.enum_base_type}}", "UInt16") do |program| - [TypeNode.new(program.string)] of ASTNode - end - end - end - end - it "executes resolve" do assert_macro("x", "{{x.resolve}}", "String") do |program| [TypeNode.new(program.string)] of ASTNode diff --git a/spec/std/enum_spec.cr b/spec/std/enum_spec.cr index f45c9f61140e..3342fc37b6a8 100644 --- a/spec/std/enum_spec.cr +++ b/spec/std/enum_spec.cr @@ -44,11 +44,6 @@ enum SpecBigEnum : Int64 end describe Enum do - describe ".base_type" do - it { SpecEnum2.base_type.should eq Int32 } - it { SpecEnum.base_type.should eq Int8 } - end - describe "to_s" do it "for simple enum" do SpecEnum::One.to_s.should eq("One") diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index 02778348d451..7e7956f6ffb0 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -1879,28 +1879,6 @@ module Crystal::Macros def name(*, generic_args : BoolLiteral = true) : MacroId end - # Returns the base type of this enum. - # If the type is not an enum, an error is raised. - # - # ``` - # enum Color - # Red - # Green - # Blue - # end - # - # enum IOMode : UInt16 - # Read - # Write - # Async - # end - # - # {{Color.enum_base_type}} # => Int32 - # {{IOMode.enum_base_type}} # => UInt16 - # ``` - def enum_base_type : TypeNode - end - # Returns the type variables of the generic type. If the type is not # generic, an empty array is returned. def type_vars : ArrayLiteral(TypeNode) diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index a9c13340ff00..aaa66b0f184c 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -1610,14 +1610,6 @@ module Crystal interpret_argless_method(method, args) { TypeNode.all_subclasses(type) } when "includers" interpret_argless_method(method, args) { TypeNode.includers(type) } - when "enum_base_type" - interpret_argless_method(method, args) do - type = self.type.instance_type - - raise "undefined method 'enum_base_type' for TypeNode of type #{type} (must be an enum type)" unless type.is_a? EnumType - - TypeNode.new type.base_type - end when "constants" interpret_argless_method(method, args) { TypeNode.constants(type) } when "constant" diff --git a/src/enum.cr b/src/enum.cr index 0813b7d12647..57c0292113ab 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -110,29 +110,6 @@ struct Enum value end - # Returns the type of the underlying enum value. - # - # ``` - # enum Color - # Red - # Green - # Blue - # end - # - # enum IOMode : UInt16 - # Read - # Write - # Async - # end - # - # Color.base_type # => Int32 - # IOMode.base_type # => UInt16 - # ``` - def self.base_type : Int.class - # TODO: Update this to leverage `{{@type.enum_base_type}}` after the next release. - typeof(self.values.first.value) - end - # Appends a `String` representation of this enum member to the given *io*. # # See also: `to_s`. From aec992acbf8047c6edd15190717d0e428bc84334 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 13 Aug 2021 11:38:56 -0400 Subject: [PATCH 5/7] Specify only Int::Primitive, not all Int types --- src/enum.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enum.cr b/src/enum.cr index 57c0292113ab..9d4969a6a8a3 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -101,7 +101,7 @@ # Color::Red.value # => UInt8 # ``` # -# NOTE: Only `Int` based types are allowed. +# NOTE: Only `Int::Primitive` based types are allowed. struct Enum include Comparable(self) From abf2710f597bca8483a0a1376cc40fec398a5d09 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sun, 4 Sep 2022 21:47:18 -0400 Subject: [PATCH 6/7] Use more appropriate comment expression to show the type of the value --- src/enum.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enum.cr b/src/enum.cr index 9d4969a6a8a3..a5a5b32e1a76 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -98,7 +98,7 @@ # Blue # end # -# Color::Red.value # => UInt8 +# Color::Red.value # : UInt8 # ``` # # NOTE: Only `Int::Primitive` based types are allowed. From f8f70d60c055e49125224bbcf9742539484ef1f3 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Mon, 5 Sep 2022 14:17:26 -0400 Subject: [PATCH 7/7] Update src/enum.cr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Müller --- src/enum.cr | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/enum.cr b/src/enum.cr index 378db14108b7..c3438d7812dd 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -89,7 +89,7 @@ # # ### Changing the Base Type # -# The type of the underlying enum value is `Int32` by default, but it can be changed: +# The type of the underlying enum value is `Int32` by default, but it can be changed to any type in `Int::Primitive`. # # ``` # enum Color : UInt8 @@ -100,8 +100,6 @@ # # Color::Red.value # : UInt8 # ``` -# -# NOTE: Only `Int::Primitive` based types are allowed. struct Enum include Comparable(self)