diff --git a/spec/compiler/codegen/c_enum_spec.cr b/spec/compiler/codegen/c_enum_spec.cr
index d93ce9a8f76e9caa8b19373a422f366dc1b7ae54..0dd82a4b4b5c8f1d94b3192333d268060c323dbc 100644
--- a/spec/compiler/codegen/c_enum_spec.cr
+++ b/spec/compiler/codegen/c_enum_spec.cr
@@ -1,23 +1,23 @@
 #!/usr/bin/env bin/crystal --run
 require "../../spec_helper"
 
-CodeGenEnumString = "lib Foo; enum Bar; X, Y, Z = 10, W; end end"
+CodeGenCEnumString = "lib Foo; enum Bar; X, Y, Z = 10, W; end end"
 
-describe "Code gen: enum" do
+describe "Code gen: c enum" do
   it "codegens enum value" do
-    run("#{CodeGenEnumString}; Foo::Bar::X").to_i.should eq(0)
+    run("#{CodeGenCEnumString}; Foo::Bar::X").to_i.should eq(0)
   end
 
   it "codegens enum value 2" do
-    run("#{CodeGenEnumString}; Foo::Bar::Y").to_i.should eq(1)
+    run("#{CodeGenCEnumString}; Foo::Bar::Y").to_i.should eq(1)
   end
 
   it "codegens enum value 3" do
-    run("#{CodeGenEnumString}; Foo::Bar::Z").to_i.should eq(10)
+    run("#{CodeGenCEnumString}; Foo::Bar::Z").to_i.should eq(10)
   end
 
   it "codegens enum value 4" do
-    run("#{CodeGenEnumString}; Foo::Bar::W").to_i.should eq(11)
+    run("#{CodeGenCEnumString}; Foo::Bar::W").to_i.should eq(11)
   end
 
   [
diff --git a/spec/compiler/codegen/enum_spec.cr b/spec/compiler/codegen/enum_spec.cr
new file mode 100644
index 0000000000000000000000000000000000000000..f8764e14cf5d39d837972e0a598e19f594ec4348
--- /dev/null
+++ b/spec/compiler/codegen/enum_spec.cr
@@ -0,0 +1,130 @@
+require "../../spec_helper"
+
+describe "Code gen: enum" do
+  it "codegens enum" do
+    run(%(
+      enum Foo
+        A = 1
+      end
+
+      Foo::A
+      )).to_i.should eq(1)
+  end
+
+  it "codegens enum without explicit value" do
+    run(%(
+      enum Foo
+        A
+        B
+        C
+      end
+
+      Foo::C
+      )).to_i.should eq(2)
+  end
+
+  it "codegens enum value" do
+    run(%(
+      enum Foo
+        A = 1
+      end
+
+      Foo::A.value
+      )).to_i.should eq(1)
+  end
+
+  it "creates enum from value" do
+    run(%(
+      enum Foo
+        A
+        B
+      end
+
+      Foo.new(1).value
+      )).to_i.should eq(1)
+  end
+
+  it "codegens enum bitflags (1)" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+      end
+
+      Foo::A
+      )).to_i.should eq(1)
+  end
+
+  it "codegens enum bitflags (2)" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+        B
+      end
+
+      Foo::B
+      )).to_i.should eq(2)
+  end
+
+  it "codegens enum bitflags (4)" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+        B
+        C
+      end
+
+      Foo::C
+      )).to_i.should eq(4)
+  end
+
+  it "codegens enum bitflags None" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+      end
+
+      Foo::None
+      )).to_i.should eq(0)
+  end
+
+  it "codegens enum bitflags All" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+        B
+        C
+      end
+
+      Foo::All
+      )).to_i.should eq(1 + 2 + 4)
+  end
+
+  it "codegens enum None redefined" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+        None = 10
+      end
+
+      Foo::None
+      )).to_i.should eq(10)
+  end
+
+  it "codegens enum All redefined" do
+    run(%(
+      @[Flags]
+      enum Foo
+        A
+        All = 10
+      end
+
+      Foo::All
+      )).to_i.should eq(10)
+  end
+end
diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr
index 61c603fe81661db60b8a9b963db3bb20dea109e9..afdd1c5ac3806bc6b3314abde750722b14ee3657 100755
--- a/spec/compiler/parser/parser_spec.cr
+++ b/spec/compiler/parser/parser_spec.cr
@@ -553,8 +553,8 @@ describe "Parser" do
   it_parses "lib C; struct Foo; x : Int**; end end", LibDef.new("C", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path.pointer_of.pointer_of)])] of ASTNode)
   it_parses "lib C; struct Foo; x, y, z : Int; end end", LibDef.new("C", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Int".path), Arg.new("z", restriction: "Int".path)])] of ASTNode)
   it_parses "lib C; union Foo; end end", LibDef.new("C", [UnionDef.new("Foo")] of ASTNode)
-  it_parses "lib C; enum Foo; A\nB, C\nD = 1; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)])] of ASTNode)
-  it_parses "lib C; enum Foo; A = 1, B; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")])] of ASTNode)
+  it_parses "lib C; enum Foo; A\nB, C\nD = 1; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)] of ASTNode)] of ASTNode)
+  it_parses "lib C; enum Foo; A = 1, B; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")] of ASTNode)] of ASTNode)
   it_parses "lib C; enum Foo < UInt16; end end", LibDef.new("C", [EnumDef.new("Foo", base_type: "UInt16".path)] of ASTNode)
   it_parses "lib C; Foo = 1; end", LibDef.new("C", [Assign.new("Foo".path, 1.int32)] of ASTNode)
   it_parses "lib C\nfun getch = GetChar\nend", LibDef.new("C", [FunDef.new("getch", real_name: "GetChar")] of ASTNode)
