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

fix(enum): Enum and enum instance are constant value #188

Merged
merged 1 commit into from
Sep 25, 2023
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
2 changes: 0 additions & 2 deletions src/chunk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ pub const OpCode = enum(u8) {
OP_SET_INSTANCE_PROPERTY,
OP_SET_FCONTAINER_INSTANCE_PROPERTY,

OP_ENUM,
OP_ENUM_CASE,
OP_GET_ENUM_CASE,
OP_GET_ENUM_CASE_VALUE,
OP_GET_ENUM_CASE_FROM_VALUE,
Expand Down
2 changes: 0 additions & 2 deletions src/disassembler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ pub fn disassembleInstruction(chunk: *Chunk, offset: usize) !usize {
.OP_SHR,
.OP_MOD,
.OP_UNWRAP,
.OP_ENUM_CASE,
.OP_GET_ENUM_CASE_VALUE,
.OP_LIST_APPEND,
.OP_SET_MAP,
Expand Down Expand Up @@ -270,7 +269,6 @@ pub fn disassembleInstruction(chunk: *Chunk, offset: usize) !usize {
=> byteInstruction(instruction, chunk, offset),

.OP_OBJECT,
.OP_ENUM,
.OP_LIST,
.OP_RANGE,
.OP_METHOD,
Expand Down
69 changes: 51 additions & 18 deletions src/node.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ObjFunction = _obj.ObjFunction;
const ObjObject = _obj.ObjObject;
const ObjList = _obj.ObjList;
const ObjEnum = _obj.ObjEnum;
const ObjEnumInstance = _obj.ObjEnumInstance;
const ObjPattern = _obj.ObjPattern;
const ObjMap = _obj.ObjMap;
const ObjBoundMethod = _obj.ObjBoundMethod;
Expand Down Expand Up @@ -5091,11 +5092,29 @@ pub const EnumNode = struct {
return false;
}

fn val(_: *anyopaque, _: *GarbageCollector) anyerror!Value {
return GenError.NotConstant;
pub fn val(nodePtr: *anyopaque, gc: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

if (node.type_def.?.resolved_type.?.Enum.value) |enum_| {
return enum_.toValue();
}

var enum_ = try gc.allocateObject(
ObjEnum,
ObjEnum.init(gc.allocator, node.type_def.?),
);

for (self.cases.items) |case| {
try enum_.cases.append(try case.toValue(case, gc));
}

node.type_def.?.resolved_type.?.Enum.value = enum_;

return enum_.toValue();
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, _: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
var codegen: *CodeGen = @ptrCast(@alignCast(codegenPtr));
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

Expand All @@ -5121,11 +5140,6 @@ pub const EnumNode = struct {
},
}

try codegen.emitCodeArg(self.node.location, .OP_ENUM, try codegen.makeConstant(node.type_def.?.toValue()));
try codegen.emitCodeArg(self.node.location, .OP_DEFINE_GLOBAL, @intCast(self.slot));

try codegen.emitCodeArg(self.node.location, .OP_GET_GLOBAL, @intCast(self.slot));

for (self.cases.items) |case| {
if (case.type_def == null or case.type_def.?.def_type == .Placeholder) {
codegen.reporter.reportPlaceholder(case.type_def.?.resolved_type.?.Placeholder);
Expand All @@ -5139,13 +5153,17 @@ pub const EnumNode = struct {
"Bad enum case type",
);
}

_ = try case.toByteCode(case, codegen, breaks);

try codegen.emitOpCode(self.node.location, .OP_ENUM_CASE);
}

try codegen.emitOpCode(self.node.location, .OP_POP);
// Since an enum contains only constant values we can make the constant right away
try codegen.emitCodeArg(
self.node.location,
.OP_CONSTANT,
try codegen.makeConstant(
try val(self.toNode(), codegen.gc),
),
);
try codegen.emitCodeArg(self.node.location, .OP_DEFINE_GLOBAL, @intCast(self.slot));

try node.patchOptJumps(codegen);
try node.endScope(codegen);
Expand Down Expand Up @@ -6837,13 +6855,28 @@ pub const DotNode = struct {
call: ?*CallNode = null,
enum_index: ?usize = null,

fn constant(_: *anyopaque) bool {
// TODO: should be true, but we have to evaluate a constant call
return false;
fn constant(nodePtr: *anyopaque) bool {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

return self.callee.type_def.?.def_type == .Enum and self.callee.type_def.?.resolved_type.?.Enum.value != null;
}

fn val(_: *anyopaque, _: *GarbageCollector) anyerror!Value {
return GenError.NotConstant;
fn val(nodePtr: *anyopaque, gc: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

if (self.callee.type_def.?.def_type != .Enum or self.callee.type_def.?.resolved_type.?.Enum.value == null) {
return GenError.NotConstant;
}

return (try gc.allocateObject(
ObjEnumInstance,
ObjEnumInstance{
.enum_ref = self.callee.type_def.?.resolved_type.?.Enum.value.?,
.case = @intCast(self.enum_index.?),
},
)).toValue();
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
Expand Down
2 changes: 2 additions & 0 deletions src/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,8 @@ pub const ObjEnum = struct {
enum_type: *ObjTypeDef,
// TODO: should be a slice
cases: std.ArrayList([]const u8),
// Circular reference but needed so that we can generate enum case at compile time
value: ?*ObjEnum = null,

pub fn init(allocator: Allocator, name: *ObjString, qualified_name: *ObjString, enum_type: *ObjTypeDef) EnumDefSelf {
return EnumDefSelf{
Expand Down
2 changes: 2 additions & 0 deletions src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,8 @@ pub const Parser = struct {
node.node.location = start_location;
node.node.end_location = self.parser.previous_token.?;

enum_type.resolved_type.?.Enum.value = ObjEnum.cast((try EnumNode.val(&node.node, self.gc)).obj()).?;

return &node.node;
}

Expand Down
56 changes: 0 additions & 56 deletions src/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -670,8 +670,6 @@ pub const VM = struct {
OP_SET_INSTANCE_PROPERTY,
OP_SET_FCONTAINER_INSTANCE_PROPERTY,

OP_ENUM,
OP_ENUM_CASE,
OP_GET_ENUM_CASE,
OP_GET_ENUM_CASE_VALUE,
OP_GET_ENUM_CASE_FROM_VALUE,
Expand Down Expand Up @@ -2154,60 +2152,6 @@ pub const VM = struct {
);
}

fn OP_ENUM(self: *Self, _: *CallFrame, _: u32, _: OpCode, arg: u24) void {
var enum_: *ObjEnum = self.gc.allocateObject(
ObjEnum,
ObjEnum.init(self.gc.allocator, self.readConstant(arg).obj().access(ObjTypeDef, .Type, self.gc).?),
) catch |e| {
panic(e);
unreachable;
};

self.push(Value.fromObj(enum_.toObj()));

const next_full_instruction: u32 = self.readInstruction();
@call(
.always_tail,
dispatch,
.{
self,
self.currentFrame().?,
next_full_instruction,
getCode(next_full_instruction),
getArg(next_full_instruction),
},
);
}

fn OP_ENUM_CASE(self: *Self, _: *CallFrame, _: u32, _: OpCode, _: u24) void {
var enum_: *ObjEnum = self.peek(1).obj().access(ObjEnum, .Enum, self.gc).?;
var enum_value: Value = self.peek(0);

enum_.cases.append(enum_value) catch |e| {
panic(e);
unreachable;
};
self.gc.markObjDirty(&enum_.obj) catch |e| {
panic(e);
unreachable;
};

_ = self.pop();

const next_full_instruction: u32 = self.readInstruction();
@call(
.always_tail,
dispatch,
.{
self,
self.currentFrame().?,
next_full_instruction,
getCode(next_full_instruction),
getArg(next_full_instruction),
},
);
}

fn OP_GET_ENUM_CASE(self: *Self, _: *CallFrame, _: u32, _: OpCode, arg: u24) void {
var enum_: *ObjEnum = self.peek(0).obj().access(ObjEnum, .Enum, self.gc).?;

Expand Down
18 changes: 16 additions & 2 deletions tests/006-enums.buzz
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ enum NaturalEnum {
two,
}

fun getValue(NaturalEnum case = NaturalEnum.zero) > int {
return case.value;
}

object Natural {
NaturalEnum natural = NaturalEnum.zero,
}

test "Enums" {
assert(StrEnum.one.value == "one", message: "str enum");

Expand All @@ -31,6 +39,12 @@ test "Enums" {
assert(myCase.value == 2, message: "enum instance");

NaturalEnum? fromValue = NaturalEnum(0);
assert(fromValue != null, message: "could get enum instance from value");
assert(fromValue!.value == 0, message: "could get correct enum instance from value");
assert(fromValue != null, message: "Could get enum instance from value");
assert(fromValue!.value == 0, message: "Could get correct enum instance from value");
}

test "Enum case as default value" {
assert(getValue() == 0, message: "Could use enum case as function argument default value");

assert(Natural{}.natural == NaturalEnum.zero, message: "Could use enum case as object field default value");
}