authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-05 14:27:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-06 22:40:57-07:00
log9afc4fe0e2114ae0ed53d48d98f5e12b6a269339
tree4324713009e22c6401d73def7fcc574336fb7ab0
parentdf38dfa4d1c9028453f90c7e37dd6c06f829a995

Sema: solve a false positive "depends on itself"

This improves the ABI alignment resolution code. This commit fully enables the MachO linker code in stage3. Note, however, that there are still miscompilations in stage3.

7 files changed, 208 insertions(+), 50 deletions(-)

lib/std/array_hash_map.zig-4
...@@ -82,10 +82,6 @@ pub fn ArrayHashMap(...@@ -82,10 +82,6 @@ pub fn ArrayHashMap(
82 allocator: Allocator,82 allocator: Allocator,
83 ctx: Context,83 ctx: Context,
8484
85 comptime {
86 std.hash_map.verifyContext(Context, K, K, u32, true);
87 }
88
89 /// The ArrayHashMapUnmanaged type using the same settings as this managed map.85 /// The ArrayHashMapUnmanaged type using the same settings as this managed map.
90 pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash);86 pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash);
9187
src/Sema.zig+1
...@@ -18073,6 +18073,7 @@ fn elemValSlice(...@@ -18073,6 +18073,7 @@ fn elemValSlice(
18073 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);18073 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
18074 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);18074 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18075 }18075 }
18076 try sema.queueFullTypeResolution(sema.typeOf(slice));
18076 return block.addBinOp(.slice_elem_val, slice, elem_index);18077 return block.addBinOp(.slice_elem_val, slice, elem_index);
18077}18078}
1807818079
src/link/MachO.zig-3
...@@ -1322,9 +1322,6 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1322,9 +1322,6 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1322 error.EndOfStream, error.NotDylib => {1322 error.EndOfStream, error.NotDylib => {
1323 try file.seekTo(0);1323 try file.seekTo(0);
13241324
1325 // TODO https://github.com/ziglang/zig/issues/11367
1326 if (@import("builtin").zig_backend != .stage1) return error.Unexpected;
1327
1328 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {1325 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
1329 dylib.deinit(self.base.allocator);1326 dylib.deinit(self.base.allocator);
1330 return false;1327 return false;
src/link/MachO/Dylib.zig+4-4
...@@ -242,20 +242,20 @@ fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8)...@@ -242,20 +242,20 @@ fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8)
242242
243 for (expanded) |sym| {243 for (expanded) |sym| {
244 if (self.symbols.contains(sym)) continue;244 if (self.symbols.contains(sym)) continue;
245 try self.symbols.putNoClobber(allocator, sym, .{});245 try self.symbols.putNoClobber(allocator, sym, {});
246 }246 }
247}247}
248248
249fn addObjCIVarSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {249fn addObjCIVarSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
250 const expanded = try std.fmt.allocPrint(allocator, "_OBJC_IVAR_$_{s}", .{sym_name});250 const expanded = try std.fmt.allocPrint(allocator, "_OBJC_IVAR_$_{s}", .{sym_name});
251 if (self.symbols.contains(expanded)) return;251 if (self.symbols.contains(expanded)) return;
252 try self.symbols.putNoClobber(allocator, expanded, .{});252 try self.symbols.putNoClobber(allocator, expanded, {});
253}253}
254254
255fn addObjCEhTypeSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {255fn addObjCEhTypeSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
256 const expanded = try std.fmt.allocPrint(allocator, "_OBJC_EHTYPE_$_{s}", .{sym_name});256 const expanded = try std.fmt.allocPrint(allocator, "_OBJC_EHTYPE_$_{s}", .{sym_name});
257 if (self.symbols.contains(expanded)) return;257 if (self.symbols.contains(expanded)) return;
258 try self.symbols.putNoClobber(allocator, expanded, .{});258 try self.symbols.putNoClobber(allocator, expanded, {});
259}259}
260260
261fn addSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {261fn addSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
...@@ -373,7 +373,7 @@ pub fn parseFromStub(...@@ -373,7 +373,7 @@ pub fn parseFromStub(
373 // TODO I thought that we could switch on presence of `parent-umbrella` map;373 // TODO I thought that we could switch on presence of `parent-umbrella` map;
374 // however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib`374 // however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib`
375 // BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps?375 // BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps?
376 try umbrella_libs.put(elem.installName(), .{});376 try umbrella_libs.put(elem.installName(), {});
377 }377 }
378378
379 switch (elem) {379 switch (elem) {
src/link/tapi/yaml.zig+1-1
...@@ -153,7 +153,7 @@ pub const Value = union(ValueType) {...@@ -153,7 +153,7 @@ pub const Value = union(ValueType) {
153 if (node.cast(Node.Doc)) |doc| {153 if (node.cast(Node.Doc)) |doc| {
154 const inner = doc.value orelse {154 const inner = doc.value orelse {
155 // empty doc155 // empty doc
156 return Value{ .empty = .{} };156 return Value{ .empty = {} };
157 };157 };
158 return Value.fromNode(arena, tree, inner, null);158 return Value.fromNode(arena, tree, inner, null);
159 } else if (node.cast(Node.Map)) |map| {159 } else if (node.cast(Node.Map)) |map| {
src/type.zig+91-38
...@@ -2394,11 +2394,15 @@ pub const Type = extern union {...@@ -2394,11 +2394,15 @@ pub const Type = extern union {
2394 _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty);2394 _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty);
2395 }2395 }
2396 switch (struct_obj.requires_comptime) {2396 switch (struct_obj.requires_comptime) {
2397 .wip => unreachable,
2398 .yes => return false,2397 .yes => return false,
2399 .no => if (struct_obj.known_non_opv) return true,2398 .wip, .no => if (struct_obj.known_non_opv) return true,
2400 .unknown => {},2399 .unknown => {},
2401 }2400 }
2401 if (struct_obj.status == .field_types_wip) {
2402 // In this case, we guess that hasRuntimeBits() for this type is true,
2403 // and then later if our guess was incorrect, we emit a compile error.
2404 return true;
2405 }
2402 if (sema_kit) |sk| {2406 if (sema_kit) |sk| {
2403 _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);2407 _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
2404 }2408 }
...@@ -2735,6 +2739,12 @@ pub const Type = extern union {...@@ -2735,6 +2739,12 @@ pub const Type = extern union {
2735 val: Value,2739 val: Value,
2736 };2740 };
27372741
2742 const AbiAlignmentAdvancedStrat = union(enum) {
2743 eager,
2744 lazy: Allocator,
2745 sema_kit: Module.WipAnalysis,
2746 };
2747
2738 /// If you pass `eager` you will get back `scalar` and assert the type is resolved.2748 /// If you pass `eager` you will get back `scalar` and assert the type is resolved.
2739 /// In this case there will be no error, guaranteed.2749 /// In this case there will be no error, guaranteed.
2740 /// If you pass `lazy` you may get back `scalar` or `val`.2750 /// If you pass `lazy` you may get back `scalar` or `val`.
...@@ -2744,11 +2754,7 @@ pub const Type = extern union {...@@ -2744,11 +2754,7 @@ pub const Type = extern union {
2744 pub fn abiAlignmentAdvanced(2754 pub fn abiAlignmentAdvanced(
2745 ty: Type,2755 ty: Type,
2746 target: Target,2756 target: Target,
2747 strat: union(enum) {2757 strat: AbiAlignmentAdvancedStrat,
2748 eager,
2749 lazy: Allocator,
2750 sema_kit: Module.WipAnalysis,
2751 },
2752 ) Module.CompileError!AbiAlignmentAdvanced {2758 ) Module.CompileError!AbiAlignmentAdvanced {
2753 const sema_kit = switch (strat) {2759 const sema_kit = switch (strat) {
2754 .sema_kit => |sk| sk,2760 .sema_kit => |sk| sk,
...@@ -2928,21 +2934,24 @@ pub const Type = extern union {...@@ -2928,21 +2934,24 @@ pub const Type = extern union {
2928 },2934 },
29292935
2930 .@"struct" => {2936 .@"struct" => {
2937 const struct_obj = ty.castTag(.@"struct").?.data;
2931 if (sema_kit) |sk| {2938 if (sema_kit) |sk| {
2932 try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);2939 if (struct_obj.status == .field_types_wip) {
2933 }2940 // We'll guess "pointer-aligned" and if we guess wrong, emit
2934 if (ty.castTag(.@"struct")) |payload| {2941 // a compile error later.
2935 const struct_obj = payload.data;2942 return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
2936 if (!struct_obj.haveLayout()) switch (strat) {
2937 .eager => unreachable, // struct layout not resolved
2938 .sema_kit => unreachable, // handled above
2939 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
2940 };
2941 if (struct_obj.layout == .Packed) {
2942 var buf: Type.Payload.Bits = undefined;
2943 const int_ty = struct_obj.packedIntegerType(target, &buf);
2944 return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) };
2945 }2943 }
2944 _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
2945 }
2946 if (!struct_obj.haveFieldTypes()) switch (strat) {
2947 .eager => unreachable, // struct layout not resolved
2948 .sema_kit => unreachable, // handled above
2949 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
2950 };
2951 if (struct_obj.layout == .Packed) {
2952 var buf: Type.Payload.Bits = undefined;
2953 const int_ty = struct_obj.packedIntegerType(target, &buf);
2954 return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) };
2946 }2955 }
29472956
2948 const fields = ty.structFields();2957 const fields = ty.structFields();
...@@ -2950,7 +2959,16 @@ pub const Type = extern union {...@@ -2950,7 +2959,16 @@ pub const Type = extern union {
2950 for (fields.values()) |field| {2959 for (fields.values()) |field| {
2951 if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue;2960 if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue;
29522961
2953 const field_align = field.normalAlignment(target);2962 const field_align = if (field.abi_align != 0)
2963 field.abi_align
2964 else switch (try field.ty.abiAlignmentAdvanced(target, strat)) {
2965 .scalar => |a| a,
2966 .val => switch (strat) {
2967 .eager => unreachable, // struct layout not resolved
2968 .sema_kit => unreachable, // handled above
2969 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
2970 },
2971 };
2954 big_align = @maximum(big_align, field_align);2972 big_align = @maximum(big_align, field_align);
2955 }2973 }
2956 return AbiAlignmentAdvanced{ .scalar = big_align };2974 return AbiAlignmentAdvanced{ .scalar = big_align };
...@@ -2980,24 +2998,14 @@ pub const Type = extern union {...@@ -2980,24 +2998,14 @@ pub const Type = extern union {
2980 const int_tag_ty = ty.intTagType(&buffer);2998 const int_tag_ty = ty.intTagType(&buffer);
2981 return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(target) };2999 return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(target) };
2982 },3000 },
2983 .@"union" => switch (strat) {3001 .@"union" => {
2984 .eager, .sema_kit => {3002 const union_obj = ty.castTag(.@"union").?.data;
2985 if (sema_kit) |sk| {3003 // TODO pass `true` for have_tag when unions have a safety tag
2986 try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);3004 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false);
2987 }
2988 // TODO pass `true` for have_tag when unions have a safety tag
2989 return AbiAlignmentAdvanced{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) };
2990 },
2991 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
2992 },3005 },
2993 .union_tagged => switch (strat) {3006 .union_tagged => {
2994 .eager, .sema_kit => {3007 const union_obj = ty.castTag(.union_tagged).?.data;
2995 if (sema_kit) |sk| {3008 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true);
2996 try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);
2997 }
2998 return AbiAlignmentAdvanced{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) };
2999 },
3000 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
3001 },3009 },
30023010
3003 .empty_struct,3011 .empty_struct,
...@@ -3023,6 +3031,51 @@ pub const Type = extern union {...@@ -3023,6 +3031,51 @@ pub const Type = extern union {
3023 };3031 };
3024 }3032 }
30253033
3034 pub fn abiAlignmentAdvancedUnion(
3035 ty: Type,
3036 target: Target,
3037 strat: AbiAlignmentAdvancedStrat,
3038 union_obj: *Module.Union,
3039 have_tag: bool,
3040 ) Module.CompileError!AbiAlignmentAdvanced {
3041 const sema_kit = switch (strat) {
3042 .sema_kit => |sk| sk,
3043 else => null,
3044 };
3045 if (sema_kit) |sk| {
3046 if (union_obj.status == .field_types_wip) {
3047 // We'll guess "pointer-aligned" and if we guess wrong, emit
3048 // a compile error later.
3049 return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
3050 }
3051 _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
3052 }
3053 if (!union_obj.haveFieldTypes()) switch (strat) {
3054 .eager => unreachable, // union layout not resolved
3055 .sema_kit => unreachable, // handled above
3056 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
3057 };
3058
3059 var max_align: u32 = 0;
3060 if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target);
3061 for (union_obj.fields.values()) |field| {
3062 if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue;
3063
3064 const field_align = if (field.abi_align != 0)
3065 field.abi_align
3066 else switch (try field.ty.abiAlignmentAdvanced(target, strat)) {
3067 .scalar => |a| a,
3068 .val => switch (strat) {
3069 .eager => unreachable, // struct layout not resolved
3070 .sema_kit => unreachable, // handled above
3071 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
3072 },
3073 };
3074 max_align = @maximum(max_align, field_align);
3075 }
3076 return AbiAlignmentAdvanced{ .scalar = max_align };
3077 }
3078
3026 /// Asserts the type has the ABI size already resolved.3079 /// Asserts the type has the ABI size already resolved.
3027 /// Types that return false for hasRuntimeBits() return 0.3080 /// Types that return false for hasRuntimeBits() return 0.
3028 pub fn abiSize(self: Type, target: Target) u64 {3081 pub fn abiSize(self: Type, target: Target) u64 {
test/behavior/eval.zig+111
...@@ -999,3 +999,114 @@ test "comptime break operand passing through runtime switch converted to runtime...@@ -999,3 +999,114 @@ test "comptime break operand passing through runtime switch converted to runtime
999 try S.doTheTest('b');999 try S.doTheTest('b');
1000 comptime try S.doTheTest('b');1000 comptime try S.doTheTest('b');
1001}1001}
1002
1003test "no dependency loop for alignment of self struct" {
1004 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1005 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1006
1007 const S = struct {
1008 fn doTheTest() !void {
1009 var a: namespace.A = undefined;
1010 a.d = .{ .g = &buf };
1011 a.d.g[3] = 42;
1012 a.d.g[3] += 1;
1013 try expect(a.d.g[3] == 43);
1014 }
1015
1016 var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
1017
1018 const namespace = struct {
1019 const B = struct { a: A };
1020 const A = C(B);
1021 };
1022
1023 pub fn C(comptime B: type) type {
1024 return struct {
1025 d: D(F) = .{},
1026
1027 const F = struct { b: B };
1028 };
1029 }
1030
1031 pub fn D(comptime F: type) type {
1032 return struct {
1033 g: [*]align(@alignOf(F)) u8 = undefined,
1034 };
1035 }
1036 };
1037 try S.doTheTest();
1038}
1039
1040test "no dependency loop for alignment of self bare union" {
1041 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1042 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1043
1044 const S = struct {
1045 fn doTheTest() !void {
1046 var a: namespace.A = undefined;
1047 a.d = .{ .g = &buf };
1048 a.d.g[3] = 42;
1049 a.d.g[3] += 1;
1050 try expect(a.d.g[3] == 43);
1051 }
1052
1053 var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
1054
1055 const namespace = struct {
1056 const B = union { a: A, b: void };
1057 const A = C(B);
1058 };
1059
1060 pub fn C(comptime B: type) type {
1061 return struct {
1062 d: D(F) = .{},
1063
1064 const F = struct { b: B };
1065 };
1066 }
1067
1068 pub fn D(comptime F: type) type {
1069 return struct {
1070 g: [*]align(@alignOf(F)) u8 = undefined,
1071 };
1072 }
1073 };
1074 try S.doTheTest();
1075}
1076
1077test "no dependency loop for alignment of self tagged union" {
1078 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1079 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1080
1081 const S = struct {
1082 fn doTheTest() !void {
1083 var a: namespace.A = undefined;
1084 a.d = .{ .g = &buf };
1085 a.d.g[3] = 42;
1086 a.d.g[3] += 1;
1087 try expect(a.d.g[3] == 43);
1088 }
1089
1090 var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
1091
1092 const namespace = struct {
1093 const B = union(enum) { a: A, b: void };
1094 const A = C(B);
1095 };
1096
1097 pub fn C(comptime B: type) type {
1098 return struct {
1099 d: D(F) = .{},
1100
1101 const F = struct { b: B };
1102 };
1103 }
1104
1105 pub fn D(comptime F: type) type {
1106 return struct {
1107 g: [*]align(@alignOf(F)) u8 = undefined,
1108 };
1109 }
1110 };
1111 try S.doTheTest();
1112}