@@ -869,6 +869,15 @@ describe "Parser" do
   it_parses %("hello \#{1}" \\\n "\#{2} world"), StringInterpolation.new(["hello ".string, 1.int32, 2.int32, " world".string] of ASTNode)
   assert_syntax_error %("foo" "bar")
 
+  it_parses "enum Foo; A\nB, C\nD = 1; end", EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)] of ASTNode)
+  it_parses "enum Foo; A = 1, B; end", EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")] of ASTNode)
+  it_parses "enum Foo < UInt16; end", EnumDef.new("Foo", base_type: "UInt16".path)
+  it_parses "enum Foo : UInt16; end", EnumDef.new("Foo", base_type: "UInt16".path)
+  it_parses "enum Foo; def foo; 1; end; end", EnumDef.new("Foo", [Def.new("foo", body: 1.int32)] of ASTNode)
+  it_parses "enum Foo; A = 1\ndef foo; 1; end; end", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", body: 1.int32)] of ASTNode)
+  it_parses "enum Foo; A = 1\ndef foo; 1; end\ndef bar; 2; end\nend", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", body: 1.int32), Def.new("bar", body: 2.int32)] of ASTNode)
+  it_parses "enum Foo; A = 1\ndef self.foo; 1; end\nend", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", receiver: "self".var, body: 1.int32)] of ASTNode)
+
   %w(def macro class struct module fun alias abstract include extend lib).each do |keyword|
     assert_syntax_error "def foo\n#{keyword}\nend", Def.new("foo", body: keyword.call)
   end
diff --git a/spec/compiler/type_inference/c_enum_spec.cr b/spec/compiler/type_inference/c_enum_spec.cr
index d8a12b587968aef52386decd5ea6ef46eae23cda..4c08a560c0ff1f759943b22e8f88f13095840deb 100755
--- a/spec/compiler/type_inference/c_enum_spec.cr
+++ b/spec/compiler/type_inference/c_enum_spec.cr
@@ -1,7 +1,7 @@
 #!/usr/bin/env bin/crystal --run
 require "../../spec_helper"
 
-describe "Type inference: enum" do
+describe "Type inference: c enum" do
   it "types enum value" do
     assert_type("lib Foo; enum Bar; X, Y, Z = 10, W; end; end; Foo::Bar::X") { int32 }
   end
