AstGen: implement extern variables

This commit is contained in:
Andrew Kelley 2021-04-29 19:44:51 -07:00
parent ba9b9cb38d
commit 86d564eed8
4 changed files with 139 additions and 16 deletions

View File

@ -2961,7 +2961,7 @@ fn globalVarDecl(
assert(var_decl.comptime_token == null); // handled by parser
const var_inst: Zir.Inst.Index = if (var_decl.ast.init_node != 0) vi: {
if (var_decl.ast.init_node != 0) {
if (is_extern) {
return astgen.failNode(
var_decl.ast.init_node,
@ -2989,19 +2989,25 @@ fn globalVarDecl(
// We do this at the end so that the instruction index marks the end
// range of a top level declaration.
_ = try block_scope.addBreak(.break_inline, block_inst, init_inst);
try block_scope.setBlockBody(block_inst);
break :vi block_inst;
} else if (!is_extern) {
return astgen.failNode(node, "variables must be initialized", .{});
} else if (var_decl.ast.type_node != 0) {
// Extern variable which has an explicit type.
const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
return astgen.failNode(node, "TODO AstGen extern global variable", .{});
const var_inst = try block_scope.addVar(.{
.var_type = type_inst,
.lib_name = lib_name,
.align_inst = .none, // passed in the decls data
.init = .none,
.is_extern = true,
});
_ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
} else {
return astgen.failNode(node, "unable to infer variable type", .{});
};
}
try block_scope.setBlockBody(block_inst);
const name_token = var_decl.ast.mut_token + 1;
const name_str_index = try gz.identAsString(name_token);
@ -3013,7 +3019,7 @@ fn globalVarDecl(
wip_decls.payload.appendSliceAssumeCapacity(&casted);
}
wip_decls.payload.appendAssumeCapacity(name_str_index);
wip_decls.payload.appendAssumeCapacity(var_inst);
wip_decls.payload.appendAssumeCapacity(block_inst);
if (align_inst != .none) {
wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
}

View File

@ -1462,6 +1462,57 @@ pub const Scope = struct {
}
}
pub fn addVar(gz: *GenZir, args: struct {
align_inst: Zir.Inst.Ref,
lib_name: u32,
var_type: Zir.Inst.Ref,
init: Zir.Inst.Ref,
is_extern: bool,
}) !Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
@boolToInt(args.lib_name != 0) +
@boolToInt(args.align_inst != .none) +
@boolToInt(args.init != .none),
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
.var_type = args.var_type,
});
if (args.lib_name != 0) {
astgen.extra.appendAssumeCapacity(args.lib_name);
}
if (args.align_inst != .none) {
astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
}
if (args.init != .none) {
astgen.extra.appendAssumeCapacity(@enumToInt(args.init));
}
const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .variable,
.small = @bitCast(u16, Zir.Inst.ExtendedVar.Small{
.has_lib_name = args.lib_name != 0,
.has_align = args.align_inst != .none,
.has_init = args.init != .none,
.is_extern = args.is_extern,
}),
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return gz.indexToRef(new_index);
}
pub fn addCall(
gz: *GenZir,
tag: Zir.Inst.Tag,

View File

@ -479,6 +479,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
switch (extended.opcode) {
// zig fmt: off
.func => return sema.zirFuncExtended( block, extended),
.variable => return sema.zirVarExtended( block, extended),
.ret_ptr => return sema.zirRetPtr( block, extended),
.ret_type => return sema.zirRetType( block, extended),
.this => return sema.zirThis( block, extended),
@ -5427,6 +5428,17 @@ fn zirAwait(
return sema.mod.fail(&block.base, src, "TODO: Sema.zirAwait", .{});
}
fn zirVarExtended(
sema: *Sema,
block: *Scope.Block,
extended: Zir.Inst.Extended.InstData,
) InnerError!*Inst {
const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
const src = sema.src;
return sema.mod.fail(&block.base, src, "TODO implement Sema.zirVarExtended", .{});
}
fn zirFuncExtended(
sema: *Sema,
block: *Scope.Block,

View File

@ -1496,6 +1496,10 @@ pub const Inst = struct {
/// `operand` is payload index to `ExtendedFunc`.
/// `small` is `ExtendedFunc.Small`.
func,
/// Declares a global variable.
/// `operand` is payload index to `ExtendedVar`.
/// `small` is `ExtendedVar.Small`.
variable,
/// Obtains a pointer to the return value.
/// `operand` is `src_node: i32`.
ret_ptr,
@ -2209,6 +2213,23 @@ pub const Inst = struct {
};
};
/// Trailing:
/// 0. lib_name: u32, // null terminated string index, if has_lib_name is set
/// 1. align: Ref, // if has_align is set
/// 2. init: Ref // if has_init is set
/// The source node is obtained from the containing `block_inline`.
pub const ExtendedVar = struct {
var_type: Ref,
pub const Small = packed struct {
has_lib_name: bool,
has_align: bool,
has_init: bool,
is_extern: bool,
_: u12 = undefined,
};
};
/// Trailing:
/// 0. param_type: Ref // for each param_types_len
/// - `none` indicates that the param type is `anytype`.
@ -3010,6 +3031,7 @@ const Writer = struct {
.@"asm" => try self.writeAsm(stream, extended),
.func => try self.writeFuncExtended(stream, extended),
.variable => try self.writeVarExtended(stream, extended),
.compile_log,
.typeof_peer,
@ -4015,6 +4037,34 @@ const Writer = struct {
);
}
fn writeVarExtended(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
const extra = self.code.extraData(Inst.ExtendedVar, extended.operand);
const small = @bitCast(Inst.ExtendedVar.Small, extended.small);
try self.writeInstRef(stream, extra.data.var_type);
var extra_index: usize = extra.end;
if (small.has_lib_name) {
const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
extra_index += 1;
try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)});
}
const align_inst: Inst.Ref = if (!small.has_align) .none else blk: {
const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1;
break :blk align_inst;
};
const init_inst: Inst.Ref = if (!small.has_init) .none else blk: {
const init_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1;
break :blk init_inst;
};
try self.writeFlag(stream, ", is_extern", small.is_extern);
try self.writeOptionalInstRef(stream, ", align=", align_inst);
try self.writeOptionalInstRef(stream, ", init=", init_inst);
try stream.writeAll("))");
}
fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].bool_br;
const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
@ -4078,15 +4128,19 @@ const Writer = struct {
try self.writeFlag(stream, ", vargs", var_args);
try self.writeFlag(stream, ", inferror", inferred_error_set);
try stream.writeAll(", {\n");
self.indent += 2;
const prev_param_count = self.param_count;
self.param_count = param_types.len;
try self.writeBody(stream, body);
self.param_count = prev_param_count;
self.indent -= 2;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}) ");
if (body.len == 0) {
try stream.writeAll(", {}) ");
} else {
try stream.writeAll(", {\n");
self.indent += 2;
const prev_param_count = self.param_count;
self.param_count = param_types.len;
try self.writeBody(stream, body);
self.param_count = prev_param_count;
self.indent -= 2;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}) ");
}
try self.writeSrc(stream, src);
}