feat: check for invalid returns inside of knots
This commit is contained in:
parent
e004d00990
commit
55ddd2b7cf
4 changed files with 55 additions and 26 deletions
|
|
@ -327,9 +327,17 @@ const GenIr = struct {
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addUnaryNode(gi: *GenIr, tag: Ir.Inst.Tag, arg: Ir.Inst.Ref) !Ir.Inst.Ref {
|
fn addUnaryNode(
|
||||||
|
gi: *GenIr,
|
||||||
|
tag: Ir.Inst.Tag,
|
||||||
|
arg: Ir.Inst.Ref,
|
||||||
|
node: *const Ast.Node,
|
||||||
|
) !Ir.Inst.Ref {
|
||||||
return add(gi, .{ .tag = tag, .data = .{
|
return add(gi, .{ .tag = tag, .data = .{
|
||||||
.un = .{ .lhs = arg },
|
.un = .{
|
||||||
|
.lhs = arg,
|
||||||
|
.src_offset = @intCast(node.loc.start),
|
||||||
|
},
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -642,21 +650,21 @@ fn setCondBrPayload(
|
||||||
fn unaryOp(
|
fn unaryOp(
|
||||||
gi: *GenIr,
|
gi: *GenIr,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
expr_node: *const Ast.Node,
|
node: *const Ast.Node,
|
||||||
op: Ir.Inst.Tag,
|
op: Ir.Inst.Tag,
|
||||||
) InnerError!Ir.Inst.Ref {
|
) InnerError!Ir.Inst.Ref {
|
||||||
const data = expr_node.data.bin;
|
const data = node.data.bin;
|
||||||
const lhs = try expr(gi, scope, data.lhs.?);
|
const lhs = try expr(gi, scope, data.lhs.?);
|
||||||
return gi.addUnaryNode(op, lhs);
|
return gi.addUnaryNode(op, lhs, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binaryOp(
|
fn binaryOp(
|
||||||
gi: *GenIr,
|
gi: *GenIr,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
expr_node: *const Ast.Node,
|
node: *const Ast.Node,
|
||||||
op: Ir.Inst.Tag,
|
op: Ir.Inst.Tag,
|
||||||
) InnerError!Ir.Inst.Ref {
|
) InnerError!Ir.Inst.Ref {
|
||||||
const data = expr_node.data.bin;
|
const data = node.data.bin;
|
||||||
const lhs = try expr(gi, scope, data.lhs.?);
|
const lhs = try expr(gi, scope, data.lhs.?);
|
||||||
const rhs = try expr(gi, scope, data.rhs.?);
|
const rhs = try expr(gi, scope, data.rhs.?);
|
||||||
return gi.addBinaryNode(op, lhs, rhs);
|
return gi.addBinaryNode(op, lhs, rhs);
|
||||||
|
|
@ -771,7 +779,7 @@ fn identifier(
|
||||||
) InnerError!Ir.Inst.Ref {
|
) InnerError!Ir.Inst.Ref {
|
||||||
const str = try block.astgen.strFromNode(node);
|
const str = try block.astgen.strFromNode(node);
|
||||||
if (scope.lookup(str.index)) |decl| {
|
if (scope.lookup(str.index)) |decl| {
|
||||||
return block.addUnaryNode(.load, decl.inst_index.toRef());
|
return block.addUnaryNode(.load, decl.inst_index.toRef(), node);
|
||||||
}
|
}
|
||||||
return block.addStrTok(.decl_ref, str.index, node.loc.start);
|
return block.addStrTok(.decl_ref, str.index, node.loc.start);
|
||||||
}
|
}
|
||||||
|
|
@ -1095,17 +1103,17 @@ fn content(
|
||||||
var suppress: bool = false;
|
var suppress: bool = false;
|
||||||
const data = node.data.content;
|
const data = node.data.content;
|
||||||
if (data.leading_glue) {
|
if (data.leading_glue) {
|
||||||
_ = try block.addUnaryNode(.content_glue, .none);
|
_ = try block.addUnaryNode(.content_glue, .none, node);
|
||||||
}
|
}
|
||||||
for (data.items) |child_node| {
|
for (data.items) |child_node| {
|
||||||
switch (child_node.tag) {
|
switch (child_node.tag) {
|
||||||
.string_literal => {
|
.string_literal => {
|
||||||
const result = try stringLiteral(block, child_node);
|
const result = try stringLiteral(block, child_node);
|
||||||
_ = try block.addUnaryNode(.content_push, result);
|
_ = try block.addUnaryNode(.content_push, result, child_node);
|
||||||
},
|
},
|
||||||
.inline_logic_expr => {
|
.inline_logic_expr => {
|
||||||
const result = try inlineLogicExpr(block, scope, child_node);
|
const result = try inlineLogicExpr(block, scope, child_node);
|
||||||
_ = try block.addUnaryNode(.content_push, result);
|
_ = try block.addUnaryNode(.content_push, result, child_node);
|
||||||
},
|
},
|
||||||
.if_stmt => _ = {
|
.if_stmt => _ = {
|
||||||
_ = try ifStmt(block, scope, child_node);
|
_ = try ifStmt(block, scope, child_node);
|
||||||
|
|
@ -1125,12 +1133,12 @@ fn content(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.trailing_glue or data.trailing_divert != null) {
|
if (data.trailing_glue or data.trailing_divert != null) {
|
||||||
_ = try block.addUnaryNode(.content_glue, .none);
|
_ = try block.addUnaryNode(.content_glue, .none, node);
|
||||||
}
|
}
|
||||||
if (!ignore_divert) {
|
if (!ignore_divert) {
|
||||||
if (data.trailing_divert) |trailing| {
|
if (data.trailing_divert) |trailing| {
|
||||||
_ = try divertExpr(block, scope, trailing);
|
_ = try divertExpr(block, scope, trailing);
|
||||||
_ = try block.addUnaryNode(.content_glue, .none);
|
_ = try block.addUnaryNode(.content_glue, .none, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return suppress;
|
return suppress;
|
||||||
|
|
@ -1143,7 +1151,7 @@ fn contentStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
supress = try content(gi, scope, item_node, false);
|
supress = try content(gi, scope, item_node, false);
|
||||||
}
|
}
|
||||||
if (!supress) {
|
if (!supress) {
|
||||||
_ = try gi.addUnaryNode(.content_line, .void);
|
_ = try gi.addUnaryNode(.content_line, .void, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1175,7 +1183,7 @@ fn assignOp(
|
||||||
|
|
||||||
const lhs = if (scope.lookup(name_str.index)) |decl| blk: {
|
const lhs = if (scope.lookup(name_str.index)) |decl| blk: {
|
||||||
const inst_ref = decl.inst_index.toRef();
|
const inst_ref = decl.inst_index.toRef();
|
||||||
_ = try gi.addUnaryNode(.load, inst_ref);
|
_ = try gi.addUnaryNode(.load, inst_ref, node);
|
||||||
break :blk inst_ref;
|
break :blk inst_ref;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
break :blk try gi.addStrTok(.decl_ref, name_str.index, node.loc.start);
|
break :blk try gi.addStrTok(.decl_ref, name_str.index, node.loc.start);
|
||||||
|
|
@ -1250,7 +1258,7 @@ fn choiceStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void
|
||||||
_ = try blockStmt(&body_block, scope, branch_body);
|
_ = try blockStmt(&body_block, scope, branch_body);
|
||||||
}
|
}
|
||||||
if (!body_block.endsWithNoReturn()) {
|
if (!body_block.endsWithNoReturn()) {
|
||||||
_ = try body_block.addUnaryNode(.implicit_ret, .none);
|
_ = try body_block.addUnaryNode(.implicit_ret, .none, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lhs_body = block_1.instructionsSliceUpto(&block_2);
|
const lhs_body = block_1.instructionsSliceUpto(&block_2);
|
||||||
|
|
@ -1438,10 +1446,10 @@ fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
// TODO: Revisit this
|
// TODO: Revisit this
|
||||||
const str_slice = gi.astgen.tree.nodeSlice(lhs);
|
const str_slice = gi.astgen.tree.nodeSlice(lhs);
|
||||||
if (std.mem.eql(u8, str_slice, "DONE")) {
|
if (std.mem.eql(u8, str_slice, "DONE")) {
|
||||||
_ = try gi.addUnaryNode(.done, .none);
|
_ = try gi.addUnaryNode(.done, .none, node);
|
||||||
return;
|
return;
|
||||||
} else if (std.mem.eql(u8, str_slice, "END")) {
|
} else if (std.mem.eql(u8, str_slice, "END")) {
|
||||||
_ = try gi.addUnaryNode(.exit, .none);
|
_ = try gi.addUnaryNode(.exit, .none, node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const callee = try calleeExpr(gi, scope, lhs);
|
const callee = try calleeExpr(gi, scope, lhs);
|
||||||
|
|
@ -1496,7 +1504,7 @@ fn returnStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
const arg_inst = try expr(gi, scope, lhs);
|
const arg_inst = try expr(gi, scope, lhs);
|
||||||
break :blk arg_inst;
|
break :blk arg_inst;
|
||||||
} else .void;
|
} else .void;
|
||||||
_ = try gi.addUnaryNode(.ret, ret_arg);
|
_ = try gi.addUnaryNode(.ret, ret_arg, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
|
|
@ -1588,7 +1596,7 @@ fn defaultBlock(
|
||||||
const knot_inst = try decl_scope.makePayloadNode(.decl_knot);
|
const knot_inst = try decl_scope.makePayloadNode(.decl_knot);
|
||||||
try blockInner(&decl_scope, scope, data.items);
|
try blockInner(&decl_scope, scope, data.items);
|
||||||
if (!decl_scope.endsWithNoReturn()) {
|
if (!decl_scope.endsWithNoReturn()) {
|
||||||
_ = try decl_scope.addUnaryNode(.implicit_ret, .none);
|
_ = try decl_scope.addUnaryNode(.implicit_ret, .none, body_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
var stub_scope = decl_scope.makeSubBlock();
|
var stub_scope = decl_scope.makeSubBlock();
|
||||||
|
|
@ -1634,7 +1642,8 @@ fn prototypeAndBody(
|
||||||
try blockStmt(gi, scope, body);
|
try blockStmt(gi, scope, body);
|
||||||
}
|
}
|
||||||
if (!gi.endsWithNoReturn()) {
|
if (!gi.endsWithNoReturn()) {
|
||||||
_ = try gi.addUnaryNode(.implicit_ret, .none);
|
// FIXME: Using `prototype_node` might break things.
|
||||||
|
_ = try gi.addUnaryNode(.implicit_ret, .none, prototype_node);
|
||||||
}
|
}
|
||||||
return .{ .decl_name = decl_name };
|
return .{ .decl_name = decl_name };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ pub const Inst = struct {
|
||||||
},
|
},
|
||||||
un: struct {
|
un: struct {
|
||||||
lhs: Ref,
|
lhs: Ref,
|
||||||
|
src_offset: u32,
|
||||||
},
|
},
|
||||||
bin: struct {
|
bin: struct {
|
||||||
lhs: Ref,
|
lhs: Ref,
|
||||||
|
|
|
||||||
16
src/Sema.zig
16
src/Sema.zig
|
|
@ -968,12 +968,18 @@ fn irChoiceBr(
|
||||||
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
|
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||||
const lhs = sema.resolveInst(data.lhs);
|
const lhs = sema.resolveInst(data.lhs);
|
||||||
if (lhs != .none) {
|
const return_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||||
try builder.materialize(lhs);
|
switch (builder.knot_tag) {
|
||||||
} else {
|
.function => {
|
||||||
try builder.addByteOp(.stream_glue);
|
if (lhs != .none) {
|
||||||
|
try builder.materialize(lhs);
|
||||||
|
} else {
|
||||||
|
try builder.addByteOp(.stream_glue);
|
||||||
|
}
|
||||||
|
try builder.addByteOp(.ret);
|
||||||
|
},
|
||||||
|
.knot => return sema.fail(return_src, "cannot return within a knot", .{}),
|
||||||
}
|
}
|
||||||
try builder.addByteOp(.ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,19 @@ test "compiler: unknown global variable" {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "compiler: invalid return" {
|
||||||
|
try testEqual(
|
||||||
|
\\=== knot ===
|
||||||
|
\\~ return 123
|
||||||
|
,
|
||||||
|
\\<STDIN>:2:3: error: cannot return within a knot
|
||||||
|
\\2 | ~ return 123
|
||||||
|
\\ | ^
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test "compiler: duplicate variable declarations" {
|
test "compiler: duplicate variable declarations" {
|
||||||
try testEqual(
|
try testEqual(
|
||||||
\\VAR a = 0
|
\\VAR a = 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue