@@ -70,6 +70,8 @@ local_zir_cache: Compilation.Directory,
70
70
/// The Export memory is owned by the `export_owners` table; the slice itself
71
71
/// is owned by this table. The slice is guaranteed to not be empty.
72
72
decl_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 )) = .{},
73
75
/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
74
76
/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
75
77
/// is performing the export of another Decl.
@@ -244,6 +246,13 @@ pub const GlobalEmitH = struct {
244
246
245
247
pub const ErrorInt = u32 ;
246
248
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
+
247
256
pub const Export = struct {
248
257
opts : Options ,
249
258
src : LazySrcLoc ,
@@ -252,8 +261,7 @@ pub const Export = struct {
252
261
/// The Decl containing the export statement. Inline function calls
253
262
/// may cause this to be different from the owner_decl.
254
263
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 ,
257
265
status : enum {
258
266
in_progress ,
259
267
failed ,
@@ -2575,6 +2583,11 @@ pub fn deinit(mod: *Module) void {
2575
2583
}
2576
2584
mod .decl_exports .deinit (gpa );
2577
2585
2586
+ for (mod .value_exports .values ()) | * export_list | {
2587
+ export_list .deinit (gpa );
2588
+ }
2589
+ mod .value_exports .deinit (gpa );
2590
+
2578
2591
for (mod .export_owners .values ()) | * value | {
2579
2592
freeExportList (gpa , value );
2580
2593
}
@@ -4620,36 +4633,49 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void
4620
4633
var export_owners = (mod .export_owners .fetchSwapRemove (decl_index ) orelse return ).value ;
4621
4634
4622
4635
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
+ }
4634
4655
}
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
+ },
4652
4677
}
4678
+ try mod .comp .bin_file .deleteDeclExport (decl_index , exp .opts .name );
4653
4679
if (mod .failed_exports .fetchSwapRemove (exp )) | failed_kv | {
4654
4680
failed_kv .value .destroy (mod .gpa );
4655
4681
}
@@ -5503,48 +5529,63 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
5503
5529
/// reporting compile errors. In this function we emit exported symbol collision
5504
5530
/// errors and communicate exported symbols to the linker backend.
5505
5531
pub fn processExports (mod : * Module ) ! void {
5506
- const gpa = mod .gpa ;
5507
5532
// 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 ;
5533
5574
}
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
- };
5547
5575
}
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
+ };
5548
5589
}
5549
5590
5550
5591
pub fn populateTestFunctions (
0 commit comments