diff --git a/src/codegen.zig b/src/codegen.zig index f085c3afc..3c6fbed41 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -284,8 +284,8 @@ fn objectInit(self: *Chunk, nodeId: cy.NodeId, req: RegisterCstr) !GenValue { entryId = entry.next; } - var hasDynamicInit = false; - const dynamicInitIdxStart = self.stackData.items.len; + var reqTypeCheck = false; + const typeCheckIdxStart = self.stackData.items.len; for (fieldsData, 0..) |item, fidx| { if (item.nodeId == cy.NullId) { if (mod.fields[fidx].typeId == bt.Dynamic) { @@ -298,17 +298,18 @@ fn objectInit(self: *Chunk, nodeId: cy.NodeId, req: RegisterCstr) !GenValue { } else { const entry = self.nodes[item.nodeId]; const exprv = try expression(self, entry.head.mapEntry.right, RegisterCstr.tempMustRetain); - if (exprv.vtype == bt.Dynamic) { - hasDynamicInit = true; + if (mod.fields[fidx].typeId != bt.Dynamic and exprv.vtype == bt.Dynamic) { + reqTypeCheck = true; try self.stackData.append(self.alloc, .{ .idx = @intCast(fidx) }); } } } - if (hasDynamicInit) { + if (reqTypeCheck) { try self.pushFailableDebugSym(nodeId); - try self.buf.pushOp2(.objectTypeCheck, tempStart, @intCast(mod.fields.len)); - for (self.stackData.items[dynamicInitIdxStart..]) |fidx| { + const typeCheckIdxes = self.stackData.items[typeCheckIdxStart..]; + try self.buf.pushOp2(.objectTypeCheck, tempStart, @intCast(typeCheckIdxes.len)); + for (typeCheckIdxes) |fidx| { const start = self.buf.ops.items.len; try self.buf.pushOperands(&.{ @as(u8, @intCast(fidx.idx)), 0, 0, 0, 0 }); self.buf.setOpArgU32(start + 1, mod.fields[fidx.idx].typeId); @@ -3325,6 +3326,9 @@ fn genMethodDecl(self: *Chunk, typeId: rt.TypeId, node: cy.Node, func: sema.Func const jumpPc = try self.pushEmptyJump(); try self.pushSemaBlock(func.semaBlockId); + if (self.compiler.config.genDebugFuncMarkers) { + try self.compiler.buf.pushDebugFuncStart(node.head.func.semaDeclId, self.id); + } const opStart: u32 = @intCast(self.buf.ops.items.len); try self.reserveFuncParams(func.numParams); @@ -3335,6 +3339,9 @@ fn genMethodDecl(self: *Chunk, typeId: rt.TypeId, node: cy.Node, func: sema.Func const stackSize = self.getMaxUsedRegisters(); self.popSemaBlock(); + if (self.compiler.config.genDebugFuncMarkers) { + try self.compiler.buf.pushDebugFuncEnd(node.head.func.semaDeclId, self.id); + } self.patchJumpToCurPc(jumpPc); diff --git a/src/vm.c b/src/vm.c index 6667076c4..b2ed26d6f 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1516,6 +1516,7 @@ ResultCode execBytecode(VM* vm) { } dataPtr += 5; } + pc = dataPtr; NEXT(); } CASE(ObjectSmall): { diff --git a/test/behavior_test.zig b/test/behavior_test.zig index 1731c94db..2320814d4 100644 --- a/test/behavior_test.zig +++ b/test/behavior_test.zig @@ -1322,6 +1322,46 @@ test "Objects." { }}.func); } +test "Object fields." { + // Initialize field with dynamic value does not gen `objectTypeCheck`. + try eval(.{ .silent = true }, + \\type S object: + \\ a + \\func foo(): + \\ return 123 + \\var s = S{a: foo()} + , struct { fn func(run: *VMrunner, res: EvalResult) !void { + _ = try res; + + const ops = run.vm.internal().ops; + var pc: u32 = 0; + while (pc < ops.len) { + if (@as(cy.OpCode, @enumFromInt(ops[pc].val)) == .objectTypeCheck) { + return error.Failed; + } + pc += cy.bytecode.getInstLenAt(ops.ptr + pc); + } + }}.func); + + // Initialize field with typed value does not gen `objectTypeCheck`. + try eval(.{ .silent = true }, + \\type S object: + \\ a + \\var s = S{a: 123} + , struct { fn func(run: *VMrunner, res: EvalResult) !void { + _ = try res; + + const ops = run.vm.internal().ops; + var pc: u32 = 0; + while (pc < ops.len) { + if (@as(cy.OpCode, @enumFromInt(ops[pc].val)) == .objectTypeCheck) { + return error.Failed; + } + pc += cy.bytecode.getInstLenAt(ops.ptr + pc); + } + }}.func); +} + test "Typed object fields." { // Field declaration ends the file without parser error. try evalPass(.{},