diff --git a/src/Sema.zig b/src/Sema.zig index cbfe6421db..0504bb7e5e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5746,91 +5746,94 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } } + const export_ty = ptr_ty.childType(zcu); + if (!export_ty.validateExtern(.other, zcu)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); + try sema.addDeclaredHereNote(msg, export_ty); + break :msg msg; + }); + } + const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr; - switch (ptr_info.base_addr) { + const target: Zcu.Exported = switch (ptr_info.base_addr) { .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}), .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}), - .uav => |uav| { - if (ptr_info.byte_offset != 0) { - return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); + .uav => |uav| .{ .uav = uav.val }, + .nav => |orig_nav| target: { + try sema.ensureNavResolved(block, src, orig_nav, .fully); + const export_nav = switch (ip.indexToKey(ip.getNav(orig_nav).status.fully_resolved.val)) { + .variable => |v| v.owner_nav, + .@"extern" => |e| e.owner_nav, + .func => |f| f.owner_nav, + else => orig_nav, + }; + if (ip.getNav(export_nav).getExtern(ip) != null) { + return sema.fail(block, src, "export target cannot be extern", .{}); } - if (zcu.llvm_object != null and options.linkage == .internal) return; - const export_ty = Value.fromInterned(uav.val).typeOf(zcu); - if (!export_ty.validateExtern(.other, zcu)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); - try sema.addDeclaredHereNote(msg, export_ty); - break :msg msg; - }); - } - try sema.exports.append(zcu.gpa, .{ - .opts = options, - .src = src, - .exported = .{ .uav = uav.val }, - .status = .in_progress, - }); - }, - .nav => |nav| { - if (ptr_info.byte_offset != 0) { - return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); - } - try sema.analyzeExport(block, src, options, nav); + try sema.maybeQueueFuncBodyAnalysis(block, src, export_nav); + break :target .{ .nav = export_nav }; }, + }; + if (ptr_info.byte_offset != 0) { + return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); } + if (zcu.llvm_object != null and options.linkage == .internal) return; + try sema.exports.append(zcu.gpa, .{ + .opts = options, + .src = src, + .exported = target, + .status = .in_progress, + }); } -pub fn analyzeExport( +/// Asserts that `sema.owner` is a `.nav_val` whose value is resolved. +/// +/// Exports that `Nav` by the given name with all other options set to default. +pub fn analyzeExportSelfNav( sema: *Sema, block: *Block, src: LazySrcLoc, - options: Zcu.Export.Options, - orig_nav_index: InternPool.Nav.Index, + name: InternPool.NullTerminatedString, ) !void { const gpa = sema.gpa; const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - if (zcu.llvm_object != null and options.linkage == .internal) - return; - - try sema.ensureNavResolved(block, src, orig_nav_index, .fully); - - const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { - .variable => |v| v.owner_nav, - .@"extern" => |e| e.owner_nav, - .func => |f| f.owner_nav, - else => orig_nav_index, - }; - - const exported_nav = ip.getNav(exported_nav_index); - const export_ty: Type = .fromInterned(exported_nav.typeOf(ip)); + const orig_nav = sema.owner.unwrap().nav_val; + const export_val: Value = .fromInterned(ip.getNav(orig_nav).status.fully_resolved.val); + const export_ty = export_val.typeOf(zcu); if (!export_ty.validateExtern(.other, zcu)) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); - try sema.addDeclaredHereNote(msg, export_ty); break :msg msg; }); } - // TODO: some backends might support re-exporting extern decls - if (exported_nav.getExtern(ip) != null) { - return sema.fail(block, src, "export target cannot be extern", .{}); - } - - try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index); + const export_nav = switch (ip.indexToKey(export_val.toIntern())) { + .variable => |v| v.owner_nav, + .@"extern" => |e| e.owner_nav, + .func => |f| export_nav: { + assert(export_ty.fnHasRuntimeBits(zcu)); // otherwise `validateExtern` failed above + const orig_fn_index = ip.unwrapCoercedFunc(export_val.toIntern()); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); + break :export_nav f.owner_nav; + }, + else => orig_nav, + }; try sema.exports.append(gpa, .{ - .opts = options, + .opts = .{ .name = name }, .src = src, - .exported = .{ .nav = exported_nav_index }, + .exported = .{ .nav = export_nav }, .status = .in_progress, }); } @@ -33739,21 +33742,9 @@ pub fn flushExports(sema: *Sema) !void { const zcu = sema.pt.zcu; const gpa = zcu.gpa; - // There may be existing exports. For instance, a struct may export - // things during both field type resolution and field default resolution. - // - // So, pick up and delete any existing exports. This strategy performs - // redundant work, but that's okay, because this case is exceedingly rare. - // - // MLUGG TODO: is this still possible? if not, delete this logic and combine deleteUnitExports into resetUnit - if (zcu.single_exports.get(sema.owner)) |export_idx| { - try sema.exports.append(gpa, export_idx.ptr(zcu).*); - } else if (zcu.multi_exports.get(sema.owner)) |info| { - try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); - } - zcu.deleteUnitExports(sema.owner); + assert(!zcu.single_exports.contains(sema.owner)); + assert(!zcu.multi_exports.contains(sema.owner)); - // `sema.exports` is completed; store the data into the `Zcu`. if (sema.exports.items.len == 1) { try zcu.single_exports.ensureUnusedCapacity(gpa, 1); const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: { @@ -34038,7 +34029,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ }; } -fn setTypeName( +pub fn setTypeName( sema: *Sema, block: *Block, wip: *const InternPool.WipContainerType, diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index cf809603c0..06218806a4 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -125,89 +125,77 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter return (try pt.aggregateValue(.fromInterned(ty), values)).toIntern(); }, .struct_literal => |init| { - if (true) @panic("MLUGG TODO"); const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len); for (0..init.names.len) |i| { elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i))); } - const struct_ty = switch (try ip.getStructType( - gpa, - io, - pt.tid, - .{ - .layout = .auto, - .fields_len = @intCast(init.names.len), - .known_non_opv = false, - .requires_comptime = .no, - .any_comptime_fields = true, - .any_default_inits = true, - .inits_resolved = true, - .any_aligned_fields = false, - .key = .{ .reified = .{ - .zir_index = self.base_node_inst, - .type_hash = hash: { - var hasher: std.hash.Wyhash = .init(0); - hasher.update(std.mem.asBytes(&node)); - hasher.update(std.mem.sliceAsBytes(elems)); - hasher.update(std.mem.sliceAsBytes(init.names)); - break :hash hasher.final(); - }, - } }, + const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ + .zir_index = self.base_node_inst, + .type_hash = hash: { + var hasher: std.hash.Wyhash = .init(0); + hasher.update(std.mem.asBytes(&node)); + hasher.update(std.mem.sliceAsBytes(elems)); + hasher.update(std.mem.sliceAsBytes(init.names)); + break :hash hasher.final(); }, - false, - )) { + .fields_len = @intCast(init.names.len), + .layout = .auto, + .any_comptime_fields = true, + .any_field_defaults = true, + .any_field_aligns = false, + .packed_backing_int_type = .none, + })) { + .existing => |ty| .fromInterned(ty), .wip => |wip| ty: { errdefer wip.cancel(ip, pt.tid); - const type_name = try self.sema.createTypeName( - self.block, - .anon, - "struct", - self.base_node_inst.resolve(ip), - wip.index, - ); - wip.setName(ip, type_name.name, type_name.nav); + const block = self.block; + const zcu = pt.zcu; + try self.sema.setTypeName(block, &wip, .anon, "struct", self.base_node_inst.resolve(ip).?); - const struct_type = ip.loadStructType(wip.index); - - for (init.names, 0..) |name, field_idx| { - const name_interned = try ip.getOrPutString( + // Reified structs have field information populated immediately. + @memcpy(wip.field_values.get(ip), elems); + if (init.names.len > 0) { + // All fields are comptime, but unused bits remain zeroed. + const unused_bits = switch (init.names.len % 32) { + 0 => 0, + else => |n| 32 - n, + }; + const comptime_bits = wip.field_is_comptime_bits.getAll(ip); + @memset(comptime_bits[0 .. comptime_bits.len - 1], std.math.maxInt(u32)); + comptime_bits[comptime_bits.len - 1] = @as(u32, std.math.maxInt(u32)) >> @intCast(unused_bits); + } + for ( + init.names, + wip.field_names.get(ip), + wip.field_types.get(ip), + wip.field_values.get(ip), + ) |zoir_name, *field_name, *field_ty, field_val| { + field_name.* = try ip.getOrPutString( gpa, io, pt.tid, - name.get(self.file.zoir.?), + zoir_name.get(self.file.zoir.?), .no_embedded_nulls, ); - assert(struct_type.addFieldName(ip, name_interned) == null); - struct_type.setFieldComptime(ip, field_idx); - } - - @memcpy(struct_type.field_inits.get(ip), elems); - const types = struct_type.field_types.get(ip); - for (0..init.names.len) |i| { - types[i] = Value.fromInterned(elems[i]).typeOf(pt.zcu).toIntern(); + field_ty.* = ip.typeOf(field_val); } const new_namespace_index = try pt.createNamespace(.{ - .parent = self.block.namespace.toOptional(), + .parent = block.namespace.toOptional(), .owner_type = wip.index, - .file_scope = self.block.getFileScopeIndex(pt.zcu), - .generation = pt.zcu.generation, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, }); - try pt.zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); - codegen_type: { - if (pt.zcu.comp.config.use_llvm) break :codegen_type; - if (self.block.ownerModule().strip) break :codegen_type; - pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try pt.zcu.comp.queueJob(.{ .link_type = wip.index }); - } - break :ty wip.finish(ip, new_namespace_index); + errdefer pt.destroyNamespace(new_namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, - .existing => |ty| ty, }; - try self.sema.declareDependency(.{ .interned = struct_ty }); try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. + try self.sema.ensureLayoutResolved(struct_ty, self.nodeSrc(node), .init); - return (try pt.aggregateValue(.fromInterned(struct_ty), elems)).toIntern(); + return (try pt.aggregateValue(struct_ty, elems)).toIntern(); }, } } diff --git a/src/Value.zig b/src/Value.zig index 5cc20853da..cf8d0acc67 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -1954,7 +1954,6 @@ pub const PointerDeriveStep = union(enum) { /// which prefer field/elem accesses when lowering constant pointer values. /// It is also used by the Value printing logic for pointers. pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep { - // MLUGG TODO: audit tf outta this code const zcu = pt.zcu; const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; const base_derive: PointerDeriveStep = switch (ptr.base_addr) { diff --git a/src/Zcu.zig b/src/Zcu.zig index 49abdbed56..ce90f2c417 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3518,50 +3518,10 @@ pub const ImportResult = struct { module: ?*Package.Module, }; -/// Delete all the Export objects that are caused by this `AnalUnit`. Re-analysis of -/// this `AnalUnit` will cause them to be re-created (or not). -pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { - const gpa = zcu.gpa; - - const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| - .{ @intFromEnum(kv.value), 1 } - else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| - .{ info.value.index, info.value.len } - else - return; - - const exports = zcu.all_exports.items[exports_base..][0..exports_len]; - - // In an only-c build, we're guaranteed to never use incremental compilation, so there are - // guaranteed not to be any exports in the output file that need deleting (since we only call - // `updateExports` on flush). - // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports - // within a single update. - if (dev.env.supports(.incremental)) { - for (exports, exports_base..) |exp, export_index_usize| { - const export_idx: Export.Index = @enumFromInt(export_index_usize); - if (zcu.comp.bin_file) |lf| { - lf.deleteExport(exp.exported, exp.opts.name); - } - if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| { - failed_kv.value.destroy(gpa); - } - } - } - - zcu.free_exports.ensureUnusedCapacity(gpa, exports_len) catch { - // This space will be reused eventually, so we need not propagate this error. - // Just leak it for now, and let GC reclaim it later on. - return; - }; - for (exports_base..exports_base + exports_len) |export_idx| { - zcu.free_exports.appendAssumeCapacity(@enumFromInt(export_idx)); - } -} - /// Prepares `unit` for re-analysis by clearing all of the following state: /// * Compile errors associated with `unit` /// * Compile logs associated with `unit` +/// * Exports performed by `unit` /// * Dependencies from `unit` on other things /// * References from `unit` to other units /// Delete all references in `reference_table` which are caused by `unit`, and all dependencies it @@ -3593,6 +3553,36 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void { } } + // Exports + exports: { + const base: u32, const len: u32 = index: { + if (zcu.single_exports.fetchSwapRemove(unit)) |kv| { + break :index .{ @intFromEnum(kv.value), 1 }; + } + if (zcu.multi_exports.fetchSwapRemove(unit)) |kv| { + break :index .{ kv.value.index, kv.value.len }; + } + break :exports; + }; + for (zcu.all_exports.items[base..][0..len], base..) |exp, exp_index_usize| { + const exp_index: Export.Index = @enumFromInt(exp_index_usize); + if (zcu.comp.bin_file) |lf| { + lf.deleteExport(exp.exported, exp.opts.name); + } + if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| { + failed_kv.value.destroy(gpa); + } + } + zcu.free_exports.ensureUnusedCapacity(gpa, len) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break :exports; + }; + for (base..base + len) |exp_index| { + zcu.free_exports.appendAssumeCapacity(@enumFromInt(exp_index)); + } + } + // Dependencies zcu.intern_pool.removeDependenciesForDepender(gpa, unit); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 954d1cd3bf..b9ad8f3dfd 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -752,7 +752,6 @@ pub fn ensureMemoizedStateUpToDate( if (was_outdated) { dev.check(.incremental); _ = zcu.outdated_ready.swapRemove(unit); - // No need for `deleteUnitExports` because we never export anything. zcu.resetUnit(unit); } else { if (prev_failed) return error.AnalysisFail; @@ -874,7 +873,6 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU _ = zcu.outdated_ready.swapRemove(anal_unit); // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. if (dev.env.supports(.incremental)) { - zcu.deleteUnitExports(anal_unit); zcu.resetUnit(anal_unit); } } else { @@ -1033,7 +1031,6 @@ pub fn ensureTypeLayoutUpToDate( _ = zcu.outdated_ready.swapRemove(anal_unit); // `was_outdated` is true in the initial update, so this isn't a `dev.check`. if (dev.env.supports(.incremental)) { - zcu.deleteUnitExports(anal_unit); zcu.resetUnit(anal_unit); } // For types, we already know that we have to invalidate all dependees. @@ -1151,7 +1148,6 @@ pub fn ensureNavValUpToDate( if (was_outdated) { dev.check(.incremental); _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. @@ -1238,7 +1234,7 @@ fn analyzeNavVal( const zir_decl = zir.getDeclaration(inst_resolved.inst); try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason); - errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); var analysis_arena: std.heap.ArenaAllocator = .init(gpa); defer analysis_arena.deinit(); @@ -1443,15 +1439,11 @@ fn analyzeNavVal( .@"addrspace" = modifiers.@"addrspace", }); - // Mark the unit as completed before evaluating the export! - // MLUGG TODO: do we really need to do this? - assert(zcu.analysis_in_progress.swapRemove(anal_unit)); - if (zir_decl.linkage == .@"export") { const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); const name_slice = zir.nullTerminatedString(zir_decl.name); const name_ip = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); - try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id); + try sema.analyzeExportSelfNav(&block, export_src, name_ip); } try sema.flushExports(); @@ -1514,7 +1506,6 @@ pub fn ensureNavTypeUpToDate( if (was_outdated) { dev.check(.incremental); _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. @@ -1751,7 +1742,6 @@ pub fn ensureFuncBodyUpToDate( if (was_outdated) { dev.check(.incremental); _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this function.