diff --git a/spec/compiler/type_inference/enum_spec.cr b/spec/compiler/type_inference/enum_spec.cr
new file mode 100644
index 0000000000000000000000000000000000000000..3c0b6a79e05b2f7663c08d40f2122153af7c847c
--- /dev/null
+++ b/spec/compiler/type_inference/enum_spec.cr
@@ -0,0 +1,185 @@
+require "../../spec_helper"
+
+describe "Type inference: enum" do
+  it "types enum" do
+    assert_type(%(
+      enum Foo
+        A = 1
+      end
+      Foo::A
+      )) { types["Foo"] }
+  end
+
+  it "types enum value" do
+    assert_type(%(
+      enum Foo
+        A = 1
+      end
+      Foo::A.value
+      )) { int32 }
+  end
+
+  it "disallows implicit conversion of int to enum" do
+    assert_error %(
+      enum Foo
+        A = 1
+      end
+
+      def foo(x : Foo)
+      end
+
+      foo 1
+      ), "mno overload matches 'foo' with types Int32"
+  end
+
+  it "finds method in enum type" do
+    assert_type(%(
+      struct Enum
+        def foo
+          1
+        end
+      end
+
+      enum Foo
+        A = 1
+      end
+
+      Foo::A.foo
+      )) { int32 }
+  end
+
+  it "finds class method in enum type" do
+    assert_type(%(
+      struct Enum
+        def self.foo
+          1
+        end
+      end
+
+      enum Foo
+        A = 1
+      end
+
+      Foo.foo
+      )) { int32 }
+  end
+
+  it "errors if using a name twice" do
+    assert_error %(
+      enum Foo
+        A
+        A
+      end
+      ),
+      "enum 'Foo' already contains a member named 'A'"
+  end
+
+  it "creates enum from value" do
+    assert_type(%(
+      enum Foo
+        A
+        B
+      end
+
+      Foo.new(1)
+      )) { types["Foo"] }
+  end
+
+  it "defines method on enum" do
+    assert_type(%(
+      enum Foo
+        A
+        B
+
+        def foo
+          1
+        end
+      end
+
+      Foo::A.foo
+      )) { int32 }
+  end
+
+  it "defines class method on enum" do
+    assert_type(%(
+      enum Foo
+        A
+        B
+
+        def self.foo
+          1
+        end
+      end
+
+      Foo.foo
+      )) { int32 }
+  end
+
+  it "reopens an enum" do
+    assert_type(%(
+      enum Foo
+        A
+        B
+      end
+
+      enum Foo
+        def foo
+          1
+        end
+      end
+
+      Foo::A.foo
+      )) { int32 }
+  end
+
+  it "errors if reopen but not enum" do
+    assert_error  %(
+      class Foo
+      end
+
+      enum Foo
+        A
+        B
+      end
+      ),
+      "Foo is not a enum, it's a class"
+  end
+
+  it "errors if reopen and tries to define constant" do
+    assert_error  %(
+      enum Foo
+        A
+        B
+      end
+
+      enum Foo
+        C
+      end
+      ),
+      "can't reopen enum and add more constants to it"
+  end
+
+  it "has None value when defined as @[Flags]" do
+    assert_type(%(
+      @[Flags]
+      enum Foo
+        A
+        B
+      end
+
+      Foo::None.value
+      )) { int32 }
+  end
+
+  it "has All value when defined as @[Flags]" do
+    assert_type(%(
+      @[Flags]
+      enum Foo
+        A
+        B
+      end
+
+      Foo::All.value
+      )) { int32 }
+  end
+end
diff --git a/src/compiler/crystal/codegen/cast.cr b/src/compiler/crystal/codegen/cast.cr
index be822d6ab06eb49389a83baf828e9f4d0313ff0d..a9d004275868476256a0f7a15f0d96f29dbcad44 100644
--- a/src/compiler/crystal/codegen/cast.cr
+++ b/src/compiler/crystal/codegen/cast.cr
@@ -324,7 +324,7 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
     union_ptr
   end
 
-  def upcast_distinct(value, to_type : CEnumType, from_type : Type)
+  def upcast_distinct(value, to_type : EnumType, from_type : Type)
     value
   end
 
diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr
index 85e257923f4d8eaba79e2f1a21bf2730595e530d..032b7b0a549bb8372f1bbbc3b9e7d1b740372d57 100644
--- a/src/compiler/crystal/codegen/codegen.cr
+++ b/src/compiler/crystal/codegen/codegen.cr
@@ -478,7 +478,7 @@ module Crystal
     end
 
     def visit(node : EnumDef)
-      node.c_enum_type.types.each_value do |type|
+      node.enum_type.try &.types.each_value do |type|
         declare_const(type as Const)
       end
       @last = llvm_nil
diff --git a/src/compiler/crystal/codegen/llvm_typer.cr b/src/compiler/crystal/codegen/llvm_typer.cr
index 524e6a6d66c5bc9fccf80a229ecf42c3f658cb28..1b20fe00b9a0d013dc1d14545be4f05606d1e44d 100644
--- a/src/compiler/crystal/codegen/llvm_typer.cr
+++ b/src/compiler/crystal/codegen/llvm_typer.cr
@@ -65,7 +65,7 @@ module Crystal
       LLVM::Int32
     end
 
-    def create_llvm_type(type : CEnumType)
+    def create_llvm_type(type : EnumType)
       llvm_type(type.base_type)
     end
 
diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr
index 551c583eedb3f9b39c83ea0053c493cdb29411a5..639923a3c111fa5717f94537a25bc4c9f9ba8439 100644
--- a/src/compiler/crystal/codegen/primitives.cr
+++ b/src/compiler/crystal/codegen/primitives.cr
@@ -69,6 +69,8 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
               codegen_primitive_pointer_diff node, target_def, call_args
             when :tuple_indexer_known_index
               codegen_primitive_tuple_indexer_known_index node, target_def, call_args
+            when :enum_value, :enum_new
+              call_args[0]
             else
               raise "Bug: unhandled primitive in codegen: #{node.name}"
             end
diff --git a/src/compiler/crystal/compiler.cr b/src/compiler/crystal/compiler.cr
index 4ae88256217ad2878c172913923721873b13d0c0..4bf7935f05632cfe0c4ee0f58fd4330afded8f92 100644
--- a/src/compiler/crystal/compiler.cr
+++ b/src/compiler/crystal/compiler.cr
@@ -260,7 +260,7 @@ module Crystal
       if @stats
         time = Time.now
         value = yield
-        puts "#{label}: #{Time.now - time} seconds"
+        puts "#{label}: #{Time.now - time}"
         value
       else
         yield
diff --git a/src/compiler/crystal/macros/macros.cr b/src/compiler/crystal/macros/macros.cr
index 4dca6b09e95e9b38995056ba75ab24a0071881ee..978cb0b641b245dc1dfaf11c4eb5f490a099a826 100644
--- a/src/compiler/crystal/macros/macros.cr
+++ b/src/compiler/crystal/macros/macros.cr
@@ -603,13 +603,28 @@ module Crystal
         when "@instance_vars"
           return @last = MacroType.instance_vars(@scope)
         when "@length"
-          if (scope = @scope).is_a?(TupleInstanceType)
+          scope = @scope.try &.instance_type
+          if scope.is_a?(TupleInstanceType)
             return @last = NumberLiteral.new(scope.tuple_types.length)
           end
         when "@superclass"
           return @last = MacroType.superclass(@scope)
         when "@type"
           return @last = MacroType.new(@scope)
+        when "@enum_members"
+          scope = @scope.try &.instance_type
+          if scope.is_a?(EnumType)
+            names = Array(ASTNode).new(scope.types.length)
+            scope.types.each_key do |name|
+              names << MacroId.new(name)
+            end
+            return @last = ArrayLiteral.new names
+          end
+        when "@enum_flags"
+          scope = @scope.try &.instance_type
+          if scope.is_a?(EnumType)
+            return @last = BoolLiteral.new(scope.flags?)
+          end
         end
 
         node.raise "unknown macro instance var: '#{node.name}'"
diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr
index 670bb5ea8009701e8a0b29b1f93ab24ff71fd2bf..70477a8a141e8b1d772d4e2703bfbd25b14f38f0 100644
--- a/src/compiler/crystal/program.cr
+++ b/src/compiler/crystal/program.cr
@@ -89,6 +89,10 @@ module Crystal
       @struct.abstract = true
       @struct.struct = true
 
+      @types["Enum"] = @enum = NonGenericClassType.new self, self, "Enum", @value
+      @enum.abstract = true
+      @enum.struct = true
+
       @types["Function"] = @function = FunType.new self, self, "Function", @value, ["T"]
       @function.variadic = true
 
@@ -297,6 +301,7 @@ module Crystal
     getter :exception
     getter :tuple
     getter :function
+    getter :enum
 
     def class_type
       @class
diff --git a/src/compiler/crystal/semantic/after_type_inference_transformer.cr b/src/compiler/crystal/semantic/after_type_inference_transformer.cr
index 363af9d1f4c7b6b1e8191a058e81fcf3be08206c..0668300d4e5760c5fe7778bc3f746c8cc074ec92 100644
--- a/src/compiler/crystal/semantic/after_type_inference_transformer.cr
+++ b/src/compiler/crystal/semantic/after_type_inference_transformer.cr
@@ -169,7 +169,7 @@ module Crystal
     def transform(node : EnumDef)
       super
 
-      node.c_enum_type.types.each_value do |const|
+      node.enum_type.try &.types.each_value do |const|
         (const as Const).initialized = true
       end
 
diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr
index 62c980a95ac156d9e75b1febb8d3e692a2aa3e95..6c57f9abbe66a37a25532419e56b7405de30ca3c 100644
--- a/src/compiler/crystal/semantic/ast.cr
+++ b/src/compiler/crystal/semantic/ast.cr
@@ -471,7 +471,7 @@ module Crystal
   end
 
   class EnumDef
