feat: function calls
This commit is contained in:
parent
11d99fba38
commit
d08e753664
99 changed files with 881 additions and 148 deletions
140
src/Sema.zig
140
src/Sema.zig
|
|
@ -29,6 +29,7 @@ pub const ValueInfo = union(enum) {
|
|||
variable: InternPool.Index,
|
||||
knot: InternPool.Index,
|
||||
stitch: InternPool.Index,
|
||||
function: InternPool.Index,
|
||||
temp: u32,
|
||||
};
|
||||
|
||||
|
|
@ -294,7 +295,11 @@ pub const Builder = struct {
|
|||
const local_index = try self.getOrPutConstantIndex(index);
|
||||
try self.addConstOp(.load_const, @intCast(local_index));
|
||||
},
|
||||
.variable, .knot, .stitch => |index| {
|
||||
.variable,
|
||||
.knot,
|
||||
.stitch,
|
||||
.function,
|
||||
=> |index| {
|
||||
const local_index = try self.getOrPutConstantIndex(index);
|
||||
try self.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
|
|
@ -536,7 +541,7 @@ fn irDeclRef(
|
|||
const ident = try sema.lookupIdentifier(builder, ip_index, src_loc);
|
||||
if (inline_block) {
|
||||
switch (ident.tag) {
|
||||
.knot, .stitch => unreachable,
|
||||
.knot, .stitch, .function => unreachable,
|
||||
.var_const => return sema.resolveGlobalDecl(builder, ip_index, src_loc),
|
||||
.var_mut => return sema.fail(
|
||||
src_loc,
|
||||
|
|
@ -548,6 +553,7 @@ fn irDeclRef(
|
|||
switch (ident.tag) {
|
||||
.knot => return .{ .knot = ip_index },
|
||||
.stitch => return .{ .stitch = ip_index },
|
||||
.function => return .{ .function = ip_index },
|
||||
.var_mut => return .{ .variable = ip_index },
|
||||
.var_const => return .{ .variable = ip_index },
|
||||
}
|
||||
|
|
@ -577,8 +583,9 @@ fn irStore(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void
|
|||
try builder.addConstOp(.store_global, @intCast(local_index));
|
||||
},
|
||||
.temp => |temp| try builder.addConstOp(.store, @intCast(temp)),
|
||||
.stitch => |_| return sema.fail(src, "could not assign to stitch", .{}),
|
||||
.knot => |_| return sema.fail(src, "could not assign to knot", .{}),
|
||||
.stitch => |_| return sema.fail(src, "could not assign to stitch", .{}),
|
||||
.function => |_| return sema.fail(src, "could not assign to function", .{}),
|
||||
}
|
||||
|
||||
try builder.addByteOp(.pop);
|
||||
|
|
@ -757,12 +764,66 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo
|
|||
}
|
||||
}
|
||||
|
||||
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||
const value = sema.resolveInst(data.lhs);
|
||||
if (value != .none) try builder.ensureLoad(value);
|
||||
try builder.addByteOp(.ret);
|
||||
}
|
||||
|
||||
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
||||
try builder.addByteOp(.exit);
|
||||
}
|
||||
|
||||
fn irCall(_: *Sema, _: *Builder, _: Ir.Inst.Index) !ValueInfo {
|
||||
return .none;
|
||||
fn irCall(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
inst: Ir.Inst.Index,
|
||||
comptime kind: enum { direct, field },
|
||||
) !ValueInfo {
|
||||
const ExtraType = switch (kind) {
|
||||
.direct => Ir.Inst.Call,
|
||||
.field => Ir.Inst.FieldCall,
|
||||
};
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(ExtraType, data.extra_index);
|
||||
const body = sema.ir.extra[extra.end..];
|
||||
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||
switch (kind) {
|
||||
.direct => {
|
||||
const callee = sema.resolveInst(extra.data.callee);
|
||||
_ = try analyzeCallTarget(sema, builder, callee_src, callee);
|
||||
},
|
||||
.field => {
|
||||
const callee = sema.resolveInst(extra.data.obj_ptr);
|
||||
const target = try analyzeCallTarget(sema, builder, callee_src, callee);
|
||||
const ip_index = try sema.module.intern_pool.getOrPutStr(
|
||||
sema.gpa,
|
||||
extra.data.field_name_start,
|
||||
);
|
||||
const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src);
|
||||
switch (e.tag) {
|
||||
.function => {
|
||||
const local_index = try builder.getOrPutConstantIndex(ip_index);
|
||||
_ = try builder.addConstOp(.load_attr, @intCast(local_index));
|
||||
},
|
||||
else => return sema.fail(callee_src, "invalid call target", .{}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const args_len = extra.data.args_len;
|
||||
var arg_start: u32 = args_len;
|
||||
var i: u32 = 0;
|
||||
while (i < args_len) : (i += 1) {
|
||||
const arg_end = sema.ir.extra[extra.end + i];
|
||||
defer arg_start = arg_end;
|
||||
const arg_body = body[arg_start..arg_end];
|
||||
const arg_value = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
||||
if (arg_value != .none) try builder.ensureLoad(arg_value);
|
||||
}
|
||||
try builder.addConstOp(.call, @intCast(args_len));
|
||||
return .stack;
|
||||
}
|
||||
|
||||
fn irDivert(
|
||||
|
|
@ -809,13 +870,8 @@ fn irDivert(
|
|||
const arg_end = sema.ir.extra[extra.end + i];
|
||||
defer arg_start = arg_end;
|
||||
const arg_body = body[arg_start..arg_end];
|
||||
_ = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
||||
// FIXME: hack
|
||||
{
|
||||
const last_inst: Ir.Inst.Index = @enumFromInt(arg_body[arg_body.len - 1]);
|
||||
const val = sema.resolveInst(last_inst.toRef());
|
||||
try builder.ensureLoad(val);
|
||||
}
|
||||
const arg_value = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
||||
if (arg_value != .none) try builder.ensureLoad(arg_value);
|
||||
}
|
||||
try builder.addConstOp(.divert, @intCast(args_len));
|
||||
}
|
||||
|
|
@ -864,6 +920,21 @@ fn analyzeArithmeticArg(
|
|||
}
|
||||
}
|
||||
|
||||
fn analyzeCallTarget(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
src: SrcLoc,
|
||||
callee: ValueInfo,
|
||||
) !Module.Namespace.Decl {
|
||||
switch (callee) {
|
||||
.function => |ip_index| {
|
||||
try builder.ensureLoad(callee);
|
||||
return sema.lookupIdentifier(builder, ip_index, src);
|
||||
},
|
||||
else => return sema.fail(src, "invalid call target", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeDivertTarget(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
|
|
@ -894,6 +965,7 @@ fn analyzeBodyInner(
|
|||
.decl_var => unreachable, // never present inside block bodies
|
||||
.decl_knot => unreachable, // never present inside block bodies
|
||||
.decl_stitch => unreachable, // never present inside block bodies
|
||||
.decl_function => unreachable, // never present inside block bodies
|
||||
.alloc => try irAlloc(sema, builder, inst),
|
||||
.store => {
|
||||
try irStore(sema, builder, inst);
|
||||
|
|
@ -919,6 +991,10 @@ fn analyzeBodyInner(
|
|||
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
||||
.cmp_gte => try irBinaryOp(sema, builder, inst, .cmp_gte),
|
||||
.decl_ref => try irDeclRef(sema, builder, inst, inline_block),
|
||||
.ret => {
|
||||
try irRet(sema, builder, inst);
|
||||
continue;
|
||||
},
|
||||
.implicit_ret => {
|
||||
try irImplicitRet(sema, builder, inst);
|
||||
continue;
|
||||
|
|
@ -948,12 +1024,12 @@ fn analyzeBodyInner(
|
|||
try irSwitchBr(sema, builder, inst);
|
||||
continue;
|
||||
},
|
||||
.call => try irCall(sema, builder, inst),
|
||||
.call => try irCall(sema, builder, inst, .direct),
|
||||
.field_call => try irCall(sema, builder, inst, .field),
|
||||
.divert => {
|
||||
try irDivert(sema, builder, inst, .direct);
|
||||
continue;
|
||||
},
|
||||
.field_call => try irCall(sema, builder, inst),
|
||||
.field_divert => {
|
||||
try irDivert(sema, builder, inst, .field);
|
||||
continue;
|
||||
|
|
@ -968,6 +1044,7 @@ fn analyzeBodyInner(
|
|||
return result;
|
||||
}
|
||||
|
||||
// TODO: No return allowed.
|
||||
pub fn analyzeStitch(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
|
|
@ -979,6 +1056,19 @@ pub fn analyzeStitch(
|
|||
_ = try analyzeBodyInner(sema, builder, body, false);
|
||||
}
|
||||
|
||||
// TODO: No diverts allowed.
|
||||
pub fn analyzeFunction(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
inst: Ir.Inst.Index,
|
||||
) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Function, data.extra_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
_ = try analyzeBodyInner(sema, builder, body, false);
|
||||
}
|
||||
|
||||
// TODO: No return allowed.
|
||||
pub fn analyzeKnot(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
|
|
@ -1088,7 +1178,6 @@ fn scanTopLevelDecl(
|
|||
.namespace = child_namespace,
|
||||
};
|
||||
}
|
||||
|
||||
try sema.module.queueWorkItem(.{
|
||||
.tag = .stitch,
|
||||
.decl_name = decl_name,
|
||||
|
|
@ -1096,6 +1185,27 @@ fn scanTopLevelDecl(
|
|||
.namespace = child_namespace,
|
||||
});
|
||||
},
|
||||
.decl_function => {
|
||||
const child_namespace = try sema.module.createNamespace(namespace);
|
||||
const gop = try namespace.decls.getOrPut(sema.arena, decl_name);
|
||||
if (gop.found_existing) {
|
||||
return sema.fail(src_loc, "duplicate identifier", .{});
|
||||
} else {
|
||||
gop.value_ptr.* = .{
|
||||
.tag = .function,
|
||||
.decl_inst = extra.value,
|
||||
.args_count = 0,
|
||||
.namespace = child_namespace,
|
||||
};
|
||||
}
|
||||
try sema.module.queueWorkItem(.{
|
||||
.tag = .function,
|
||||
.decl_name = decl_name,
|
||||
.inst_index = extra.value,
|
||||
.namespace = child_namespace,
|
||||
});
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue