| ... | @@ -41,14 +41,11 @@ root_pkg: *Package, | ... | @@ -41,14 +41,11 @@ root_pkg: *Package, |
| 41 | global_zir_cache: Compilation.Directory, | 41 | global_zir_cache: Compilation.Directory, |
| 42 | /// Used by AstGen worker to load and store ZIR cache. | 42 | /// Used by AstGen worker to load and store ZIR cache. |
| 43 | local_zir_cache: Compilation.Directory, | 43 | local_zir_cache: Compilation.Directory, |
| 44 | /// It's rare for a decl to be exported, so we save memory by having a sparse map of | 44 | /// It's rare for a decl to be exported, so we save memory by having a sparse |
| 45 | /// Decl pointers to details about them being exported. | 45 | /// map of Decl pointers to details about them being exported. |
| 46 | /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. | 46 | /// The Export memory is owned by the `export_owners` table; the slice itself |
| 47 | /// The slice is guaranteed to not be empty. | 47 | /// is owned by this table. The slice is guaranteed to not be empty. |
| 48 | decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | 48 | decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 49 | /// We track which export is associated with the given symbol name for quick | | |
| 50 | /// detection of symbol collisions. | | |
| 51 | symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}, | | |
| 52 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl | 49 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl |
| 53 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that | 50 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that |
| 54 | /// is performing the export of another Decl. | 51 | /// is performing the export of another Decl. |
| ... | @@ -144,6 +141,14 @@ pub const Export = struct { | ... | @@ -144,6 +141,14 @@ pub const Export = struct { |
| 144 | failed_retryable, | 141 | failed_retryable, |
| 145 | complete, | 142 | complete, |
| 146 | }, | 143 | }, |
| | 144 | |
| | 145 | pub fn getSrcLoc(exp: Export) SrcLoc { |
| | 146 | return .{ |
| | 147 | .file_scope = exp.owner_decl.namespace.file_scope, |
| | 148 | .parent_decl_node = exp.owner_decl.src_node, |
| | 149 | .lazy = exp.src, |
| | 150 | }; |
| | 151 | } |
| 147 | }; | 152 | }; |
| 148 | | 153 | |
| 149 | /// When Module emit_h field is non-null, each Decl is allocated via this struct, so that | 154 | /// When Module emit_h field is non-null, each Decl is allocated via this struct, so that |
| ... | @@ -2184,8 +2189,6 @@ pub fn deinit(mod: *Module) void { | ... | @@ -2184,8 +2189,6 @@ pub fn deinit(mod: *Module) void { |
| 2184 | } | 2189 | } |
| 2185 | mod.export_owners.deinit(gpa); | 2190 | mod.export_owners.deinit(gpa); |
| 2186 | | 2191 | |
| 2187 | mod.symbol_exports.deinit(gpa); | | |
| 2188 | | | |
| 2189 | var it = mod.global_error_set.iterator(); | 2192 | var it = mod.global_error_set.iterator(); |
| 2190 | while (it.next()) |entry| { | 2193 | while (it.next()) |entry| { |
| 2191 | gpa.free(entry.key); | 2194 | gpa.free(entry.key); |
| ... | @@ -2779,7 +2782,10 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -2779,7 +2782,10 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2779 | for (decl.dependencies.items()) |entry| { | 2782 | for (decl.dependencies.items()) |entry| { |
| 2780 | const dep = entry.key; | 2783 | const dep = entry.key; |
| 2781 | dep.removeDependant(decl); | 2784 | dep.removeDependant(decl); |
| 2782 | if (dep.dependants.items().len == 0 and !dep.deletion_flag) { | 2785 | if (dep.dependants.count() == 0 and !dep.deletion_flag) { |
| | 2786 | log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ |
| | 2787 | decl, decl.name, dep, dep.name, |
| | 2788 | }); |
| 2783 | // We don't perform a deletion here, because this Decl or another one | 2789 | // We don't perform a deletion here, because this Decl or another one |
| 2784 | // may end up referencing it before the update is complete. | 2790 | // may end up referencing it before the update is complete. |
| 2785 | dep.deletion_flag = true; | 2791 | dep.deletion_flag = true; |
| ... | @@ -2795,11 +2801,18 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -2795,11 +2801,18 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2795 | }; | 2801 | }; |
| 2796 | | 2802 | |
| 2797 | const type_changed = mod.semaDecl(decl) catch |err| switch (err) { | 2803 | const type_changed = mod.semaDecl(decl) catch |err| switch (err) { |
| 2798 | error.OutOfMemory => return error.OutOfMemory, | 2804 | error.AnalysisFail => { |
| 2799 | error.AnalysisFail => return error.AnalysisFail, | 2805 | if (decl.analysis == .in_progress) { |
| | 2806 | // If this decl caused the compile error, the analysis field would |
| | 2807 | // be changed to indicate it was this Decl's fault. Because this |
| | 2808 | // did not happen, we infer here that it was a dependency failure. |
| | 2809 | decl.analysis = .dependency_failure; |
| | 2810 | } |
| | 2811 | return error.AnalysisFail; |
| | 2812 | }, |
| 2800 | else => { | 2813 | else => { |
| 2801 | decl.analysis = .sema_failure_retryable; | 2814 | decl.analysis = .sema_failure_retryable; |
| 2802 | try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1); | 2815 | try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); |
| 2803 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 2816 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 2804 | mod.gpa, | 2817 | mod.gpa, |
| 2805 | decl.srcLoc(), | 2818 | decl.srcLoc(), |
| ... | @@ -2818,7 +2831,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -2818,7 +2831,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2818 | const dep = entry.key; | 2831 | const dep = entry.key; |
| 2819 | switch (dep.analysis) { | 2832 | switch (dep.analysis) { |
| 2820 | .unreferenced => unreachable, | 2833 | .unreferenced => unreachable, |
| 2821 | .in_progress => unreachable, | 2834 | .in_progress => continue, // already doing analysis, ok |
| 2822 | .outdated => continue, // already queued for update | 2835 | .outdated => continue, // already queued for update |
| 2823 | | 2836 | |
| 2824 | .file_failure, | 2837 | .file_failure, |
| ... | @@ -3115,8 +3128,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { | ... | @@ -3115,8 +3128,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3115 | | 3128 | |
| 3116 | /// Returns the depender's index of the dependee. | 3129 | /// Returns the depender's index of the dependee. |
| 3117 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { | 3130 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { |
| 3118 | try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.count() + 1); | 3131 | if (depender == dependee) return; |
| 3119 | try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.count() + 1); | 3132 | |
| | 3133 | log.debug("{*} ({s}) depends on {*} ({s})", .{ |
| | 3134 | depender, depender.name, dependee, dependee.name, |
| | 3135 | }); |
| | 3136 | |
| | 3137 | try depender.dependencies.ensureUnusedCapacity(mod.gpa, 1); |
| | 3138 | try dependee.dependants.ensureUnusedCapacity(mod.gpa, 1); |
| 3120 | | 3139 | |
| 3121 | if (dependee.deletion_flag) { | 3140 | if (dependee.deletion_flag) { |
| 3122 | dependee.deletion_flag = false; | 3141 | dependee.deletion_flag = false; |
| ... | @@ -3513,7 +3532,6 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { | ... | @@ -3513,7 +3532,6 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { |
| 3513 | if (mod.failed_exports.swapRemove(exp)) |entry| { | 3532 | if (mod.failed_exports.swapRemove(exp)) |entry| { |
| 3514 | entry.value.destroy(mod.gpa); | 3533 | entry.value.destroy(mod.gpa); |
| 3515 | } | 3534 | } |
| 3516 | _ = mod.symbol_exports.swapRemove(exp.options.name); | | |
| 3517 | mod.gpa.free(exp.options.name); | 3535 | mod.gpa.free(exp.options.name); |
| 3518 | mod.gpa.destroy(exp); | 3536 | mod.gpa.destroy(exp); |
| 3519 | } | 3537 | } |
| ... | @@ -3726,38 +3744,6 @@ pub fn analyzeExport( | ... | @@ -3726,38 +3744,6 @@ pub fn analyzeExport( |
| 3726 | de_gop.entry.value = try mod.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); | 3744 | de_gop.entry.value = try mod.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); |
| 3727 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; | 3745 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; |
| 3728 | errdefer de_gop.entry.value = mod.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); | 3746 | errdefer de_gop.entry.value = mod.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); |
| 3729 | | | |
| 3730 | if (mod.symbol_exports.get(symbol_name)) |other_export| { | | |
| 3731 | new_export.status = .failed_retryable; | | |
| 3732 | try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); | | |
| 3733 | const msg = try mod.errMsg( | | |
| 3734 | scope, | | |
| 3735 | src, | | |
| 3736 | "exported symbol collision: {s}", | | |
| 3737 | .{symbol_name}, | | |
| 3738 | ); | | |
| 3739 | errdefer msg.destroy(mod.gpa); | | |
| 3740 | const other_src_loc: SrcLoc = .{ | | |
| 3741 | .file_scope = other_export.owner_decl.namespace.file_scope, | | |
| 3742 | .parent_decl_node = other_export.owner_decl.src_node, | | |
| 3743 | .lazy = other_export.src, | | |
| 3744 | }; | | |
| 3745 | try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); | | |
| 3746 | mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); | | |
| 3747 | new_export.status = .failed; | | |
| 3748 | return; | | |
| 3749 | } | | |
| 3750 | | | |
| 3751 | try mod.symbol_exports.putNoClobber(mod.gpa, symbol_name, new_export); | | |
| 3752 | mod.comp.bin_file.updateDeclExports(mod, exported_decl, de_gop.entry.value) catch |err| switch (err) { | | |
| 3753 | error.OutOfMemory => return error.OutOfMemory, | | |
| 3754 | else => { | | |
| 3755 | new_export.status = .failed_retryable; | | |
| 3756 | try mod.failed_exports.ensureCapacity(mod.gpa, mod.failed_exports.items().len + 1); | | |
| 3757 | const msg = try mod.errMsg(scope, src, "unable to export: {s}", .{@errorName(err)}); | | |
| 3758 | mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); | | |
| 3759 | }, | | |
| 3760 | }; | | |
| 3761 | } | 3747 | } |
| 3762 | pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst { | 3748 | pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst { |
| 3763 | const const_inst = try arena.create(ir.Inst.Constant); | 3749 | const const_inst = try arena.create(ir.Inst.Constant); |
| ... | @@ -3903,59 +3889,6 @@ pub fn getNextAnonNameIndex(mod: *Module) usize { | ... | @@ -3903,59 +3889,6 @@ pub fn getNextAnonNameIndex(mod: *Module) usize { |
| 3903 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); | 3889 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); |
| 3904 | } | 3890 | } |
| 3905 | | 3891 | |
| 3906 | /// This looks up a bare identifier in the given scope. This will walk up the tree of namespaces | | |
| 3907 | /// in scope and check each one for the identifier. | | |
| 3908 | /// TODO emit a compile error if more than one decl would be matched. | | |
| 3909 | pub fn lookupIdentifier( | | |
| 3910 | mod: *Module, | | |
| 3911 | scope: *Scope, | | |
| 3912 | ident_name: []const u8, | | |
| 3913 | ) error{AnalysisFail}!?*Decl { | | |
| 3914 | var namespace = scope.namespace(); | | |
| 3915 | while (true) { | | |
| 3916 | if (try mod.lookupInNamespace(namespace, ident_name, false)) |decl| { | | |
| 3917 | return decl; | | |
| 3918 | } | | |
| 3919 | namespace = namespace.parent orelse break; | | |
| 3920 | } | | |
| 3921 | return null; | | |
| 3922 | } | | |
| 3923 | | | |
| 3924 | /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but | | |
| 3925 | /// only for ones in the specified namespace. | | |
| 3926 | pub fn lookupInNamespace( | | |
| 3927 | mod: *Module, | | |
| 3928 | namespace: *Scope.Namespace, | | |
| 3929 | ident_name: []const u8, | | |
| 3930 | only_pub_usingnamespaces: bool, | | |
| 3931 | ) error{AnalysisFail}!?*Decl { | | |
| 3932 | const owner_decl = namespace.getDecl(); | | |
| 3933 | if (owner_decl.analysis == .file_failure) { | | |
| 3934 | return error.AnalysisFail; | | |
| 3935 | } | | |
| 3936 | | | |
| 3937 | // TODO the decl doing the looking up needs to create a decl dependency | | |
| 3938 | // TODO implement usingnamespace | | |
| 3939 | if (namespace.decls.get(ident_name)) |decl| { | | |
| 3940 | return decl; | | |
| 3941 | } | | |
| 3942 | return null; | | |
| 3943 | //// TODO handle decl collision with usingnamespace | | |
| 3944 | //// on each usingnamespace decl here. | | |
| 3945 | //{ | | |
| 3946 | // var it = namespace.usingnamespace_set.iterator(); | | |
| 3947 | // while (it.next()) |entry| { | | |
| 3948 | // const other_ns = entry.key; | | |
| 3949 | // const other_is_pub = entry.value; | | |
| 3950 | // if (only_pub_usingnamespaces and !other_is_pub) continue; | | |
| 3951 | // // TODO handle cycles | | |
| 3952 | // if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| { | | |
| 3953 | // return decl; | | |
| 3954 | // } | | |
| 3955 | // } | | |
| 3956 | //} | | |
| 3957 | } | | |
| 3958 | | | |
| 3959 | pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { | 3892 | pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { |
| 3960 | const int_payload = try arena.create(Type.Payload.Bits); | 3893 | const int_payload = try arena.create(Type.Payload.Bits); |
| 3961 | int_payload.* = .{ | 3894 | int_payload.* = .{ |
| ... | @@ -4922,3 +4855,50 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { | ... | @@ -4922,3 +4855,50 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
| 4922 | try mod.markOutdatedDecl(entry.key); | 4855 | try mod.markOutdatedDecl(entry.key); |
| 4923 | } | 4856 | } |
| 4924 | } | 4857 | } |
| | 4858 | |
| | 4859 | /// Called from `Compilation.update`, after everything is done, just before |
| | 4860 | /// reporting compile errors. In this function we emit exported symbol collision |
| | 4861 | /// errors and communicate exported symbols to the linker backend. |
| | 4862 | pub fn processExports(mod: *Module) !void { |
| | 4863 | const gpa = mod.gpa; |
| | 4864 | // Map symbol names to `Export` for name collision detection. |
| | 4865 | var symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}; |
| | 4866 | defer symbol_exports.deinit(gpa); |
| | 4867 | |
| | 4868 | for (mod.decl_exports.items()) |entry| { |
| | 4869 | const exported_decl = entry.key; |
| | 4870 | const exports = entry.value; |
| | 4871 | for (exports) |new_export| { |
| | 4872 | const gop = try symbol_exports.getOrPut(gpa, new_export.options.name); |
| | 4873 | if (gop.found_existing) { |
| | 4874 | new_export.status = .failed_retryable; |
| | 4875 | try mod.failed_exports.ensureUnusedCapacity(gpa, 1); |
| | 4876 | const src_loc = new_export.getSrcLoc(); |
| | 4877 | const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {s}", .{ |
| | 4878 | new_export.options.name, |
| | 4879 | }); |
| | 4880 | errdefer msg.destroy(gpa); |
| | 4881 | const other_export = gop.entry.value; |
| | 4882 | const other_src_loc = other_export.getSrcLoc(); |
| | 4883 | try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); |
| | 4884 | mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); |
| | 4885 | new_export.status = .failed; |
| | 4886 | } else { |
| | 4887 | gop.entry.value = new_export; |
| | 4888 | } |
| | 4889 | } |
| | 4890 | mod.comp.bin_file.updateDeclExports(mod, exported_decl, exports) catch |err| switch (err) { |
| | 4891 | error.OutOfMemory => return error.OutOfMemory, |
| | 4892 | else => { |
| | 4893 | const new_export = exports[0]; |
| | 4894 | new_export.status = .failed_retryable; |
| | 4895 | try mod.failed_exports.ensureUnusedCapacity(gpa, 1); |
| | 4896 | const src_loc = new_export.getSrcLoc(); |
| | 4897 | const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{ |
| | 4898 | @errorName(err), |
| | 4899 | }); |
| | 4900 | mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); |
| | 4901 | }, |
| | 4902 | }; |
| | 4903 | } |
| | 4904 | } |