-    property! c_enum_type
+    property enum_type
   end
 
   class Yield
diff --git a/src/compiler/crystal/semantic/restrictions.cr b/src/compiler/crystal/semantic/restrictions.cr
index 052a1a99c1c3681758105ced0840b96f9fee86a0..d38e46baf9627c147f745f2bd7d72afdaa782b4c 100644
--- a/src/compiler/crystal/semantic/restrictions.cr
+++ b/src/compiler/crystal/semantic/restrictions.cr
@@ -251,8 +251,12 @@ module Crystal
   end
 
   class IntegerType
-    def restrict(other : CEnumType, context)
-      self == other.base_type ? self : nil
+    def restrict(other : EnumType, context)
+      if other.c_enum?
+        self == other.base_type ? self : nil
+      else
+        super
+      end
     end
   end
 
diff --git a/src/compiler/crystal/semantic/type_inference.cr b/src/compiler/crystal/semantic/type_inference.cr
index 83be1d8a8c608447015cd91fb73657ac6550605a..621d81914a2f3d3d555f881fa464e78bb2116447 100644
--- a/src/compiler/crystal/semantic/type_inference.cr
+++ b/src/compiler/crystal/semantic/type_inference.cr
@@ -21,6 +21,7 @@ module Crystal
     ValidStructDefAttributes = %w(Packed)
     ValidDefAttributes = %w(AlwaysInline NoInline Raises ReturnsTwice)
     ValidFunDefAttributes = %w(AlwaysInline NoInline Raises ReturnsTwice)
+    ValidEnumDefAttributes = %w(Flags)
 
     getter mod
     property! scope
@@ -1681,36 +1682,78 @@ module Crystal
     end
 
     def visit(node : EnumDef)
-      type = current_type.types[node.name]?
-      if type
-        node.raise "#{node.name} is already defined"
-      else
-        if base_type = node.base_type
-          base_type.accept self
-          enum_base_type = base_type.type.instance_type
-          unless enum_base_type.is_a?(IntegerType)
-            base_type.raise "enum base type must be an integer type"
-          end
-        else
-          enum_base_type = @mod.int32
+      check_valid_attributes node, ValidEnumDefAttributes, "enum"
+
+      enum_type = current_type.types[node.name]?
+      if enum_type
+        unless enum_type.is_a?(EnumType)
+          node.raise "#{node.name} is not a enum, it's a #{enum_type.type_desc}"
         end
+      end
 
-        enum_type = CEnumType.new(@mod, current_type, node.name, enum_base_type)
+      if base_type = node.base_type
+        base_type.accept self
+        enum_base_type = base_type.type.instance_type
+        unless enum_base_type.is_a?(IntegerType)
+          base_type.raise "enum base type must be an integer type"
+        end
+      else
+        enum_base_type = @mod.int32
+      end
+
+      is_lib = current_type.is_a?(LibType)
+      is_flags = node.has_attribute?("Flags")
+      all_value = 0_u64
+      existed = !!enum_type
+      enum_type ||= EnumType.new(@mod, current_type, node.name, enum_base_type, is_lib, is_flags)
+
+      pushing_type(enum_type) do
+        counter = is_flags ? 1 : 0
+        node.members.each do |member|
+          case member
+          when Arg
+            if existed
+              member.raise "can't reopen enum and add more constants to it"
+            end
 
-        pushing_type(enum_type) do
-          counter = 0
-          node.constants.each do |constant|
-            if default_value = constant.default_value
+            if default_value = member.default_value
               counter = interpret_enum_value(default_value)
             end
-            constant.default_value = NumberLiteral.new(counter, enum_base_type.kind)
-            enum_type.add_constant constant
-            counter += 1
+            all_value |= counter
+            const_value = NumberLiteral.new(counter, enum_base_type.kind)
+            member.default_value = const_value
+            if enum_type.types.has_key?(member.name)
+              member.raise "enum '#{enum_type}' already contains a member named '#{member.name}'"
+            end
+            enum_type.add_constant member
+            const_value.type = enum_type unless is_lib
+            counter = is_flags ? counter * 2 : counter + 1
+          when Def
+            member.accept self
           end
         end
+      end
+
+      unless existed
+        if is_flags
+          unless enum_type.types["None"]?
+            none = NumberLiteral.new(0, enum_base_type.kind)
+            none.type = enum_type unless is_lib
+            enum_type.add_constant Arg.new("None", default_value: none)
+          end
 
-        node.c_enum_type = current_type.types[node.name] = enum_type
+          unless enum_type.types["All"]?
+            all = NumberLiteral.new(all_value, enum_base_type.kind)
+            all.type = enum_type unless is_lib
+            enum_type.add_constant Arg.new("All", default_value: all)
+          end
+        end
+
+        node.enum_type = current_type.types[node.name] = enum_type
       end
+
+      node.type = mod.nil
+
       false
     end
 
@@ -2153,6 +2196,10 @@ module Crystal
         node.type = mod.int64
       when :class_name
         node.type = mod.string
+      when :enum_value
+        # Nothing to do
+      when :enum_new
+        # Nothing to do
       else
         node.raise "Bug: unhandled primitive in type inference: #{node.name}"
       end
diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr
index 2980e2df8bb7f384684301446d7feadddf530a7f..3100fe2bc8a91023d6d43c7317532e48f9cfeffc 100644
--- a/src/compiler/crystal/syntax/ast.cr
+++ b/src/compiler/crystal/syntax/ast.cr
@@ -22,10 +22,6 @@ module Crystal
       Attribute.any?(attributes, name)
     end
 
-    def accepts_attributes?
-      false
-    end
-
     def name_column_number
       @location.try(&.column_number) || 0
     end
@@ -673,10 +669,6 @@ module Crystal
     def initialize(@name)
     end
 
-    def accepts_attributes?
-      true
-    end
-
     def name_length
       name.length
     end
@@ -835,10 +827,6 @@ module Crystal
       @name_column_number = 0
     end
 
-    def accepts_attributes?
-      true
-    end
-
     def accept_children(visitor)
       @receiver.try &.accept visitor
       @args.each &.accept visitor
@@ -1155,10 +1143,6 @@ module Crystal
       @body = Expressions.from body
     end
 
-    def accepts_attributes?
-      true
-    end
-
     def accept_children(visitor)
       @superclass.try &.accept visitor
       @body.accept visitor
@@ -1561,10 +1545,6 @@ module Crystal
     def initialize(@name, @args = [] of Arg, @return_type = nil, @varargs = false, @body = nil, @real_name = name)
     end
 
-    def accepts_attributes?
-      true
-    end
-
     def accept_children(visitor)
       @args.each &.accept visitor
       @return_type.try &.accept visitor
@@ -1614,10 +1594,6 @@ module Crystal
   class StructDef < StructOrUnionDef
     property :attributes
 
-    def accepts_attributes?
-      true
-    end
-
     def clone_without_location
       StructDef.new(@name, @fields.clone)
     end
@@ -1631,22 +1607,23 @@ module Crystal
 
   class EnumDef < ASTNode
     property :name
-    property :constants
+    property :members
     property :base_type
+    property :attributes
 
-    def initialize(@name, @constants = [] of Arg, @base_type = nil)
+    def initialize(@name, @members = [] of ASTNode, @base_type = nil)
     end
 
     def accept_children(visitor)
-      @constants.each &.accept visitor
+      @members.each &.accept visitor
       @base_type.try &.accept visitor
     end
 
     def clone_without_location
-      EnumDef.new(@name, @constants.clone, @base_type.clone)
+      EnumDef.new(@name, @members.clone, @base_type.clone)
     end
 
-    def_equals_and_hash @name, @constants, @base_type
+    def_equals_and_hash @name, @members, @base_type
   end
 
   class ExternalVar < ASTNode
@@ -1658,10 +1635,6 @@ module Crystal
     def initialize(@name, @type_spec, @real_name = nil)
     end
 
-    def accepts_attributes?
-      true
-    end
-
     def accept_children(visitor)
       @type_spec.accept visitor
     end
diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr
index f25b1ac5699458f90fedc97d8d78377b9fdce6bc..f8ece985290db6523b791d5fa6e6c360e080a16b 100644
--- a/src/compiler/crystal/syntax/parser.cr
+++ b/src/compiler/crystal/syntax/parser.cr
@@ -858,6 +858,10 @@ module Crystal
           check_not_inside_def("can't define module inside def") do
             parse_module_def
           end
+        when :enum
+          check_not_inside_def("can't define enum inside def") do
+            parse_enum_def
+          end
         when :while
           parse_while
         when :until
@@ -3513,7 +3517,7 @@ module Crystal
         when :union
           parse_struct_or_union UnionDef
         when :enum
