| ... | ... | @@ -41,14 +41,11 @@ root_pkg: *Package, |
| 41 | 41 | global_zir_cache: Compilation.Directory, |
| 42 | 42 | /// Used by AstGen worker to load and store ZIR cache. |
| 43 | 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 |
| 45 | | /// 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. |
| 47 | | /// The slice is guaranteed to not be empty. |
| 44 | /// It's rare for a decl to be exported, so we save memory by having a sparse |
| 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 |
| 47 | /// is owned by this table. The slice is guaranteed to not be empty. |
| 48 | 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 | 49 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl |
| 53 | 50 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that |
| 54 | 51 | /// is performing the export of another Decl. |
| ... | ... | @@ -144,6 +141,14 @@ pub const Export = struct { |
| 144 | 141 | failed_retryable, |
| 145 | 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 | 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 | 2189 | } |
| 2185 | 2190 | mod.export_owners.deinit(gpa); |
| 2186 | 2191 | |
| 2187 | | mod.symbol_exports.deinit(gpa); |
| 2188 | | |
| 2189 | 2192 | var it = mod.global_error_set.iterator(); |
| 2190 | 2193 | while (it.next()) |entry| { |
| 2191 | 2194 | gpa.free(entry.key); |
| ... | ... | @@ -2779,7 +2782,10 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2779 | 2782 | for (decl.dependencies.items()) |entry| { |
| 2780 | 2783 | const dep = entry.key; |
| 2781 | 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 | 2789 | // We don't perform a deletion here, because this Decl or another one |
| 2784 | 2790 | // may end up referencing it before the update is complete. |
| 2785 | 2791 | dep.deletion_flag = true; |
| ... | ... | @@ -2795,11 +2801,18 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2795 | 2801 | }; |
| 2796 | 2802 | |
| 2797 | 2803 | const type_changed = mod.semaDecl(decl) catch |err| switch (err) { |
| 2798 | | error.OutOfMemory => return error.OutOfMemory, |
| 2799 | | error.AnalysisFail => return error.AnalysisFail, |
| 2804 | 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 | 2813 | else => { |
| 2801 | 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 | 2816 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 2804 | 2817 | mod.gpa, |
| 2805 | 2818 | decl.srcLoc(), |
| ... | ... | @@ -2818,7 +2831,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2818 | 2831 | const dep = entry.key; |
| 2819 | 2832 | switch (dep.analysis) { |
| 2820 | 2833 | .unreferenced => unreachable, |
| 2821 | | .in_progress => unreachable, |
| 2834 | .in_progress => continue, // already doing analysis, ok |
| 2822 | 2835 | .outdated => continue, // already queued for update |
| 2823 | 2836 | |
| 2824 | 2837 | .file_failure, |
| ... | ... | @@ -3115,8 +3128,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3115 | 3128 | |
| 3116 | 3129 | /// Returns the depender's index of the dependee. |
| 3117 | 3130 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { |
| 3118 | | try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.count() + 1); |
| 3119 | | try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.count() + 1); |
| 3131 | if (depender == dependee) return; |
| 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 | 3140 | if (dependee.deletion_flag) { |
| 3122 | 3141 | dependee.deletion_flag = false; |
| ... | ... | @@ -3513,7 +3532,6 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { |
| 3513 | 3532 | if (mod.failed_exports.swapRemove(exp)) |entry| { |
| 3514 | 3533 | entry.value.destroy(mod.gpa); |
| 3515 | 3534 | } |
| 3516 | | _ = mod.symbol_exports.swapRemove(exp.options.name); |
| 3517 | 3535 | mod.gpa.free(exp.options.name); |
| 3518 | 3536 | mod.gpa.destroy(exp); |
| 3519 | 3537 | } |
| ... | ... | @@ -3726,38 +3744,6 @@ pub fn analyzeExport( |
| 3726 | 3744 | de_gop.entry.value = try mod.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); |
| 3727 | 3745 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; |
| 3728 | 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 | 3748 | pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst { |
| 3763 | 3749 | const const_inst = try arena.create(ir.Inst.Constant); |
| ... | ... | @@ -3903,59 +3889,6 @@ pub fn getNextAnonNameIndex(mod: *Module) usize { |
| 3903 | 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 | 3892 | pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { |
| 3960 | 3893 | const int_payload = try arena.create(Type.Payload.Bits); |
| 3961 | 3894 | int_payload.* = .{ |
| ... | ... | @@ -4922,3 +4855,50 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
| 4922 | 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 | } |