Commit a7f3c2eab4

Andrew Kelley <andrew@ziglang.org>
2023-07-24 02:19:50
InternPool: fix coerced func hash/eql same as uncoerced
Since the same Key.Func data structure is used for coerced function bodies as well as uncoerced function bodies, there is danger of them being hashed and equality-checked as the same. When that happens, the type of a function body value might be wrong, causing lots of problems. In this instance, it causes an assertion failure. This commit fixes it by introducing an `uncoerced_ty` field which is different than `ty` in the case of `func_coerced` and is used to differentiate when doing hashing and equality checking. I have a new behavior test to cover this bug, but it revealed *another* bug at the same time, so I will fix it in the next commit and and add the new test therein.
1 parent 7dcbabe
Changed files (1)
src/InternPool.zig
@@ -594,6 +594,13 @@ pub const Key = union(enum) {
         /// In the case of a generic function, this type will potentially have fewer parameters
         /// than the generic owner's type, because the comptime parameters will be deleted.
         ty: Index,
+        /// If this is a function body that has been coerced to a different type, for example
+        /// ```
+        /// fn f2() !void {}
+        /// const f: fn()anyerror!void = f2;
+        /// ```
+        /// then it contains the original type of the function body.
+        uncoerced_ty: Index,
         /// Index into extra array of the `FuncAnalysis` corresponding to this function.
         /// Used for mutating that data.
         analysis_extra_index: u32,
@@ -990,11 +997,15 @@ pub const Key = union(enum) {
                 // otherwise we would get false negatives for interning generic
                 // function instances which have inferred error sets.
 
-                if (func.generic_owner == .none and func.resolved_error_set_extra_index == 0)
-                    return Hash.hash(seed, asBytes(&func.owner_decl) ++ asBytes(&func.ty));
+                if (func.generic_owner == .none and func.resolved_error_set_extra_index == 0) {
+                    const bytes = asBytes(&func.owner_decl) ++ asBytes(&func.ty) ++
+                        [1]u8{@intFromBool(func.uncoerced_ty == func.ty)};
+                    return Hash.hash(seed, bytes);
+                }
 
                 var hasher = Hash.init(seed);
                 std.hash.autoHash(&hasher, func.generic_owner);
+                std.hash.autoHash(&hasher, func.uncoerced_ty == func.ty);
                 for (func.comptime_args.get(ip)) |arg| std.hash.autoHash(&hasher, arg);
                 if (func.resolved_error_set_extra_index == 0) {
                     std.hash.autoHash(&hasher, func.ty);
@@ -1122,6 +1133,12 @@ pub const Key = union(enum) {
                     )) return false;
                 }
 
+                if ((a_info.ty == a_info.uncoerced_ty) !=
+                    (b_info.ty == b_info.uncoerced_ty))
+                {
+                    return false;
+                }
+
                 if (a_info.ty == b_info.ty)
                     return true;
 
@@ -3371,6 +3388,7 @@ fn extraFuncDecl(ip: *const InternPool, extra_index: u32) Key.Func {
     const func_decl = ip.extraDataTrail(P, extra_index);
     return .{
         .ty = func_decl.data.ty,
+        .uncoerced_ty = func_decl.data.ty,
         .analysis_extra_index = extra_index + std.meta.fieldIndex(P, "analysis").?,
         .zir_body_inst_extra_index = extra_index + std.meta.fieldIndex(P, "zir_body_inst").?,
         .resolved_error_set_extra_index = if (func_decl.data.analysis.inferred_error_set) func_decl.end else 0,
@@ -3392,6 +3410,7 @@ fn extraFuncInstance(ip: *const InternPool, extra_index: u32) Key.Func {
     const func_decl = ip.funcDeclInfo(fi.data.generic_owner);
     return .{
         .ty = fi.data.ty,
+        .uncoerced_ty = fi.data.ty,
         .analysis_extra_index = extra_index + std.meta.fieldIndex(P, "analysis").?,
         .zir_body_inst_extra_index = func_decl.zir_body_inst_extra_index,
         .resolved_error_set_extra_index = if (fi.data.analysis.inferred_error_set) fi.end else 0,