-          parse_enum
+          parse_enum_def
         when :ifdef
           parse_ifdef check_end: true, inside_lib: true
         else
@@ -3780,14 +3784,15 @@ module Crystal
       fields
     end
 
-    def parse_enum
+    def parse_enum_def
       next_token_skip_space_or_newline
 
       check :CONST
       name = @token.value.to_s
 
       next_token_skip_space
-      if @token.type == :"<"
+      case @token.type
+      when :"<", :":"
         next_token_skip_space_or_newline
         base_type = parse_single_type
         skip_statement_end
@@ -3795,33 +3800,45 @@ module Crystal
         next_token_skip_statement_end
       end
 
-      constants = [] of Arg
-      while !@token.keyword?(:end)
-        check :CONST
+      members = [] of ASTNode
+      until @token.keyword?(:end)
+        case @token.type
+        when :CONST
+          constant_name = @token.value.to_s
+          next_token_skip_space
+          if @token.type == :"="
+            next_token_skip_space_or_newline
 
-        constant_name = @token.value.to_s
-        next_token_skip_space
-        if @token.type == :"="
-          next_token_skip_space_or_newline
+            constant_value = parse_logical_or
+            next_token_skip_statement_end
+          else
+            constant_value = nil
+            skip_statement_end
+          end
 
-          constant_value = parse_logical_or
-          next_token_skip_statement_end
-        else
-          constant_value = nil
-          skip_statement_end
-        end
+          case @token.type
+          when :",", :";"
+            next_token_skip_statement_end
+          end
 
-        if @token.type == :","
-          next_token_skip_statement_end
+          members << Arg.new(constant_name, constant_value)
+        when :IDENT
+          if @token.value == :def
+            members << parse_def
+          else
+            unexpected_token
+          end
+        when :";", :NEWLINE
+          skip_statement_end
+        else
+          unexpected_token
         end
-
-        constants << Arg.new(constant_name, constant_value)
       end
 
       check_ident :end
       next_token_skip_space
 
-      EnumDef.new name, constants, base_type
+      EnumDef.new name, members, base_type
     end
 
     def node_and_next_token(node)
diff --git a/src/compiler/crystal/syntax/to_s.cr b/src/compiler/crystal/syntax/to_s.cr
index d22465763cb5a330de567721ad86de882c09887a..a15f639fd4c427263a25d8d3184547b2cbf0ac40 100644
--- a/src/compiler/crystal/syntax/to_s.cr
+++ b/src/compiler/crystal/syntax/to_s.cr
@@ -1015,9 +1015,9 @@ module Crystal
       end
       @str << newline
       with_indent do
-        node.constants.each do |constant|
+        node.members.each do |member|
           append_indent
-          constant.accept self
+          member.accept self
           @str << newline
         end
       end
diff --git a/src/compiler/crystal/syntax/transformer.cr b/src/compiler/crystal/syntax/transformer.cr
index b54a370f82bd8cebb126ed8ebc41bb2daff9ea97..4ba288c60ba961d8d2c31089deecdcbf442bf45e 100644
--- a/src/compiler/crystal/syntax/transformer.cr
+++ b/src/compiler/crystal/syntax/transformer.cr
@@ -465,6 +465,7 @@ module Crystal
     end
 
     def transform(node : EnumDef)
+      transform_many node.members
       node
     end
 
diff --git a/src/compiler/crystal/types.cr b/src/compiler/crystal/types.cr
index 807d106edc4bc75b022ae97ad129444a4e93582d..880ea6bc27bf22c1f1fde24d0f6fb5f5ebf0930b 100644
--- a/src/compiler/crystal/types.cr
+++ b/src/compiler/crystal/types.cr
@@ -1303,6 +1303,8 @@ module Crystal
     def virtual_type
       if leaf? && !self.abstract
         self
+      elsif struct?
+        self
       else
         virtual_type!
       end
@@ -2220,11 +2222,22 @@ module Crystal
     end
   end
 
-  class CEnumType < NamedType
+  class EnumType < NamedType
+    include DefContainer
+    include DefInstanceContainer
+
     getter base_type
+    getter? flags
 
-    def initialize(program, container, name, @base_type)
+    def initialize(program, container, name, @base_type, @c_enum, @flags)
       super(program, container, name)
+
+      add_def Def.new("value", [] of Arg, Primitive.new(:enum_value, @base_type))
+      metaclass.add_def Def.new("new", [Arg.new("value", type: @base_type)], Primitive.new(:enum_new, self))
+    end
+
+    def parents
+      @parents ||= [program.enum] of Type
     end
 
     def add_constant(constant)
