Commit 86d564eed8

Andrew Kelley <andrew@ziglang.org>
2021-04-30 04:44:51
AstGen: implement extern variables
1 parent ba9b9cb
src/AstGen.zig
@@ -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));
     }
src/Module.zig
@@ -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,
src/Sema.zig
@@ -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,
src/Zir.zig
@@ -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);
     }