Commit d0cd1c89da

Jacob Young <jacobly0@users.noreply.github.com>
2023-05-30 08:18:07
Sema: port lazy value usage to be InternPool aware
1 parent 61978c8
Changed files (1)
src/Sema.zig
@@ -1915,6 +1915,18 @@ fn resolveConstValue(
     return sema.failWithNeededComptime(block, src, reason);
 }
 
+/// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors.
+/// Lazy values are recursively resolved.
+fn resolveConstLazyValue(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    air_ref: Air.Inst.Ref,
+    reason: []const u8,
+) CompileError!Value {
+    return sema.resolveLazyValue(try sema.resolveConstValue(block, src, air_ref, reason));
+}
+
 /// Value Tag `variable` causes this function to return `null`.
 /// Value Tag `undef` causes this function to return a compile error.
 fn resolveDefinedValue(
@@ -1952,10 +1964,22 @@ fn resolveMaybeUndefVal(
     }
 }
 
+/// Value Tag `variable` causes this function to return `null`.
+/// Value Tag `undef` causes this function to return the Value.
+/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
+/// Lazy values are recursively resolved.
+fn resolveMaybeUndefLazyVal(
+    sema: *Sema,
+    inst: Air.Inst.Ref,
+) CompileError!?Value {
+    return try sema.resolveLazyValue((try sema.resolveMaybeUndefVal(inst)) orelse return null);
+}
+
 /// Value Tag `variable` results in `null`.
 /// Value Tag `undef` results in the Value.
 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
 /// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`.
+/// Lazy values are recursively resolved.
 fn resolveMaybeUndefValIntable(
     sema: *Sema,
     inst: Air.Inst.Ref,
@@ -1976,8 +2000,7 @@ fn resolveMaybeUndefValIntable(
             else => break,
         },
     };
-    try sema.resolveLazyValue(val);
-    return val;
+    return try sema.resolveLazyValue(val);
 }
 
 /// Returns all Value tags including `variable` and `undef`.
@@ -5257,8 +5280,7 @@ fn zirCompileLog(
 
         const arg = try sema.resolveInst(arg_ref);
         const arg_ty = sema.typeOf(arg);
-        if (try sema.resolveMaybeUndefVal(arg)) |val| {
-            try sema.resolveLazyValue(val);
+        if (try sema.resolveMaybeUndefLazyVal(arg)) |val| {
             try writer.print("@as({}, {})", .{
                 arg_ty.fmt(sema.mod), val.fmtValue(arg_ty, sema.mod),
             });
@@ -7266,15 +7288,14 @@ fn analyzeInlineCallArg(
                         // parameter or return type.
                         return error.GenericPoison;
                     },
-                    else => {
-                        // Needed so that lazy values do not trigger
-                        // assertion due to type not being resolved
-                        // when the hash function is called.
-                        try sema.resolveLazyValue(arg_val);
-                    },
+                    else => {},
                 }
-                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod);
-                memoized_arg_values[arg_i.*] = try arg_val.intern(param_ty.toType(), mod);
+                // Needed so that lazy values do not trigger
+                // assertion due to type not being resolved
+                // when the hash function is called.
+                const resolved_arg_val = try sema.resolveLazyValue(arg_val);
+                should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
+                memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(param_ty.toType(), mod);
             } else {
                 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
             }
@@ -7302,15 +7323,14 @@ fn analyzeInlineCallArg(
                         // parameter or return type.
                         return error.GenericPoison;
                     },
-                    else => {
-                        // Needed so that lazy values do not trigger
-                        // assertion due to type not being resolved
-                        // when the hash function is called.
-                        try sema.resolveLazyValue(arg_val);
-                    },
+                    else => {},
                 }