@@ -2232,26 +2245,16 @@ module Crystal
     end
 
     def c_enum?
-      true
+      @c_enum
     end
 
     def primitive_like?
-      true
-    end
-
-    def parents
-      nil
+      c_enum?
     end
 
     def type_desc
       "enum"
     end
-
-    def to_s(io)
-      container.to_s(io)
-      io << "::"
-      name.to_s(io)
-    end
   end
 
   class MetaclassType < ClassType
@@ -2267,6 +2270,8 @@ module Crystal
       @instance_type = instance_type
       super_class ||= if instance_type.is_a?(ClassType) && instance_type.superclass
                         instance_type.superclass.not_nil!.metaclass as ClassType
+                      elsif instance_type.is_a?(EnumType)
+                        @program.enum.metaclass as MetaclassType
                       else
                         @program.class_type
                       end
@@ -2552,7 +2557,7 @@ module Crystal
     property value
     getter scope_types
     getter scope
-    property! vars
+    property vars
     property used
     property? visited
     property? initialized
diff --git a/src/enum.cr b/src/enum.cr
new file mode 100644
index 0000000000000000000000000000000000000000..fe1dad436556a52d580b0e10e16c72a26181cd76
--- /dev/null
+++ b/src/enum.cr
@@ -0,0 +1,119 @@
+struct Enum
+  include Comparable(self)
+
+  macro def to_s(io : IO) : Nil
+    {% if @enum_flags %}
+      if value == 0
+        io << "None"
+      else
+        found = false
+        {% for member in @enum_members %}
+          {% if member.stringify != "All" %}
+            if {{member}}.value != 0 && (value & {{member}}.value) == {{member}}.value
+              io << ", " if found
+              io << {{member.stringify}}
+              found = true
+            end
+          {% end %}
+        {% end %}
+        io << value unless found
+      end
+    {% else %}
+      io << to_s
+    {% end %}
+    nil
+  end
+
+  macro def to_s : String
+    {% if @enum_flags %}
+      String.build { |io| to_s(io) }
+    {% else %}
+      case value
+      {% for member in @enum_members %}
+      when {{member}}.value
+        {{member.stringify}}
+      {% end %}
+      else
+        value.to_s
+      end
+    {% end %}
+  end
+
+  def +(other : Int)
+    self.class.new(value + other)
+  end
+
+  def -(other : Int)
+    self.class.new(value - other)
+  end
+
+  def |(other : self)
+    self.class.new(value | other.value)
+  end
+
+  def &(other : self)
+    self.class.new(value & other.value)
+  end
+
+  def ^(other : self)
+    self.class.new(value ^ other.value)
+  end
+
+  def ~(other : self)
+    self.class.new(value ~ other.value)
+  end
+
+  def <=>(other : self)
+    value <=> other.value
+  end
+
+  def includes?(other : self)
+    (value & other.value) != 0
+  end
+
+  def ==(other : self)
+    value == other.value
+  end
+
+  macro def self.names : Array(String)
+    {{ @enum_members.map &.stringify }}
+  end
+
+  # macro def self.values : Array(self)
+  #   {{ @enum_members }}
+  # end
+
+  # macro def self.to_h : Hash(String, self)
+  #   {
+  #     {% for member in @enum_members %}
+  #       {{member.stringify}} => {{member}},
+  #     {% end %}
+  #   }
+  # end
+
+  # def self.parse(string)
+  #   value = parse?(string)
+  #   if value
+  #     value
+  #   else
+  #     raise "Uknonwn enum #{self} value: #{string}"
+  #   end
+  # end
+
+  # macro def self.parse?(string) : self ?
+  #   case string.downcase
+  #   {% for member in @enum_members %}
+  #     when {{member.stringify.downcase}}
+  #       {{member}}
+  #   {% end %}
+  #   else
+  #     nil
+  #   end
+  # end
+
+  # def self.each
+  #   to_h.each do |key, value|
+  #     yield key, value
+  #   end
+  # end
+end
diff --git a/src/prelude.cr b/src/prelude.cr
index b2f1f85e7d990ce0dfc0809cfbfc175d7895eb7b..bd71fb7352bc9da8d200e96b225fbca3411112ef 100644
--- a/src/prelude.cr
+++ b/src/prelude.cr
@@ -26,6 +26,7 @@ require "range"
 require "char_reader"
 require "string"
 require "symbol"
+require "enum"
 require "static_array"
 require "array"
 require "hash"