| author | |
| committer | |
| log | 60d8c4739de14823c407245f01c9b7483f3b6e7f |
| tree | 8321a331d269b3ce4af0fdd88e99a6421f2a3b5d |
| parent | 593130ce0a4b06185fcb4806f8330857a1da9f92 |
This commit adds a new optional argument to several Value methods which
provides the ability to resolve types if it comes to it. This prevents
having duplicated logic inside both Sema and Value.
With this commit, the "struct contains slice of itself" test is passing
by exploiting the new lazy_align Value Tag.5 files changed, 167 insertions(+), 54 deletions(-)
src/Module.zig+6| ... | @@ -177,6 +177,12 @@ const MonomorphedFuncsContext = struct { | ... | @@ -177,6 +177,12 @@ const MonomorphedFuncsContext = struct { |
| 177 | } | 177 | } |
| 178 | }; | 178 | }; |
| 179 | 179 | ||
| 180 | pub const WipAnalysis = struct { | ||
| 181 | sema: *Sema, | ||
| 182 | block: *Sema.Block, | ||
| 183 | src: Module.LazySrcLoc, | ||
| 184 | }; | ||
| 185 | |||
| 180 | pub const MemoizedCallSet = std.HashMapUnmanaged( | 186 | pub const MemoizedCallSet = std.HashMapUnmanaged( |
| 181 | MemoizedCall.Key, | 187 | MemoizedCall.Key, |
| 182 | MemoizedCall.Result, | 188 | MemoizedCall.Result, |
src/Sema.zig+32-16| ... | @@ -1591,7 +1591,7 @@ fn resolveInt( | ... | @@ -1591,7 +1591,7 @@ fn resolveInt( |
| 1591 | const coerced = try sema.coerce(block, dest_ty, air_inst, src); | 1591 | const coerced = try sema.coerce(block, dest_ty, air_inst, src); |
| 1592 | const val = try sema.resolveConstValue(block, src, coerced); | 1592 | const val = try sema.resolveConstValue(block, src, coerced); |
| 1593 | const target = sema.mod.getTarget(); | 1593 | const target = sema.mod.getTarget(); |
| 1594 | return val.toUnsignedInt(target); | 1594 | return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; |
| 1595 | } | 1595 | } |
| 1596 | 1596 | ||
| 1597 | // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for | 1597 | // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for |
| ... | @@ -9926,7 +9926,7 @@ fn analyzePtrArithmetic( | ... | @@ -9926,7 +9926,7 @@ fn analyzePtrArithmetic( |
| 9926 | const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); | 9926 | const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); |
| 9927 | // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons | 9927 | // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons |
| 9928 | if (offset_int == 0) return ptr; | 9928 | if (offset_int == 0) return ptr; |
| 9929 | if (ptr_val.getUnsignedInt(target)) |addr| { | 9929 | if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| { |
| 9930 | const ptr_child_ty = ptr_ty.childType(); | 9930 | const ptr_child_ty = ptr_ty.childType(); |
| 9931 | const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) | 9931 | const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) |
| 9932 | ptr_child_ty.childType() | 9932 | ptr_child_ty.childType() |
| ... | @@ -11863,6 +11863,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -11863,6 +11863,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 11863 | const elem_ty_src: LazySrcLoc = .unneeded; | 11863 | const elem_ty_src: LazySrcLoc = .unneeded; |
| 11864 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; | 11864 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; |
| 11865 | const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); | 11865 | const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); |
| 11866 | const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); | ||
| 11867 | const target = sema.mod.getTarget(); | ||
| 11866 | 11868 | ||
| 11867 | var extra_i = extra.end; | 11869 | var extra_i = extra.end; |
| 11868 | 11870 | ||
| ... | @@ -11872,10 +11874,19 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -11872,10 +11874,19 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 11872 | break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; | 11874 | break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; |
| 11873 | } else null; | 11875 | } else null; |
| 11874 | 11876 | ||
| 11875 | const abi_align = if (inst_data.flags.has_align) blk: { | 11877 | const abi_align: u32 = if (inst_data.flags.has_align) blk: { |
| 11876 | const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); | 11878 | const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); |
| 11877 | extra_i += 1; | 11879 | extra_i += 1; |
| 11878 | const abi_align = try sema.resolveInt(block, .unneeded, ref, Type.u32); | 11880 | const coerced = try sema.coerce(block, Type.u32, sema.resolveInst(ref), src); |
| 11881 | const val = try sema.resolveConstValue(block, src, coerced); | ||
| 11882 | // Check if this happens to be the lazy alignment of our element type, in | ||
| 11883 | // which case we can make this 0 without resolving it. | ||
| 11884 | if (val.castTag(.lazy_align)) |payload| { | ||
| 11885 | if (payload.data.eql(unresolved_elem_ty, target)) { | ||
| 11886 | break :blk 0; | ||
| 11887 | } | ||
| 11888 | } | ||
| 11889 | const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; | ||
| 11879 | break :blk @intCast(u32, abi_align); | 11890 | break :blk @intCast(u32, abi_align); |
| 11880 | } else 0; | 11891 | } else 0; |
| 11881 | 11892 | ||
| ... | @@ -11903,7 +11914,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -11903,7 +11914,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 11903 | return sema.fail(block, src, "bit offset starts after end of host integer", .{}); | 11914 | return sema.fail(block, src, "bit offset starts after end of host integer", .{}); |
| 11904 | } | 11915 | } |
| 11905 | 11916 | ||
| 11906 | const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); | ||
| 11907 | const elem_ty = if (abi_align == 0) | 11917 | const elem_ty = if (abi_align == 0) |
| 11908 | unresolved_elem_ty | 11918 | unresolved_elem_ty |
| 11909 | else t: { | 11919 | else t: { |
| ... | @@ -11911,7 +11921,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -11911,7 +11921,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 11911 | try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); | 11921 | try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); |
| 11912 | break :t elem_ty; | 11922 | break :t elem_ty; |
| 11913 | }; | 11923 | }; |
| 11914 | const target = sema.mod.getTarget(); | ||
| 11915 | const ty = try Type.ptr(sema.arena, target, .{ | 11924 | const ty = try Type.ptr(sema.arena, target, .{ |
| 11916 | .pointee_type = elem_ty, | 11925 | .pointee_type = elem_ty, |
| 11917 | .sentinel = sentinel, | 11926 | .sentinel = sentinel, |
| ... | @@ -18390,15 +18399,15 @@ fn storePtrVal( | ... | @@ -18390,15 +18399,15 @@ fn storePtrVal( |
| 18390 | operand_val: Value, | 18399 | operand_val: Value, |
| 18391 | operand_ty: Type, | 18400 | operand_ty: Type, |
| 18392 | ) !void { | 18401 | ) !void { |
| 18393 | var kit = try beginComptimePtrMutation(sema, block, src, ptr_val); | 18402 | var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val); |
| 18394 | try sema.checkComptimeVarStore(block, src, kit.decl_ref_mut); | 18403 | try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut); |
| 18395 | 18404 | ||
| 18396 | const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, kit.ty, 0); | 18405 | const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); |
| 18397 | 18406 | ||
| 18398 | const arena = kit.beginArena(sema.gpa); | 18407 | const arena = mut_kit.beginArena(sema.gpa); |
| 18399 | defer kit.finishArena(); | 18408 | defer mut_kit.finishArena(); |
| 18400 | 18409 | ||
| 18401 | kit.val.* = try bitcasted_val.copy(arena); | 18410 | mut_kit.val.* = try bitcasted_val.copy(arena); |
| 18402 | } | 18411 | } |
| 18403 | 18412 | ||
| 18404 | const ComptimePtrMutationKit = struct { | 18413 | const ComptimePtrMutationKit = struct { |
| ... | @@ -19891,7 +19900,7 @@ fn cmpNumeric( | ... | @@ -19891,7 +19900,7 @@ fn cmpNumeric( |
| 19891 | return Air.Inst.Ref.bool_false; | 19900 | return Air.Inst.Ref.bool_false; |
| 19892 | } | 19901 | } |
| 19893 | } | 19902 | } |
| 19894 | if (Value.compareHetero(lhs_val, op, rhs_val, target)) { | 19903 | if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, target, sema.kit(block, src))) { |
| 19895 | return Air.Inst.Ref.bool_true; | 19904 | return Air.Inst.Ref.bool_true; |
| 19896 | } else { | 19905 | } else { |
| 19897 | return Air.Inst.Ref.bool_false; | 19906 | return Air.Inst.Ref.bool_false; |
| ... | @@ -20758,7 +20767,7 @@ pub fn resolveFnTypes( | ... | @@ -20758,7 +20767,7 @@ pub fn resolveFnTypes( |
| 20758 | } | 20767 | } |
| 20759 | } | 20768 | } |
| 20760 | 20769 | ||
| 20761 | fn resolveTypeLayout( | 20770 | pub fn resolveTypeLayout( |
| 20762 | sema: *Sema, | 20771 | sema: *Sema, |
| 20763 | block: *Block, | 20772 | block: *Block, |
| 20764 | src: LazySrcLoc, | 20773 | src: LazySrcLoc, |
| ... | @@ -20929,7 +20938,7 @@ fn resolveUnionFully( | ... | @@ -20929,7 +20938,7 @@ fn resolveUnionFully( |
| 20929 | union_obj.status = .fully_resolved; | 20938 | union_obj.status = .fully_resolved; |
| 20930 | } | 20939 | } |
| 20931 | 20940 | ||
| 20932 | fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { | 20941 | pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { |
| 20933 | switch (ty.tag()) { | 20942 | switch (ty.tag()) { |
| 20934 | .@"struct" => { | 20943 | .@"struct" => { |
| 20935 | const struct_obj = ty.castTag(.@"struct").?.data; | 20944 | const struct_obj = ty.castTag(.@"struct").?.data; |
| ... | @@ -22209,7 +22218,9 @@ fn typePtrOrOptionalPtrTy( | ... | @@ -22209,7 +22218,9 @@ fn typePtrOrOptionalPtrTy( |
| 22209 | /// This function returns false negatives when structs and unions are having their | 22218 | /// This function returns false negatives when structs and unions are having their |
| 22210 | /// field types resolved. | 22219 | /// field types resolved. |
| 22211 | /// TODO assert the return value matches `ty.comptimeOnly` | 22220 | /// TODO assert the return value matches `ty.comptimeOnly` |
| 22212 | fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { | 22221 | /// TODO merge these implementations together with the "advanced"/sema_kit pattern seen |
| 22222 | /// elsewhere in value.zig | ||
| 22223 | pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { | ||
| 22213 | return switch (ty.tag()) { | 22224 | return switch (ty.tag()) { |
| 22214 | .u1, | 22225 | .u1, |
| 22215 | .u8, | 22226 | .u8, |
| ... | @@ -22415,6 +22426,7 @@ fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { | ... | @@ -22415,6 +22426,7 @@ fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { |
| 22415 | return ty.abiSize(target); | 22426 | return ty.abiSize(target); |
| 22416 | } | 22427 | } |
| 22417 | 22428 | ||
| 22429 | /// TODO merge with Type.abiAlignmentAdvanced | ||
| 22418 | fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { | 22430 | fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { |
| 22419 | try sema.resolveTypeLayout(block, src, ty); | 22431 | try sema.resolveTypeLayout(block, src, ty); |
| 22420 | const target = sema.mod.getTarget(); | 22432 | const target = sema.mod.getTarget(); |
| ... | @@ -22498,3 +22510,7 @@ fn anonStructFieldIndex( | ... | @@ -22498,3 +22510,7 @@ fn anonStructFieldIndex( |
| 22498 | struct_ty.fmt(target), field_name, | 22510 | struct_ty.fmt(target), field_name, |
| 22499 | }); | 22511 | }); |
| 22500 | } | 22512 | } |
| 22513 | |||
| 22514 | fn kit(sema: *Sema, block: *Block, src: LazySrcLoc) Module.WipAnalysis { | ||
| 22515 | return .{ .sema = sema, .block = block, .src = src }; | ||
| 22516 | } |
src/type.zig+65-30| ... | @@ -1483,20 +1483,29 @@ pub const Type = extern union { | ... | @@ -1483,20 +1483,29 @@ pub const Type = extern union { |
| 1483 | @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); | 1483 | @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); |
| 1484 | } | 1484 | } |
| 1485 | 1485 | ||
| 1486 | pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) { | 1486 | pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(format2) { |
| 1487 | var ty_payload: Value.Payload.Ty = .{ | ||
| 1488 | .base = .{ .tag = .ty }, | ||
| 1489 | .data = ty, | ||
| 1490 | }; | ||
| 1491 | return .{ .data = .{ | 1487 | return .{ .data = .{ |
| 1492 | .tv = .{ | 1488 | .ty = ty, |
| 1493 | .ty = Type.type, | ||
| 1494 | .val = Value.initPayload(&ty_payload.base), | ||
| 1495 | }, | ||
| 1496 | .target = target, | 1489 | .target = target, |
| 1497 | } }; | 1490 | } }; |
| 1498 | } | 1491 | } |
| 1499 | 1492 | ||
| 1493 | const FormatContext = struct { | ||
| 1494 | ty: Type, | ||
| 1495 | target: Target, | ||
| 1496 | }; | ||
| 1497 | |||
| 1498 | fn format2( | ||
| 1499 | ctx: FormatContext, | ||
| 1500 | comptime unused_format_string: []const u8, | ||
| 1501 | options: std.fmt.FormatOptions, | ||
| 1502 | writer: anytype, | ||
| 1503 | ) !void { | ||
| 1504 | comptime assert(unused_format_string.len == 0); | ||
| 1505 | _ = options; | ||
| 1506 | return print(ctx.ty, writer, ctx.target); | ||
| 1507 | } | ||
| 1508 | |||
| 1500 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { | 1509 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { |
| 1501 | return .{ .data = ty }; | 1510 | return .{ .data = ty }; |
| 1502 | } | 1511 | } |
| ... | @@ -2241,8 +2250,12 @@ pub const Type = extern union { | ... | @@ -2241,8 +2250,12 @@ pub const Type = extern union { |
| 2241 | /// * the type has only one possible value, making its ABI size 0. | 2250 | /// * the type has only one possible value, making its ABI size 0. |
| 2242 | /// When `ignore_comptime_only` is true, then types that are comptime only | 2251 | /// When `ignore_comptime_only` is true, then types that are comptime only |
| 2243 | /// may return false positives. | 2252 | /// may return false positives. |
| 2244 | pub fn hasRuntimeBitsAdvanced(ty: Type, ignore_comptime_only: bool) bool { | 2253 | pub fn hasRuntimeBitsAdvanced( |
| 2245 | return switch (ty.tag()) { | 2254 | ty: Type, |
| 2255 | ignore_comptime_only: bool, | ||
| 2256 | sema_kit: ?Module.WipAnalysis, | ||
| 2257 | ) Module.CompileError!bool { | ||
| 2258 | switch (ty.tag()) { | ||
| 2246 | .u1, | 2259 | .u1, |
| 2247 | .u8, | 2260 | .u8, |
| 2248 | .i8, | 2261 | .i8, |
| ... | @@ -2296,7 +2309,7 @@ pub const Type = extern union { | ... | @@ -2296,7 +2309,7 @@ pub const Type = extern union { |
| 2296 | .@"anyframe", | 2309 | .@"anyframe", |
| 2297 | .anyopaque, | 2310 | .anyopaque, |
| 2298 | .@"opaque", | 2311 | .@"opaque", |
| 2299 | => true, | 2312 | => return true, |
| 2300 | 2313 | ||
| 2301 | // These are false because they are comptime-only types. | 2314 | // These are false because they are comptime-only types. |
| 2302 | .single_const_pointer_to_comptime_int, | 2315 | .single_const_pointer_to_comptime_int, |
| ... | @@ -2320,7 +2333,7 @@ pub const Type = extern union { | ... | @@ -2320,7 +2333,7 @@ pub const Type = extern union { |
| 2320 | .fn_void_no_args, | 2333 | .fn_void_no_args, |
| 2321 | .fn_naked_noreturn_no_args, | 2334 | .fn_naked_noreturn_no_args, |
| 2322 | .fn_ccc_void_no_args, | 2335 | .fn_ccc_void_no_args, |
| 2323 | => false, | 2336 | => return false, |
| 2324 | 2337 | ||
| 2325 | // These types have more than one possible value, so the result is the same as | 2338 | // These types have more than one possible value, so the result is the same as |
| 2326 | // asking whether they are comptime-only types. | 2339 | // asking whether they are comptime-only types. |
| ... | @@ -2337,20 +2350,34 @@ pub const Type = extern union { | ... | @@ -2337,20 +2350,34 @@ pub const Type = extern union { |
| 2337 | .const_slice, | 2350 | .const_slice, |
| 2338 | .mut_slice, | 2351 | .mut_slice, |
| 2339 | .pointer, | 2352 | .pointer, |
| 2340 | => if (ignore_comptime_only) true else !comptimeOnly(ty), | 2353 | => { |
| 2354 | if (ignore_comptime_only) { | ||
| 2355 | return true; | ||
| 2356 | } else if (sema_kit) |sk| { | ||
| 2357 | return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); | ||
| 2358 | } else { | ||
| 2359 | return !comptimeOnly(ty); | ||
| 2360 | } | ||
| 2361 | }, | ||
| 2341 | 2362 | ||
| 2342 | .@"struct" => { | 2363 | .@"struct" => { |
| 2343 | const struct_obj = ty.castTag(.@"struct").?.data; | 2364 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 2365 | if (sema_kit) |sk| { | ||
| 2366 | _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty); | ||
| 2367 | } | ||
| 2344 | switch (struct_obj.requires_comptime) { | 2368 | switch (struct_obj.requires_comptime) { |
| 2345 | .wip => unreachable, | 2369 | .wip => unreachable, |
| 2346 | .yes => return false, | 2370 | .yes => return false, |
| 2347 | .no => if (struct_obj.known_non_opv) return true, | 2371 | .no => if (struct_obj.known_non_opv) return true, |
| 2348 | .unknown => {}, | 2372 | .unknown => {}, |
| 2349 | } | 2373 | } |
| 2374 | if (sema_kit) |sk| { | ||
| 2375 | _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); | ||
| 2376 | } | ||
| 2350 | assert(struct_obj.haveFieldTypes()); | 2377 | assert(struct_obj.haveFieldTypes()); |
| 2351 | for (struct_obj.fields.values()) |value| { | 2378 | for (struct_obj.fields.values()) |value| { |
| 2352 | if (value.is_comptime) continue; | 2379 | if (value.is_comptime) continue; |
| 2353 | if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) | 2380 | if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) |
| 2354 | return true; | 2381 | return true; |
| 2355 | } else { | 2382 | } else { |
| 2356 | return false; | 2383 | return false; |
| ... | @@ -2368,14 +2395,17 @@ pub const Type = extern union { | ... | @@ -2368,14 +2395,17 @@ pub const Type = extern union { |
| 2368 | .enum_numbered, .enum_nonexhaustive => { | 2395 | .enum_numbered, .enum_nonexhaustive => { |
| 2369 | var buffer: Payload.Bits = undefined; | 2396 | var buffer: Payload.Bits = undefined; |
| 2370 | const int_tag_ty = ty.intTagType(&buffer); | 2397 | const int_tag_ty = ty.intTagType(&buffer); |
| 2371 | return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only); | 2398 | return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit); |
| 2372 | }, | 2399 | }, |
| 2373 | 2400 | ||
| 2374 | .@"union" => { | 2401 | .@"union" => { |
| 2375 | const union_obj = ty.castTag(.@"union").?.data; | 2402 | const union_obj = ty.castTag(.@"union").?.data; |
| 2403 | if (sema_kit) |sk| { | ||
| 2404 | _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); | ||
| 2405 | } | ||
| 2376 | assert(union_obj.haveFieldTypes()); | 2406 | assert(union_obj.haveFieldTypes()); |
| 2377 | for (union_obj.fields.values()) |value| { | 2407 | for (union_obj.fields.values()) |value| { |
| 2378 | if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) | 2408 | if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) |
| 2379 | return true; | 2409 | return true; |
| 2380 | } else { | 2410 | } else { |
| 2381 | return false; | 2411 | return false; |
| ... | @@ -2383,29 +2413,32 @@ pub const Type = extern union { | ... | @@ -2383,29 +2413,32 @@ pub const Type = extern union { |
| 2383 | }, | 2413 | }, |
| 2384 | .union_tagged => { | 2414 | .union_tagged => { |
| 2385 | const union_obj = ty.castTag(.union_tagged).?.data; | 2415 | const union_obj = ty.castTag(.union_tagged).?.data; |
| 2386 | if (union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) { | 2416 | if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { |
| 2387 | return true; | 2417 | return true; |
| 2388 | } | 2418 | } |
| 2419 | if (sema_kit) |sk| { | ||
| 2420 | _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); | ||
| 2421 | } | ||
| 2389 | assert(union_obj.haveFieldTypes()); | 2422 | assert(union_obj.haveFieldTypes()); |
| 2390 | for (union_obj.fields.values()) |value| { | 2423 | for (union_obj.fields.values()) |value| { |
| 2391 | if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) | 2424 | if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) |
| 2392 | return true; | 2425 | return true; |
| 2393 | } else { | 2426 | } else { |
| 2394 | return false; | 2427 | return false; |
| 2395 | } | 2428 | } |
| 2396 | }, | 2429 | }, |
| 2397 | 2430 | ||
| 2398 | .array, .vector => ty.arrayLen() != 0 and | 2431 | .array, .vector => return ty.arrayLen() != 0 and |
| 2399 | ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only), | 2432 | try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit), |
| 2400 | .array_u8 => ty.arrayLen() != 0, | 2433 | .array_u8 => return ty.arrayLen() != 0, |
| 2401 | .array_sentinel => ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only), | 2434 | .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit), |
| 2402 | 2435 | ||
| 2403 | .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data != 0, | 2436 | .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0, |
| 2404 | 2437 | ||
| 2405 | .error_union => { | 2438 | .error_union => { |
| 2406 | const payload = ty.castTag(.error_union).?.data; | 2439 | const payload = ty.castTag(.error_union).?.data; |
| 2407 | return payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only) or | 2440 | return (try payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) or |
| 2408 | payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only); | 2441 | (try payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)); |
| 2409 | }, | 2442 | }, |
| 2410 | 2443 | ||
| 2411 | .tuple, .anon_struct => { | 2444 | .tuple, .anon_struct => { |
| ... | @@ -2413,7 +2446,7 @@ pub const Type = extern union { | ... | @@ -2413,7 +2446,7 @@ pub const Type = extern union { |
| 2413 | for (tuple.types) |field_ty, i| { | 2446 | for (tuple.types) |field_ty, i| { |
| 2414 | const val = tuple.values[i]; | 2447 | const val = tuple.values[i]; |
| 2415 | if (val.tag() != .unreachable_value) continue; // comptime field | 2448 | if (val.tag() != .unreachable_value) continue; // comptime field |
| 2416 | if (field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) return true; | 2449 | if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; |
| 2417 | } | 2450 | } |
| 2418 | return false; | 2451 | return false; |
| 2419 | }, | 2452 | }, |
| ... | @@ -2422,7 +2455,7 @@ pub const Type = extern union { | ... | @@ -2422,7 +2455,7 @@ pub const Type = extern union { |
| 2422 | .inferred_alloc_mut => unreachable, | 2455 | .inferred_alloc_mut => unreachable, |
| 2423 | .var_args_param => unreachable, | 2456 | .var_args_param => unreachable, |
| 2424 | .generic_poison => unreachable, | 2457 | .generic_poison => unreachable, |
| 2425 | }; | 2458 | } |
| 2426 | } | 2459 | } |
| 2427 | 2460 | ||
| 2428 | /// true if and only if the type has a well-defined memory layout | 2461 | /// true if and only if the type has a well-defined memory layout |
| ... | @@ -2548,11 +2581,11 @@ pub const Type = extern union { | ... | @@ -2548,11 +2581,11 @@ pub const Type = extern union { |
| 2548 | } | 2581 | } |
| 2549 | 2582 | ||
| 2550 | pub fn hasRuntimeBits(ty: Type) bool { | 2583 | pub fn hasRuntimeBits(ty: Type) bool { |
| 2551 | return hasRuntimeBitsAdvanced(ty, false); | 2584 | return hasRuntimeBitsAdvanced(ty, false, null) catch unreachable; |
| 2552 | } | 2585 | } |
| 2553 | 2586 | ||
| 2554 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool { | 2587 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool { |
| 2555 | return hasRuntimeBitsAdvanced(ty, true); | 2588 | return hasRuntimeBitsAdvanced(ty, true, null) catch unreachable; |
| 2556 | } | 2589 | } |
| 2557 | 2590 | ||
| 2558 | pub fn isFnOrHasRuntimeBits(ty: Type) bool { | 2591 | pub fn isFnOrHasRuntimeBits(ty: Type) bool { |
| ... | @@ -4538,6 +4571,8 @@ pub const Type = extern union { | ... | @@ -4538,6 +4571,8 @@ pub const Type = extern union { |
| 4538 | 4571 | ||
| 4539 | /// During semantic analysis, instead call `Sema.typeRequiresComptime` which | 4572 | /// During semantic analysis, instead call `Sema.typeRequiresComptime` which |
| 4540 | /// resolves field types rather than asserting they are already resolved. | 4573 | /// resolves field types rather than asserting they are already resolved. |
| 4574 | /// TODO merge these implementations together with the "advanced" pattern seen | ||
| 4575 | /// elsewhere in this file. | ||
| 4541 | pub fn comptimeOnly(ty: Type) bool { | 4576 | pub fn comptimeOnly(ty: Type) bool { |
| 4542 | return switch (ty.tag()) { | 4577 | return switch (ty.tag()) { |
| 4543 | .u1, | 4578 | .u1, |
src/value.zig+55-8| ... | @@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator; |
| 9 | const Module = @import("Module.zig"); | 9 | const Module = @import("Module.zig"); |
| 10 | const Air = @import("Air.zig"); | 10 | const Air = @import("Air.zig"); |
| 11 | const TypedValue = @import("TypedValue.zig"); | 11 | const TypedValue = @import("TypedValue.zig"); |
| 12 | const Sema = @import("Sema.zig"); | ||
| 12 | 13 | ||
| 13 | /// This is the raw data, with no bookkeeping, no memory awareness, | 14 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 14 | /// no de-duplication, and no type system awareness. | 15 | /// no de-duplication, and no type system awareness. |
| ... | @@ -990,6 +991,16 @@ pub const Value = extern union { | ... | @@ -990,6 +991,16 @@ pub const Value = extern union { |
| 990 | 991 | ||
| 991 | /// Asserts the value is an integer. | 992 | /// Asserts the value is an integer. |
| 992 | pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { | 993 | pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { |
| 994 | return val.toBigIntAdvanced(space, target, null) catch unreachable; | ||
| 995 | } | ||
| 996 | |||
| 997 | /// Asserts the value is an integer. | ||
| 998 | pub fn toBigIntAdvanced( | ||
| 999 | val: Value, | ||
| 1000 | space: *BigIntSpace, | ||
| 1001 | target: Target, | ||
| 1002 | sema_kit: ?Module.WipAnalysis, | ||
| 1003 | ) !BigIntConst { | ||
| 993 | switch (val.tag()) { | 1004 | switch (val.tag()) { |
| 994 | .zero, | 1005 | .zero, |
| 995 | .bool_false, | 1006 | .bool_false, |
| ... | @@ -1008,7 +1019,11 @@ pub const Value = extern union { | ... | @@ -1008,7 +1019,11 @@ pub const Value = extern union { |
| 1008 | .undef => unreachable, | 1019 | .undef => unreachable, |
| 1009 | 1020 | ||
| 1010 | .lazy_align => { | 1021 | .lazy_align => { |
| 1011 | const x = val.castTag(.lazy_align).?.data.abiAlignment(target); | 1022 | const ty = val.castTag(.lazy_align).?.data; |
| 1023 | if (sema_kit) |sk| { | ||
| 1024 | try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); | ||
| 1025 | } | ||
| 1026 | const x = ty.abiAlignment(target); | ||
| 1012 | return BigIntMutable.init(&space.limbs, x).toConst(); | 1027 | return BigIntMutable.init(&space.limbs, x).toConst(); |
| 1013 | }, | 1028 | }, |
| 1014 | 1029 | ||
| ... | @@ -1019,6 +1034,12 @@ pub const Value = extern union { | ... | @@ -1019,6 +1034,12 @@ pub const Value = extern union { |
| 1019 | /// If the value fits in a u64, return it, otherwise null. | 1034 | /// If the value fits in a u64, return it, otherwise null. |
| 1020 | /// Asserts not undefined. | 1035 | /// Asserts not undefined. |
| 1021 | pub fn getUnsignedInt(val: Value, target: Target) ?u64 { | 1036 | pub fn getUnsignedInt(val: Value, target: Target) ?u64 { |
| 1037 | return getUnsignedIntAdvanced(val, target, null) catch unreachable; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | /// If the value fits in a u64, return it, otherwise null. | ||
| 1041 | /// Asserts not undefined. | ||
| 1042 | pub fn getUnsignedIntAdvanced(val: Value, target: Target, sema_kit: ?Module.WipAnalysis) !?u64 { | ||
| 1022 | switch (val.tag()) { | 1043 | switch (val.tag()) { |
| 1023 | .zero, | 1044 | .zero, |
| 1024 | .bool_false, | 1045 | .bool_false, |
| ... | @@ -1036,7 +1057,13 @@ pub const Value = extern union { | ... | @@ -1036,7 +1057,13 @@ pub const Value = extern union { |
| 1036 | 1057 | ||
| 1037 | .undef => unreachable, | 1058 | .undef => unreachable, |
| 1038 | 1059 | ||
| 1039 | .lazy_align => return val.castTag(.lazy_align).?.data.abiAlignment(target), | 1060 | .lazy_align => { |
| 1061 | const ty = val.castTag(.lazy_align).?.data; | ||
| 1062 | if (sema_kit) |sk| { | ||
| 1063 | try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); | ||
| 1064 | } | ||
| 1065 | return ty.abiAlignment(target); | ||
| 1066 | }, | ||
| 1040 | 1067 | ||
| 1041 | else => return null, | 1068 | else => return null, |
| 1042 | } | 1069 | } |
| ... | @@ -1777,6 +1804,10 @@ pub const Value = extern union { | ... | @@ -1777,6 +1804,10 @@ pub const Value = extern union { |
| 1777 | } | 1804 | } |
| 1778 | 1805 | ||
| 1779 | pub fn orderAgainstZero(lhs: Value) std.math.Order { | 1806 | pub fn orderAgainstZero(lhs: Value) std.math.Order { |
| 1807 | return orderAgainstZeroAdvanced(lhs, null) catch unreachable; | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | pub fn orderAgainstZeroAdvanced(lhs: Value, sema_kit: ?Module.WipAnalysis) !std.math.Order { | ||
| 1780 | return switch (lhs.tag()) { | 1811 | return switch (lhs.tag()) { |
| 1781 | .zero, | 1812 | .zero, |
| 1782 | .bool_false, | 1813 | .bool_false, |
| ... | @@ -1799,7 +1830,7 @@ pub const Value = extern union { | ... | @@ -1799,7 +1830,7 @@ pub const Value = extern union { |
| 1799 | 1830 | ||
| 1800 | .lazy_align => { | 1831 | .lazy_align => { |
| 1801 | const ty = lhs.castTag(.lazy_align).?.data; | 1832 | const ty = lhs.castTag(.lazy_align).?.data; |
| 1802 | if (ty.hasRuntimeBitsIgnoreComptime()) { | 1833 | if (try ty.hasRuntimeBitsAdvanced(false, sema_kit)) { |
| 1803 | return .gt; | 1834 | return .gt; |
| 1804 | } else { | 1835 | } else { |
| 1805 | return .eq; | 1836 | return .eq; |
| ... | @@ -1818,10 +1849,16 @@ pub const Value = extern union { | ... | @@ -1818,10 +1849,16 @@ pub const Value = extern union { |
| 1818 | 1849 | ||
| 1819 | /// Asserts the value is comparable. | 1850 | /// Asserts the value is comparable. |
| 1820 | pub fn order(lhs: Value, rhs: Value, target: Target) std.math.Order { | 1851 | pub fn order(lhs: Value, rhs: Value, target: Target) std.math.Order { |
| 1852 | return orderAdvanced(lhs, rhs, target, null) catch unreachable; | ||
| 1853 | } | ||
| 1854 | |||
| 1855 | /// Asserts the value is comparable. | ||
| 1856 | /// If sema_kit is null then this function asserts things are resolved and cannot fail. | ||
| 1857 | pub fn orderAdvanced(lhs: Value, rhs: Value, target: Target, sema_kit: ?Module.WipAnalysis) !std.math.Order { | ||
| 1821 | const lhs_tag = lhs.tag(); | 1858 | const lhs_tag = lhs.tag(); |
| 1822 | const rhs_tag = rhs.tag(); | 1859 | const rhs_tag = rhs.tag(); |
| 1823 | const lhs_against_zero = lhs.orderAgainstZero(); | 1860 | const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(sema_kit); |
| 1824 | const rhs_against_zero = rhs.orderAgainstZero(); | 1861 | const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(sema_kit); |
| 1825 | switch (lhs_against_zero) { | 1862 | switch (lhs_against_zero) { |
| 1826 | .lt => if (rhs_against_zero != .lt) return .lt, | 1863 | .lt => if (rhs_against_zero != .lt) return .lt, |
| 1827 | .eq => return rhs_against_zero.invert(), | 1864 | .eq => return rhs_against_zero.invert(), |
| ... | @@ -1855,14 +1892,24 @@ pub const Value = extern union { | ... | @@ -1855,14 +1892,24 @@ pub const Value = extern union { |
| 1855 | 1892 | ||
| 1856 | var lhs_bigint_space: BigIntSpace = undefined; | 1893 | var lhs_bigint_space: BigIntSpace = undefined; |
| 1857 | var rhs_bigint_space: BigIntSpace = undefined; | 1894 | var rhs_bigint_space: BigIntSpace = undefined; |
| 1858 | const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, target); | 1895 | const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, target, sema_kit); |
| 1859 | const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, target); | 1896 | const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, target, sema_kit); |
| 1860 | return lhs_bigint.order(rhs_bigint); | 1897 | return lhs_bigint.order(rhs_bigint); |
| 1861 | } | 1898 | } |
| 1862 | 1899 | ||
| 1863 | /// Asserts the value is comparable. Does not take a type parameter because it supports | 1900 | /// Asserts the value is comparable. Does not take a type parameter because it supports |
| 1864 | /// comparisons between heterogeneous types. | 1901 | /// comparisons between heterogeneous types. |
| 1865 | pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, target: Target) bool { | 1902 | pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, target: Target) bool { |
| 1903 | return compareHeteroAdvanced(lhs, op, rhs, target, null) catch unreachable; | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | pub fn compareHeteroAdvanced( | ||
| 1907 | lhs: Value, | ||
| 1908 | op: std.math.CompareOperator, | ||
| 1909 | rhs: Value, | ||
| 1910 | target: Target, | ||
| 1911 | sema_kit: ?Module.WipAnalysis, | ||
| 1912 | ) !bool { | ||
| 1866 | if (lhs.pointerDecl()) |lhs_decl| { | 1913 | if (lhs.pointerDecl()) |lhs_decl| { |
| 1867 | if (rhs.pointerDecl()) |rhs_decl| { | 1914 | if (rhs.pointerDecl()) |rhs_decl| { |
| 1868 | switch (op) { | 1915 | switch (op) { |
| ... | @@ -1884,7 +1931,7 @@ pub const Value = extern union { | ... | @@ -1884,7 +1931,7 @@ pub const Value = extern union { |
| 1884 | else => {}, | 1931 | else => {}, |
| 1885 | } | 1932 | } |
| 1886 | } | 1933 | } |
| 1887 | return order(lhs, rhs, target).compare(op); | 1934 | return (try orderAdvanced(lhs, rhs, target, sema_kit)).compare(op); |
| 1888 | } | 1935 | } |
| 1889 | 1936 | ||
| 1890 | /// Asserts the values are comparable. Both operands have type `ty`. | 1937 | /// Asserts the values are comparable. Both operands have type `ty`. |
test/behavior/struct_contains_slice_of_itself.zig+9| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const expect = @import("std").testing.expect; | 2 | const expect = @import("std").testing.expect; |
| 2 | 3 | ||
| 3 | const Node = struct { | 4 | const Node = struct { |
| ... | @@ -11,6 +12,10 @@ const NodeAligned = struct { | ... | @@ -11,6 +12,10 @@ const NodeAligned = struct { |
| 11 | }; | 12 | }; |
| 12 | 13 | ||
| 13 | test "struct contains slice of itself" { | 14 | test "struct contains slice of itself" { |
| 15 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 16 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 17 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 18 | |||
| 14 | var other_nodes = [_]Node{ | 19 | var other_nodes = [_]Node{ |
| 15 | Node{ | 20 | Node{ |
| 16 | .payload = 31, | 21 | .payload = 31, |
| ... | @@ -48,6 +53,10 @@ test "struct contains slice of itself" { | ... | @@ -48,6 +53,10 @@ test "struct contains slice of itself" { |
| 48 | } | 53 | } |
| 49 | 54 | ||
| 50 | test "struct contains aligned slice of itself" { | 55 | test "struct contains aligned slice of itself" { |
| 56 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 57 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 58 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 59 | |||
| 51 | var other_nodes = [_]NodeAligned{ | 60 | var other_nodes = [_]NodeAligned{ |
| 52 | NodeAligned{ | 61 | NodeAligned{ |
| 53 | .payload = 31, | 62 | .payload = 31, |