-                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod);
-                memoized_arg_values[arg_i.*] = try arg_val.intern(sema.typeOf(uncasted_arg), mod);
+                // Needed so that lazy values do not trigger
+                // assertion due to type not being resolved
+                // when the hash function is called.
+                const resolved_arg_val = try sema.resolveLazyValue(arg_val);
+                should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
+                memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(sema.typeOf(uncasted_arg), mod);
             } else {
                 if (zir_tags[inst] == .param_anytype_comptime) {
                     _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
@@ -7369,9 +7389,7 @@ fn analyzeGenericCallArg(
 }
 
 fn analyzeGenericCallArgVal(sema: *Sema, block: *Block, arg_src: LazySrcLoc, uncasted_arg: Air.Inst.Ref) !Value {
-    const arg_val = try sema.resolveValue(block, arg_src, uncasted_arg, "parameter is comptime");
-    try sema.resolveLazyValue(arg_val);
-    return arg_val;
+    return sema.resolveLazyValue(try sema.resolveValue(block, arg_src, uncasted_arg, "parameter is comptime"));
 }
 
 fn instantiateGenericCall(
@@ -7903,7 +7921,8 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
     for (tuple.types, tuple.values) |field_ty, field_val| {
         try sema.resolveTupleLazyValues(block, src, field_ty.toType());
         if (field_val == .none) continue;
-        try sema.resolveLazyValue(field_val.toValue());
+        // TODO: mutate in intern pool
+        _ = try sema.resolveLazyValue(field_val.toValue());
     }
 }
 
@@ -10104,8 +10123,9 @@ fn zirSwitchCapture(
 
     if (block.inline_case_capture != .none) {
         const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
+        const resolved_item_val = try sema.resolveLazyValue(item_val);
         if (operand_ty.zigTypeTag(mod) == .Union) {
-            const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
+            const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(resolved_item_val, sema.mod).?);
             const union_obj = mod.typeToUnion(operand_ty).?;
             const field_ty = union_obj.fields.values()[field_index].ty;
             if (try sema.resolveDefinedValue(block, sema.src, operand_ptr)) |union_val| {
@@ -10141,7 +10161,7 @@ fn zirSwitchCapture(
                 return block.addStructFieldVal(operand_ptr, field_index, field_ty);
             }
         } else if (is_ref) {
-            return sema.addConstantMaybeRef(block, operand_ty, item_val, true);
+            return sema.addConstantMaybeRef(block, operand_ty, resolved_item_val, true);
         } else {
             return block.inline_case_capture;
         }
@@ -10249,7 +10269,7 @@ fn zirSwitchCapture(
                 for (items) |item| {
                     const item_ref = try sema.resolveInst(item);
                     // Previous switch validation ensured this will succeed
-                    const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable;
+                    const item_val = sema.resolveConstLazyValue(block, .unneeded, item_ref, "") catch unreachable;
                     const name_ip = try mod.intern_pool.getOrPutString(gpa, item_val.getError(mod).?);
                     names.putAssumeCapacityNoClobber(name_ip, {});
                 }
@@ -10259,7 +10279,7 @@ fn zirSwitchCapture(
             } else {
                 const item_ref = try sema.resolveInst(items[0]);
                 // Previous switch validation ensured this will succeed
-                const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable;
+                const item_val = sema.resolveConstLazyValue(block, .unneeded, item_ref, "") catch unreachable;
 
                 const item_ty = try mod.singleErrorSetType(item_val.getError(mod).?);
                 return sema.bitCast(block, item_ty, operand, operand_src, null);
@@ -10993,6 +11013,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     defer merges.deinit(gpa);
 
     if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {
+        const resolved_operand_val = try sema.resolveLazyValue(operand_val);
         var extra_index: usize = special.end;
         {
             var scalar_i: usize = 0;
@@ -11007,8 +11028,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
                 const item = try sema.resolveInst(item_ref);
                 // Validation above ensured these will succeed.
-                const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
-                if (operand_val.eql(item_val, operand_ty, mod)) {
+                const item_val = sema.resolveConstLazyValue(&child_block, .unneeded, item, "") catch unreachable;
+                if (resolved_operand_val.eql(item_val, operand_ty, mod)) {
                     if (is_inline) child_block.inline_case_capture = operand;
 
                     if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
@@ -11033,8 +11054,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 for (items) |item_ref| {
                     const item = try sema.resolveInst(item_ref);
                     // Validation above ensured these will succeed.
-                    const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
-                    if (operand_val.eql(item_val, operand_ty, mod)) {
+                    const item_val = sema.resolveConstLazyValue(&child_block, .unneeded, item, "") catch unreachable;
+                    if (resolved_operand_val.eql(item_val, operand_ty, mod)) {
                         if (is_inline) child_block.inline_case_capture = operand;
 
                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
@@ -11052,8 +11073,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     // Validation above ensured these will succeed.
                     const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first, "") catch unreachable;
                     const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last, "") catch unreachable;
-                    if ((try sema.compareAll(operand_val, .gte, first_tv.val, operand_ty)) and
-                        (try sema.compareAll(operand_val, .lte, last_tv.val, operand_ty)))
+                    if ((try sema.compareAll(resolved_operand_val, .gte, first_tv.val, operand_ty)) and
+                        (try sema.compareAll(resolved_operand_val, .lte, last_tv.val, operand_ty)))
                     {
                         if (is_inline) child_block.inline_case_capture = operand;
                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
@@ -11135,7 +11156,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         // `item` is already guaranteed to be constant known.
 
         const analyze_body = if (union_originally) blk: {
-            const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
+            const item_val = sema.resolveConstLazyValue(block, .unneeded, item, "") catch unreachable;
             const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
             break :blk field_ty.zigTypeTag(mod) != .NoReturn;
         } else true;
@@ -11683,8 +11704,7 @@ fn resolveSwitchItemVal(
     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
     // Only if we know for sure we need to report a compile error do we resolve the
     // full source locations.
-    if (sema.resolveConstValue(block, .unneeded, item, "")) |val| {
-        try sema.resolveLazyValue(val);
+    if (sema.resolveConstLazyValue(block, .unneeded, item, "")) |val| {
         return val.toIntern();
     } else |err| switch (err) {
         error.NeededSourceLocation => {
@@ -20634,9 +20654,8 @@ fn zirBitCount(
             }
         },
         .Int => {
-            if (try sema.resolveMaybeUndefVal(operand)) |val| {
+            if (try sema.resolveMaybeUndefLazyVal(operand)) |val| {
                 if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty);
-                try sema.resolveLazyValue(val);
                 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod));
             } else {
                 try sema.requireRuntimeBlock(block, src, operand_src);
@@ -22311,18 +22330,18 @@ fn analyzeMinMax(
                 continue;
             }
 
-            try sema.resolveLazyValue(cur_val);
-            try sema.resolveLazyValue(operand_val);
+            const resolved_cur_val = try sema.resolveLazyValue(cur_val);
+            const resolved_operand_val = try sema.resolveLazyValue(operand_val);
 
             const vec_len = simd_op.len orelse {
-                const result_val = opFunc(cur_val, operand_val, mod);
+                const result_val = opFunc(resolved_cur_val, resolved_operand_val, mod);
                 cur_minmax = try sema.addConstant(simd_op.result_ty, result_val);
                 continue;
             };
             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
             for (elems, 0..) |*elem, i| {
-                const lhs_elem_val = try cur_val.elemValue(mod, i);
-                const rhs_elem_val = try operand_val.elemValue(mod, i);
+                const lhs_elem_val = try resolved_cur_val.elemValue(mod, i);
+                const rhs_elem_val = try resolved_operand_val.elemValue(mod, i);
                 elem.* = try opFunc(lhs_elem_val, rhs_elem_val, mod).intern(simd_op.scalar_ty, mod);
             }
             cur_minmax = try sema.addConstant(simd_op.result_ty, (try mod.intern(.{ .aggregate = .{
@@ -30360,13 +30379,11 @@ fn cmpNumeric(
             if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
                 // Compare ints: const vs. undefined (or vice versa)
                 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod) and rhs_val.isUndef(mod)) {
-                    try sema.resolveLazyValue(lhs_val);
-                    if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
+                    if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
                     }
                 } else if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod) and lhs_val.isUndef(mod)) {
-                    try sema.resolveLazyValue(rhs_val);
-                    if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
+                    if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
                     }
                 }
@@ -30389,19 +30406,17 @@ fn cmpNumeric(
             } else {
                 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) {
                     // Compare ints: const vs. var
-                    try sema.resolveLazyValue(lhs_val);
-                    if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
+                    if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
                     }
                 }
                 break :src rhs_src;
             }
         } else {
-            if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
+            if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
                 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) {
                     // Compare ints: var vs. const
-                    try sema.resolveLazyValue(rhs_val);
-                    if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
+                    if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
                         return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
                     }
                 }
@@ -30465,8 +30480,7 @@ fn cmpNumeric(
     var dest_float_type: ?Type = null;
 
     var lhs_bits: usize = undefined;
-    if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| {
-        try sema.resolveLazyValue(lhs_val);
+    if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| {
         if (lhs_val.isUndef(mod))
             return sema.addConstUndef(Type.bool);
         if (lhs_val.isNan(mod)) switch (op) {
@@ -30524,8 +30538,7 @@ fn cmpNumeric(
     }
 
     var rhs_bits: usize = undefined;
-    if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| {
-        try sema.resolveLazyValue(rhs_val);
+    if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| {
         if (rhs_val.isUndef(mod))
             return sema.addConstUndef(Type.bool);
         if (rhs_val.isNan(mod)) switch (op) {
@@ -31467,32 +31480,133 @@ pub fn resolveFnTypes(sema: *Sema, fn_info: InternPool.Key.FuncType) CompileErro
 
 /// Make it so that calling hash() and eql() on `val` will not assert due
 /// to a type not having its layout resolved.
-fn resolveLazyValue(sema: *Sema, val: Value) CompileError!void {
-    switch (sema.mod.intern_pool.indexToKey(val.toIntern())) {
+fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
+    const mod = sema.mod;
+    switch (mod.intern_pool.indexToKey(val.toIntern())) {
         .int => |int| switch (int.storage) {
-            .u64, .i64, .big_int => {},
-            .lazy_align, .lazy_size => |lazy_ty| try sema.resolveTypeLayout(lazy_ty.toType()),
+            .u64, .i64, .big_int => return val,
+            .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{
+                .ty = int.ty,
+                .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? },
+            } })).toValue(),
         },
         .ptr => |ptr| {
+            const resolved_len = switch (ptr.len) {
+                .none => .none,
+                else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(),
+            };
             switch (ptr.addr) {
-                .decl, .mut_decl => {},
-                .int => |int| try sema.resolveLazyValue(int.toValue()),
-                .eu_payload, .opt_payload => |base| try sema.resolveLazyValue(base.toValue()),
-                .comptime_field => |comptime_field| try sema.resolveLazyValue(comptime_field.toValue()),
-                .elem, .field => |base_index| try sema.resolveLazyValue(base_index.base.toValue()),
+                .decl, .mut_decl => return if (resolved_len == ptr.len)
+                    val
+                else
+                    (try mod.intern(.{ .ptr = .{
+                        .ty = ptr.ty,
+                        .addr = switch (ptr.addr) {
+                            .decl => |decl| .{ .decl = decl },
+                            .mut_decl => |mut_decl| .{ .mut_decl = mut_decl },
+                            else => unreachable,
+                        },
+                        .len = resolved_len,
+                    } })).toValue(),
+                .comptime_field => |field_val| {
+                    const resolved_field_val =
+                        (try sema.resolveLazyValue(field_val.toValue())).toIntern();
+                    return if (resolved_field_val == field_val and resolved_len == ptr.len)
+                        val
+                    else
+                        (try mod.intern(.{ .ptr = .{
+                            .ty = ptr.ty,
+                            .addr = .{ .comptime_field = resolved_field_val },
+                            .len = resolved_len,
+                        } })).toValue();
+                },
+                .int => |int| {
+                    const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern();
+                    return if (resolved_int == int and resolved_len == ptr.len)
+                        val
+                    else
+                        (try mod.intern(.{ .ptr = .{
+                            .ty = ptr.ty,
+                            .addr = .{ .int = resolved_int },
+                            .len = resolved_len,
+                        } })).toValue();
+                },
+                .eu_payload, .opt_payload => |base| {
+                    const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern();
+                    return if (resolved_base == base and resolved_len == ptr.len)
+                        val
+                    else
+                        (try mod.intern(.{ .ptr = .{
+                            .ty = ptr.ty,
+                            .addr = switch (ptr.addr) {
+                                .eu_payload => .{ .eu_payload = resolved_base },
+                                .opt_payload => .{ .opt_payload = resolved_base },
+                                else => unreachable,
+                            },
+                            .len = ptr.len,
+                        } })).toValue();
+                },
+                .elem, .field => |base_index| {
+                    const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern();
+                    return if (resolved_base == base_index.base and resolved_len == ptr.len)
+                        val
+                    else
+                        (try mod.intern(.{ .ptr = .{
+                            .ty = ptr.ty,
+                            .addr = switch (ptr.addr) {
+                                .elem => .{ .elem = .{
+                                    .base = resolved_base,
+                                    .index = base_index.index,
+                                } },
+                                .field => .{ .field = .{
+                                    .base = resolved_base,
+                                    .index = base_index.index,
+                                } },
+                                else => unreachable,
+                            },
+                            .len = ptr.len,
+                        } })).toValue();
+                },
             }
-            if (ptr.len != .none) try sema.resolveLazyValue(ptr.len.toValue());
         },
         .aggregate => |aggregate| switch (aggregate.storage) {
-            .bytes => {},
-            .elems => |elems| for (elems) |elem| try sema.resolveLazyValue(elem.toValue()),
-            .repeated_elem => |elem| try sema.resolveLazyValue(elem.toValue()),
+            .bytes => return val,
+            .elems => |elems| {
+                var resolved_elems: []InternPool.Index = &.{};
+                for (elems, 0..) |elem, i| {
+                    const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
+                    if (resolved_elems.len == 0 and resolved_elem != elem) {
+                        resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len);
+                        @memcpy(resolved_elems[0..i], elems[0..i]);
+                    }
+                    if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem;
+                }
+                return if (resolved_elems.len == 0) val else (try mod.intern(.{ .aggregate = .{
+                    .ty = aggregate.ty,
+                    .storage = .{ .elems = resolved_elems },
+                } })).toValue();
+            },
+            .repeated_elem => |elem| {
+                const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern();
+                return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{
+                    .ty = aggregate.ty,
+                    .storage = .{ .repeated_elem = resolved_elem },
+                } })).toValue();
+            },
         },
         .un => |un| {
-            try sema.resolveLazyValue(un.tag.toValue());
-            try sema.resolveLazyValue(un.val.toValue());
+            const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern();
+            const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern();
+            return if (resolved_tag == un.tag and resolved_val == un.val)
+                val
+            else
+                (try mod.intern(.{ .un = .{
+                    .ty = un.ty,
+                    .tag = resolved_tag,
+                    .val = resolved_val,
+                } })).toValue();
         },
-        else => {},
+        else => return val,
     }
 }