Commit 2ae72c6f09

Andrew Kelley <andrew@ziglang.org>
2021-05-04 05:05:29
Sema: implement ExportOptions support in `@export`
Also fix switch blocks not emitting their AIR code.
1 parent 69d18ad
lib/std/start.zig
@@ -83,7 +83,7 @@ fn _start2() callconv(.Naked) noreturn {
     exit2(0);
 }
 
-fn exit2(code: u8) noreturn {
+fn exit2(code: usize) noreturn {
     switch (builtin.stage2_arch) {
         .x86_64 => {
             asm volatile ("syscall"
src/Module.zig
@@ -3273,6 +3273,10 @@ pub fn analyzeExport(
 
     const owner_decl = scope.ownerDecl().?;
 
+    log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{
+        exported_decl.name, borrowed_symbol_name, owner_decl.name,
+    });
+
     new_export.* = .{
         .options = .{ .name = symbol_name },
         .src = src,
src/Sema.zig
@@ -1617,6 +1617,18 @@ fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inner
     return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
 }
 
+fn resolveBlockBody(
+    sema: *Sema,
+    parent_block: *Scope.Block,
+    src: LazySrcLoc,
+    child_block: *Scope.Block,
+    body: []const Zir.Inst.Index,
+    merges: *Scope.Block.Merges,
+) InnerError!*Inst {
+    _ = try sema.analyzeBody(child_block, body);
+    return sema.analyzeBlockBody(parent_block, src, child_block, merges);
+}
+
 fn analyzeBlockBody(
     sema: *Sema,
     parent_block: *Scope.Block,
@@ -1711,11 +1723,23 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
     const decl_name = sema.code.nullTerminatedString(extra.decl_name);
     const decl = try sema.lookupIdentifier(block, lhs_src, decl_name);
     const options = try sema.resolveInstConst(block, rhs_src, extra.options);
+    const struct_obj = options.ty.castTag(.@"struct").?.data;
+    const fields = options.val.castTag(.@"struct").?.data[0..struct_obj.fields.count()];
+    const name_index = struct_obj.fields.getIndex("name").?;
+    const linkage_index = struct_obj.fields.getIndex("linkage").?;
+    const section_index = struct_obj.fields.getIndex("section").?;
+    const export_name = try fields[name_index].toAllocatedBytes(sema.arena);
+    const linkage = fields[linkage_index].toEnum(
+        struct_obj.fields.items()[linkage_index].value.ty,
+        std.builtin.GlobalLinkage,
+    );
 
-    // TODO respect the name, linkage, and section options. Until then we export
-    // as the decl name.
-    _ = options;
-    const export_name = mem.spanZ(decl.name);
+    if (linkage != .Strong) {
+        return sema.mod.fail(&block.base, src, "TODO: implement exporting with non-strong linkage", .{});
+    }
+    if (!fields[section_index].isNull()) {
+        return sema.mod.fail(&block.base, src, "TODO: implement exporting with linksection", .{});
+    }
 
     try sema.mod.analyzeExport(&block.base, src, export_name, decl);
 }
@@ -3600,7 +3624,39 @@ fn analyzeSwitch(
         }),
     }
 
-    if (try sema.resolveDefinedValue(block, src, operand)) |operand_val| {
+    const block_inst = try sema.arena.create(Inst.Block);
+    block_inst.* = .{
+        .base = .{
+            .tag = Inst.Block.base_tag,
+            .ty = undefined, // Set after analysis.
+            .src = src,
+        },
+        .body = undefined,
+    };
+
+    var child_block: Scope.Block = .{
+        .parent = block,
+        .sema = sema,
+        .src_decl = block.src_decl,
+        .instructions = .{},
+        // TODO @as here is working around a stage1 miscompilation bug :(
+        .label = @as(?Scope.Block.Label, Scope.Block.Label{
+            .zir_block = switch_inst,
+            .merges = .{
+                .results = .{},
+                .br_list = .{},
+                .block_inst = block_inst,
+            },
+        }),
+        .inlining = block.inlining,
+        .is_comptime = block.is_comptime,
+    };
+    const merges = &child_block.label.?.merges;
+    defer child_block.instructions.deinit(gpa);
+    defer merges.results.deinit(gpa);
+    defer merges.br_list.deinit(gpa);
+
+    if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {
         var extra_index: usize = special.end;
         {
             var scalar_i: usize = 0;
@@ -3614,9 +3670,9 @@ fn analyzeSwitch(
 
                 // Validation above ensured these will succeed.
                 const item = sema.resolveInst(item_ref) catch unreachable;
-                const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
+                const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
                 if (operand_val.eql(item_val)) {
-                    return sema.resolveBody(block, body);
+                    return sema.resolveBlockBody(block, src, &child_block, body, merges);
                 }
             }
         }
@@ -3636,9 +3692,9 @@ fn analyzeSwitch(
                 for (items) |item_ref| {
                     // Validation above ensured these will succeed.
                     const item = sema.resolveInst(item_ref) catch unreachable;
-                    const item_val = sema.resolveConstValue(block, item.src, item) catch unreachable;
+                    const item_val = sema.resolveConstValue(&child_block, item.src, item) catch unreachable;
                     if (operand_val.eql(item_val)) {
-                        return sema.resolveBody(block, body);
+                        return sema.resolveBlockBody(block, src, &child_block, body, merges);
                     }
                 }
 
@@ -3650,59 +3706,27 @@ fn analyzeSwitch(
                     extra_index += 1;
 
                     // Validation above ensured these will succeed.
-                    const first_tv = sema.resolveInstConst(block, .unneeded, item_first) catch unreachable;
-                    const last_tv = sema.resolveInstConst(block, .unneeded, item_last) catch unreachable;
+                    const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable;
+                    const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable;
                     if (Value.compare(operand_val, .gte, first_tv.val) and
                         Value.compare(operand_val, .lte, last_tv.val))
                     {
-                        return sema.resolveBody(block, body);
+                        return sema.resolveBlockBody(block, src, &child_block, body, merges);
                     }
                 }
 
                 extra_index += body_len;
             }
         }
-        return sema.resolveBody(block, special.body);
+        return sema.resolveBlockBody(block, src, &child_block, special.body, merges);
     }
 
     if (scalar_cases_len + multi_cases_len == 0) {
-        return sema.resolveBody(block, special.body);
+        return sema.resolveBlockBody(block, src, &child_block, special.body, merges);
     }
 
     try sema.requireRuntimeBlock(block, src);
 
-    const block_inst = try sema.arena.create(Inst.Block);
-    block_inst.* = .{
-        .base = .{
-            .tag = Inst.Block.base_tag,
-            .ty = undefined, // Set after analysis.
-            .src = src,
-        },
-        .body = undefined,
-    };
-
-    var child_block: Scope.Block = .{
-        .parent = block,
-        .sema = sema,
-        .src_decl = block.src_decl,
-        .instructions = .{},
-        // TODO @as here is working around a stage1 miscompilation bug :(
-        .label = @as(?Scope.Block.Label, Scope.Block.Label{
-            .zir_block = switch_inst,
-            .merges = .{
-                .results = .{},
-                .br_list = .{},
-                .block_inst = block_inst,
-            },
-        }),
-        .inlining = block.inlining,
-        .is_comptime = block.is_comptime,
-    };
-    const merges = &child_block.label.?.merges;
-    defer child_block.instructions.deinit(gpa);
-    defer merges.results.deinit(gpa);
-    defer merges.br_list.deinit(gpa);
-
     // TODO when reworking AIR memory layout make multi cases get generated as cases,
     // not as part of the "else" block.
     const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
@@ -4614,7 +4638,8 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
 }
 
 fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const zir_datas = sema.code.instructions.items(.data);
+    const inst_data = zir_datas[inst].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     return sema.mod.constType(sema.arena, src, operand.ty);
@@ -5555,15 +5580,7 @@ fn zirFuncExtended(
         const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
         extra_index += 1;
         const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref);
-        // TODO this needs to resolve other kinds of Value tags rather than
-        // assuming the tag will be .enum_field_index.
-        const cc_field_index = cc_tv.val.castTag(.enum_field_index).?.data;
-        // TODO should `@intToEnum` do this `@intCast` for you?
-        const cc = @intToEnum(
-            std.builtin.CallingConvention,
-            @intCast(@typeInfo(std.builtin.CallingConvention).Enum.tag_type, cc_field_index),
-        );
-        break :blk cc;
+        break :blk cc_tv.val.toEnum(cc_tv.ty, std.builtin.CallingConvention);
     } else .Unspecified;
 
     const align_val: Value = if (small.has_align) blk: {
src/value.zig
@@ -715,6 +715,15 @@ pub const Value = extern union {
         };
     }
 
+    /// Asserts the type is an enum type.
+    pub fn toEnum(val: Value, enum_ty: Type, comptime E: type) E {
+        // TODO this needs to resolve other kinds of Value tags rather than
+        // assuming the tag will be .enum_field_index.
+        const field_index = val.castTag(.enum_field_index).?.data;
+        // TODO should `@intToEnum` do this `@intCast` for you?
+        return @intToEnum(E, @intCast(@typeInfo(E).Enum.tag_type, field_index));
+    }
+
     /// Asserts the value is an integer.
     pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
         switch (self.tag()) {
BRANCH_TODO
@@ -1,5 +1,3 @@
- * implement lazy struct field resolution; don't resolve struct fields until
-   they are needed.
  * AstGen threadlocal
  * extern "foo" for vars and for functions
  * namespace decls table can't reference ZIR memory because it can get modified on updates