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.
This commit is contained in:
Andrew Kelley 2022-05-05 14:27:20 -07:00
parent df38dfa4d1
commit 9afc4fe0e2
7 changed files with 208 additions and 50 deletions

View File

@ -82,10 +82,6 @@ pub fn ArrayHashMap(
allocator: Allocator,
ctx: Context,
comptime {
std.hash_map.verifyContext(Context, K, K, u32, true);
}
/// The ArrayHashMapUnmanaged type using the same settings as this managed map.
pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash);

View File

@ -18073,6 +18073,7 @@ fn elemValSlice(
const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
}
try sema.queueFullTypeResolution(sema.typeOf(slice));
return block.addBinOp(.slice_elem_val, slice, elem_index);
}

View File

@ -1322,9 +1322,6 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
error.EndOfStream, error.NotDylib => {
try file.seekTo(0);
// TODO https://github.com/ziglang/zig/issues/11367
if (@import("builtin").zig_backend != .stage1) return error.Unexpected;
var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
dylib.deinit(self.base.allocator);
return false;

View File

@ -242,20 +242,20 @@ fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8)
for (expanded) |sym| {
if (self.symbols.contains(sym)) continue;
try self.symbols.putNoClobber(allocator, sym, .{});
try self.symbols.putNoClobber(allocator, sym, {});
}
}
fn addObjCIVarSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
const expanded = try std.fmt.allocPrint(allocator, "_OBJC_IVAR_$_{s}", .{sym_name});
if (self.symbols.contains(expanded)) return;
try self.symbols.putNoClobber(allocator, expanded, .{});
try self.symbols.putNoClobber(allocator, expanded, {});
}
fn addObjCEhTypeSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
const expanded = try std.fmt.allocPrint(allocator, "_OBJC_EHTYPE_$_{s}", .{sym_name});
if (self.symbols.contains(expanded)) return;
try self.symbols.putNoClobber(allocator, expanded, .{});
try self.symbols.putNoClobber(allocator, expanded, {});
}
fn addSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
@ -373,7 +373,7 @@ pub fn parseFromStub(
// TODO I thought that we could switch on presence of `parent-umbrella` map;
// however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib`
// BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps?
try umbrella_libs.put(elem.installName(), .{});
try umbrella_libs.put(elem.installName(), {});
}
switch (elem) {

View File

@ -153,7 +153,7 @@ pub const Value = union(ValueType) {
if (node.cast(Node.Doc)) |doc| {
const inner = doc.value orelse {
// empty doc
return Value{ .empty = .{} };
return Value{ .empty = {} };
};
return Value.fromNode(arena, tree, inner, null);
} else if (node.cast(Node.Map)) |map| {

View File

@ -2394,11 +2394,15 @@ pub const Type = extern union {
_ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty);
}
switch (struct_obj.requires_comptime) {
.wip => unreachable,
.yes => return false,
.no => if (struct_obj.known_non_opv) return true,
.wip, .no => if (struct_obj.known_non_opv) return true,
.unknown => {},
}
if (struct_obj.status == .field_types_wip) {
// In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error.
return true;
}
if (sema_kit) |sk| {
_ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
}
@ -2735,6 +2739,12 @@ pub const Type = extern union {
val: Value,
};
const AbiAlignmentAdvancedStrat = union(enum) {
eager,
lazy: Allocator,
sema_kit: Module.WipAnalysis,
};
/// If you pass `eager` you will get back `scalar` and assert the type is resolved.
/// In this case there will be no error, guaranteed.
/// If you pass `lazy` you may get back `scalar` or `val`.
@ -2744,11 +2754,7 @@ pub const Type = extern union {
pub fn abiAlignmentAdvanced(
ty: Type,
target: Target,
strat: union(enum) {
eager,
lazy: Allocator,
sema_kit: Module.WipAnalysis,
},
strat: AbiAlignmentAdvancedStrat,
) Module.CompileError!AbiAlignmentAdvanced {
const sema_kit = switch (strat) {
.sema_kit => |sk| sk,
@ -2928,21 +2934,24 @@ pub const Type = extern union {
},
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
if (sema_kit) |sk| {
try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);
}
if (ty.castTag(.@"struct")) |payload| {
const struct_obj = payload.data;
if (!struct_obj.haveLayout()) switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema_kit => unreachable, // handled above
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
};
if (struct_obj.layout == .Packed) {
var buf: Type.Payload.Bits = undefined;
const int_ty = struct_obj.packedIntegerType(target, &buf);
return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) };
if (struct_obj.status == .field_types_wip) {
// We'll guess "pointer-aligned" and if we guess wrong, emit
// a compile error later.
return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
}
_ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
}
if (!struct_obj.haveFieldTypes()) switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema_kit => unreachable, // handled above
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
};
if (struct_obj.layout == .Packed) {
var buf: Type.Payload.Bits = undefined;
const int_ty = struct_obj.packedIntegerType(target, &buf);
return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) };
}
const fields = ty.structFields();
@ -2950,7 +2959,16 @@ pub const Type = extern union {
for (fields.values()) |field| {
if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue;
const field_align = field.normalAlignment(target);
const field_align = if (field.abi_align != 0)
field.abi_align
else switch (try field.ty.abiAlignmentAdvanced(target, strat)) {
.scalar => |a| a,
.val => switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema_kit => unreachable, // handled above
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
},
};
big_align = @maximum(big_align, field_align);
}
return AbiAlignmentAdvanced{ .scalar = big_align };
@ -2980,24 +2998,14 @@ pub const Type = extern union {
const int_tag_ty = ty.intTagType(&buffer);
return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(target) };
},
.@"union" => switch (strat) {
.eager, .sema_kit => {
if (sema_kit) |sk| {
try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);
}
// TODO pass `true` for have_tag when unions have a safety tag
return AbiAlignmentAdvanced{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) };
},
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
.@"union" => {
const union_obj = ty.castTag(.@"union").?.data;
// TODO pass `true` for have_tag when unions have a safety tag
return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false);
},
.union_tagged => switch (strat) {
.eager, .sema_kit => {
if (sema_kit) |sk| {
try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);
}
return AbiAlignmentAdvanced{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) };
},
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
.union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data;
return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true);
},
.empty_struct,
@ -3023,6 +3031,51 @@ pub const Type = extern union {
};
}
pub fn abiAlignmentAdvancedUnion(
ty: Type,
target: Target,
strat: AbiAlignmentAdvancedStrat,
union_obj: *Module.Union,
have_tag: bool,
) Module.CompileError!AbiAlignmentAdvanced {
const sema_kit = switch (strat) {
.sema_kit => |sk| sk,
else => null,
};
if (sema_kit) |sk| {
if (union_obj.status == .field_types_wip) {
// We'll guess "pointer-aligned" and if we guess wrong, emit
// a compile error later.
return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
}
_ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
}
if (!union_obj.haveFieldTypes()) switch (strat) {
.eager => unreachable, // union layout not resolved
.sema_kit => unreachable, // handled above
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
};
var max_align: u32 = 0;
if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target);
for (union_obj.fields.values()) |field| {
if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue;
const field_align = if (field.abi_align != 0)
field.abi_align
else switch (try field.ty.abiAlignmentAdvanced(target, strat)) {
.scalar => |a| a,
.val => switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema_kit => unreachable, // handled above
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
},
};
max_align = @maximum(max_align, field_align);
}
return AbiAlignmentAdvanced{ .scalar = max_align };
}
/// Asserts the type has the ABI size already resolved.
/// Types that return false for hasRuntimeBits() return 0.
pub fn abiSize(self: Type, target: Target) u64 {

View File

@ -999,3 +999,114 @@ test "comptime break operand passing through runtime switch converted to runtime
try S.doTheTest('b');
comptime try S.doTheTest('b');
}
test "no dependency loop for alignment of self struct" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: namespace.A = undefined;
a.d = .{ .g = &buf };
a.d.g[3] = 42;
a.d.g[3] += 1;
try expect(a.d.g[3] == 43);
}
var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
const namespace = struct {
const B = struct { a: A };
const A = C(B);
};
pub fn C(comptime B: type) type {
return struct {
d: D(F) = .{},
const F = struct { b: B };
};
}
pub fn D(comptime F: type) type {
return struct {
g: [*]align(@alignOf(F)) u8 = undefined,
};
}
};
try S.doTheTest();
}
test "no dependency loop for alignment of self bare union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: namespace.A = undefined;
a.d = .{ .g = &buf };
a.d.g[3] = 42;
a.d.g[3] += 1;
try expect(a.d.g[3] == 43);
}
var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
const namespace = struct {
const B = union { a: A, b: void };
const A = C(B);
};
pub fn C(comptime B: type) type {
return struct {
d: D(F) = .{},
const F = struct { b: B };
};
}
pub fn D(comptime F: type) type {
return struct {
g: [*]align(@alignOf(F)) u8 = undefined,
};
}
};
try S.doTheTest();
}
test "no dependency loop for alignment of self tagged union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: namespace.A = undefined;
a.d = .{ .g = &buf };
a.d.g[3] = 42;
a.d.g[3] += 1;
try expect(a.d.g[3] == 43);
}
var buf: [10]u8 align(@alignOf([*]u8)) = undefined;
const namespace = struct {
const B = union(enum) { a: A, b: void };
const A = C(B);
};
pub fn C(comptime B: type) type {
return struct {
d: D(F) = .{},
const F = struct { b: B };
};
}
pub fn D(comptime F: type) type {
return struct {
g: [*]align(@alignOf(F)) u8 = undefined,
};
}
};
try S.doTheTest();
}