@@ -70,6 +70,8 @@ local_zir_cache: Compilation.Directory,
7070/// The Export memory is owned by the `export_owners` table; the slice itself
7171/// is owned by this table. The slice is guaranteed to not be empty.
7272decl_exports : std .AutoArrayHashMapUnmanaged (Decl.Index , ArrayListUnmanaged (* Export )) = .{},
73+ /// Same as `decl_exports` but for exported constant values.
74+ value_exports : std .AutoArrayHashMapUnmanaged (InternPool.Index , ArrayListUnmanaged (* Export )) = .{},
7375/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
7476/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
7577/// is performing the export of another Decl.
@@ -244,6 +246,13 @@ pub const GlobalEmitH = struct {
244246
245247pub const ErrorInt = u32 ;
246248
249+ pub const Exported = union (enum ) {
250+ /// The Decl being exported. Note this is *not* the Decl performing the export.
251+ decl_index : Decl.Index ,
252+ /// Constant value being exported.
253+ value : InternPool.Index ,
254+ };
255+
247256pub const Export = struct {
248257 opts : Options ,
249258 src : LazySrcLoc ,
@@ -252,8 +261,7 @@ pub const Export = struct {
252261 /// The Decl containing the export statement. Inline function calls
253262 /// may cause this to be different from the owner_decl.
254263 src_decl : Decl.Index ,
255- /// The Decl being exported. Note this is *not* the Decl performing the export.
256- exported_decl : Decl.Index ,
264+ exported : Exported ,
257265 status : enum {
258266 in_progress ,
259267 failed ,
@@ -2575,6 +2583,11 @@ pub fn deinit(mod: *Module) void {
25752583 }
25762584 mod .decl_exports .deinit (gpa );
25772585
2586+ for (mod .value_exports .values ()) | * export_list | {
2587+ export_list .deinit (gpa );
2588+ }
2589+ mod .value_exports .deinit (gpa );
2590+
25782591 for (mod .export_owners .values ()) | * value | {
25792592 freeExportList (gpa , value );
25802593 }
@@ -4620,36 +4633,49 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void
46204633 var export_owners = (mod .export_owners .fetchSwapRemove (decl_index ) orelse return ).value ;
46214634
46224635 for (export_owners .items ) | exp | {
4623- if (mod .decl_exports .getPtr (exp .exported_decl )) | value_ptr | {
4624- // Remove exports with owner_decl matching the regenerating decl.
4625- const list = value_ptr .items ;
4626- var i : usize = 0 ;
4627- var new_len = list .len ;
4628- while (i < new_len ) {
4629- if (list [i ].owner_decl == decl_index ) {
4630- mem .copyBackwards (* Export , list [i .. ], list [i + 1 .. new_len ]);
4631- new_len -= 1 ;
4632- } else {
4633- i += 1 ;
4636+ switch (exp .exported ) {
4637+ .decl_index = > | exported_decl_index | {
4638+ if (mod .decl_exports .getPtr (exported_decl_index )) | export_list | {
4639+ // Remove exports with owner_decl matching the regenerating decl.
4640+ const list = export_list .items ;
4641+ var i : usize = 0 ;
4642+ var new_len = list .len ;
4643+ while (i < new_len ) {
4644+ if (list [i ].owner_decl == decl_index ) {
4645+ mem .copyBackwards (* Export , list [i .. ], list [i + 1 .. new_len ]);
4646+ new_len -= 1 ;
4647+ } else {
4648+ i += 1 ;
4649+ }
4650+ }
4651+ export_list .shrinkAndFree (mod .gpa , new_len );
4652+ if (new_len == 0 ) {
4653+ assert (mod .decl_exports .swapRemove (exported_decl_index ));
4654+ }
46344655 }
4635- }
4636- value_ptr .shrinkAndFree (mod .gpa , new_len );
4637- if (new_len == 0 ) {
4638- assert (mod .decl_exports .swapRemove (exp .exported_decl ));
4639- }
4640- }
4641- if (mod .comp .bin_file .cast (link .File .Elf )) | elf | {
4642- elf .deleteDeclExport (decl_index , exp .opts .name );
4643- }
4644- if (mod .comp .bin_file .cast (link .File .MachO )) | macho | {
4645- try macho .deleteDeclExport (decl_index , exp .opts .name );
4646- }
4647- if (mod .comp .bin_file .cast (link .File .Wasm )) | wasm | {
4648- wasm .deleteDeclExport (decl_index );
4649- }
4650- if (mod .comp .bin_file .cast (link .File .Coff )) | coff | {
4651- coff .deleteDeclExport (decl_index , exp .opts .name );
4656+ },
4657+ .value = > | value | {
4658+ if (mod .value_exports .getPtr (value )) | export_list | {
4659+ // Remove exports with owner_decl matching the regenerating decl.
4660+ const list = export_list .items ;
4661+ var i : usize = 0 ;
4662+ var new_len = list .len ;
4663+ while (i < new_len ) {
4664+ if (list [i ].owner_decl == decl_index ) {
4665+ mem .copyBackwards (* Export , list [i .. ], list [i + 1 .. new_len ]);
4666+ new_len -= 1 ;
4667+ } else {
4668+ i += 1 ;
4669+ }
4670+ }
4671+ export_list .shrinkAndFree (mod .gpa , new_len );
4672+ if (new_len == 0 ) {
4673+ assert (mod .value_exports .swapRemove (value ));
4674+ }
4675+ }
4676+ },
46524677 }
4678+ try mod .comp .bin_file .deleteDeclExport (decl_index , exp .opts .name );
46534679 if (mod .failed_exports .fetchSwapRemove (exp )) | failed_kv | {
46544680 failed_kv .value .destroy (mod .gpa );
46554681 }
@@ -5503,48 +5529,63 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
55035529/// reporting compile errors. In this function we emit exported symbol collision
55045530/// errors and communicate exported symbols to the linker backend.
55055531pub fn processExports (mod : * Module ) ! void {
5506- const gpa = mod .gpa ;
55075532 // Map symbol names to `Export` for name collision detection.
5508- var symbol_exports : std .AutoArrayHashMapUnmanaged (InternPool.NullTerminatedString , * Export ) = .{};
5509- defer symbol_exports .deinit (gpa );
5510-
5511- var it = mod .decl_exports .iterator ();
5512- while (it .next ()) | entry | {
5513- const exported_decl = entry .key_ptr .* ;
5514- const exports = entry .value_ptr .items ;
5515- for (exports ) | new_export | {
5516- const gop = try symbol_exports .getOrPut (gpa , new_export .opts .name );
5517- if (gop .found_existing ) {
5518- new_export .status = .failed_retryable ;
5519- try mod .failed_exports .ensureUnusedCapacity (gpa , 1 );
5520- const src_loc = new_export .getSrcLoc (mod );
5521- const msg = try ErrorMsg .create (gpa , src_loc , "exported symbol collision: {}" , .{
5522- new_export .opts .name .fmt (& mod .intern_pool ),
5523- });
5524- errdefer msg .destroy (gpa );
5525- const other_export = gop .value_ptr .* ;
5526- const other_src_loc = other_export .getSrcLoc (mod );
5527- try mod .errNoteNonLazy (other_src_loc , msg , "other symbol here" , .{});
5528- mod .failed_exports .putAssumeCapacityNoClobber (new_export , msg );
5529- new_export .status = .failed ;
5530- } else {
5531- gop .value_ptr .* = new_export ;
5532- }
5533+ var symbol_exports : SymbolExports = .{};
5534+ defer symbol_exports .deinit (mod .gpa );
5535+
5536+ for (mod .decl_exports .keys (), mod .decl_exports .values ()) | exported_decl , exports_list | {
5537+ const exported : Exported = .{ .decl_index = exported_decl };
5538+ try processExportsInner (mod , & symbol_exports , exported , exports_list .items );
5539+ }
5540+
5541+ for (mod .value_exports .keys (), mod .value_exports .values ()) | exported_value , exports_list | {
5542+ const exported : Exported = .{ .value = exported_value };
5543+ try processExportsInner (mod , & symbol_exports , exported , exports_list .items );
5544+ }
5545+ }
5546+
5547+ const SymbolExports = std .AutoArrayHashMapUnmanaged (InternPool .NullTerminatedString , * Export );
5548+
5549+ fn processExportsInner (
5550+ mod : * Module ,
5551+ symbol_exports : * SymbolExports ,
5552+ exported : Exported ,
5553+ exports : []const * Export ,
5554+ ) error {OutOfMemory }! void {
5555+ const gpa = mod .gpa ;
5556+
5557+ for (exports ) | new_export | {
5558+ const gop = try symbol_exports .getOrPut (gpa , new_export .opts .name );
5559+ if (gop .found_existing ) {
5560+ new_export .status = .failed_retryable ;
5561+ try mod .failed_exports .ensureUnusedCapacity (gpa , 1 );
5562+ const src_loc = new_export .getSrcLoc (mod );
5563+ const msg = try ErrorMsg .create (gpa , src_loc , "exported symbol collision: {}" , .{
5564+ new_export .opts .name .fmt (& mod .intern_pool ),
5565+ });
5566+ errdefer msg .destroy (gpa );
5567+ const other_export = gop .value_ptr .* ;
5568+ const other_src_loc = other_export .getSrcLoc (mod );
5569+ try mod .errNoteNonLazy (other_src_loc , msg , "other symbol here" , .{});
5570+ mod .failed_exports .putAssumeCapacityNoClobber (new_export , msg );
5571+ new_export .status = .failed ;
5572+ } else {
5573+ gop .value_ptr .* = new_export ;
55335574 }
5534- mod .comp .bin_file .updateDeclExports (mod , exported_decl , exports ) catch | err | switch (err ) {
5535- error .OutOfMemory = > return error .OutOfMemory ,
5536- else = > {
5537- const new_export = exports [0 ];
5538- new_export .status = .failed_retryable ;
5539- try mod .failed_exports .ensureUnusedCapacity (gpa , 1 );
5540- const src_loc = new_export .getSrcLoc (mod );
5541- const msg = try ErrorMsg .create (gpa , src_loc , "unable to export: {s}" , .{
5542- @errorName (err ),
5543- });
5544- mod .failed_exports .putAssumeCapacityNoClobber (new_export , msg );
5545- },
5546- };
55475575 }
5576+ mod .comp .bin_file .updateExports (mod , exported , exports ) catch | err | switch (err ) {
5577+ error .OutOfMemory = > return error .OutOfMemory ,
5578+ else = > {
5579+ const new_export = exports [0 ];
5580+ new_export .status = .failed_retryable ;
5581+ try mod .failed_exports .ensureUnusedCapacity (gpa , 1 );
5582+ const src_loc = new_export .getSrcLoc (mod );
5583+ const msg = try ErrorMsg .create (gpa , src_loc , "unable to export: {s}" , .{
5584+ @errorName (err ),
5585+ });
5586+ mod .failed_exports .putAssumeCapacityNoClobber (new_export , msg );
5587+ },
5588+ };
55485589}
55495590
55505591pub fn populateTestFunctions (
0 commit comments