partially implement peer type resolution for pointer types
This commit is contained in:
parent
475da58895
commit
5598ad032b
@ -31,20 +31,20 @@ pub const Int = struct {
|
|||||||
|
|
||||||
pub const Pointer = struct {
|
pub const Pointer = struct {
|
||||||
elem_type: Index,
|
elem_type: Index,
|
||||||
sentinel: Index,
|
sentinel: Index = .none,
|
||||||
alignment: u16,
|
alignment: u16 = 0,
|
||||||
size: std.builtin.Type.Pointer.Size,
|
size: std.builtin.Type.Pointer.Size,
|
||||||
is_const: bool,
|
is_const: bool = false,
|
||||||
is_volatile: bool,
|
is_volatile: bool = false,
|
||||||
is_allowzero: bool,
|
is_allowzero: bool = false,
|
||||||
address_space: std.builtin.AddressSpace,
|
address_space: std.builtin.AddressSpace = .generic,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Array = struct {
|
pub const Array = struct {
|
||||||
// TODO support big int
|
// TODO support big int
|
||||||
len: u32,
|
len: u32,
|
||||||
child: Index,
|
child: Index,
|
||||||
sentinel: Index,
|
sentinel: Index = .none,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Struct = struct {
|
pub const Struct = struct {
|
||||||
@ -181,6 +181,7 @@ pub const Key = union(enum) {
|
|||||||
union_type: Union,
|
union_type: Union,
|
||||||
tuple_type: Tuple,
|
tuple_type: Tuple,
|
||||||
vector_type: Vector,
|
vector_type: Vector,
|
||||||
|
// TODO anyframe_T
|
||||||
|
|
||||||
declaration: Decl,
|
declaration: Decl,
|
||||||
namespace: Namespace,
|
namespace: Namespace,
|
||||||
@ -560,6 +561,61 @@ pub const Key = union(enum) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For pointer-like optionals, returns true, otherwise returns the allowzero property
|
||||||
|
/// of pointers.
|
||||||
|
pub fn ptrAllowsZero(ty: Key, ip: *const InternPool) bool {
|
||||||
|
if (ty.isPtrLikeOptional(ip)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ty.pointer_type.is_allowzero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the type is optional and would be lowered to a single pointer
|
||||||
|
/// address value, using 0 for null. Note that this returns true for C pointers.
|
||||||
|
pub fn isPtrLikeOptional(ty: Key, ip: *const InternPool) bool {
|
||||||
|
switch (ty) {
|
||||||
|
.optional_type => |optional_info| {
|
||||||
|
const child_ty = optional_info.payload_type;
|
||||||
|
const child_key = ip.indexToKey(child_ty);
|
||||||
|
if(child_key != .pointer_type) return false;
|
||||||
|
const info = child_key.pointer_type;
|
||||||
|
switch (info.size) {
|
||||||
|
.Slice, .C => return false,
|
||||||
|
.Many, .One => return !info.is_allowzero,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.pointer_type => |pointer_info| return pointer_info.size == .C,
|
||||||
|
else => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elemType2(ty: Key) Index {
|
||||||
|
return switch (ty) {
|
||||||
|
.simple => |simple| switch(simple) {
|
||||||
|
.@"anyframe" => @panic("TODO: return void type"),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.pointer_type => |pointer_info| pointer_info.elem_type,
|
||||||
|
.array_type => |array_info| array_info.child,
|
||||||
|
.optional_type => |optional_info| optional_info.payload_type,
|
||||||
|
.vector_type => |vector_info| vector_info.child,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts the type is an array, pointer or vector.
|
||||||
|
pub fn sentinel(ty: Key) Index {
|
||||||
|
return switch (ty) {
|
||||||
|
.pointer_type => |pointer_info| pointer_info.sentinel,
|
||||||
|
.array_type => |array_info| array_info.sentinel,
|
||||||
|
.struct_type,
|
||||||
|
.tuple_type,
|
||||||
|
.vector_type,
|
||||||
|
=> Index.none,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getNamespace(ty: Key) ?Index {
|
pub fn getNamespace(ty: Key) ?Index {
|
||||||
return switch (ty) {
|
return switch (ty) {
|
||||||
.struct_type => |struct_info| struct_info.namespace,
|
.struct_type => |struct_info| struct_info.namespace,
|
||||||
@ -1152,6 +1208,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
var arena = arena_allocator.allocator();
|
var arena = arena_allocator.allocator();
|
||||||
|
|
||||||
var chosen = types[0];
|
var chosen = types[0];
|
||||||
|
// // If this is non-null then it does the following thing, depending on the chosen zigTypeTag().
|
||||||
|
// // * ErrorSet: this is an override
|
||||||
|
// // * ErrorUnion: this is an override of the error set only
|
||||||
|
// // * other: at the end we make an ErrorUnion with the other thing and this
|
||||||
|
// var err_set_ty: Index = Index.none;
|
||||||
var any_are_null = false;
|
var any_are_null = false;
|
||||||
var seen_const = false;
|
var seen_const = false;
|
||||||
var convert_to_slice = false;
|
var convert_to_slice = false;
|
||||||
@ -1162,6 +1223,17 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
const candidate_key: Key = ip.indexToKey(candidate);
|
const candidate_key: Key = ip.indexToKey(candidate);
|
||||||
const chosen_key = ip.indexToKey(chosen);
|
const chosen_key = ip.indexToKey(chosen);
|
||||||
|
|
||||||
|
// If the candidate can coerce into our chosen type, we're done.
|
||||||
|
// If the chosen type can coerce into the candidate, use that.
|
||||||
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen, candidate, true, target)) == .ok) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, candidate, chosen, true, target)) == .ok) {
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (candidate_key) {
|
switch (candidate_key) {
|
||||||
.simple => |candidate_simple| switch (candidate_simple) {
|
.simple => |candidate_simple| switch (candidate_simple) {
|
||||||
.f16, .f32, .f64, .f80, .f128 => switch (chosen_key) {
|
.f16, .f32, .f64, .f80, .f128 => switch (chosen_key) {
|
||||||
@ -1170,8 +1242,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
if (chosen_key.floatBits(target) < candidate_key.floatBits(target)) {
|
if (chosen_key.floatBits(target) < candidate_key.floatBits(target)) {
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
},
|
},
|
||||||
.comptime_int, .comptime_float => {
|
.comptime_int, .comptime_float => {
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
@ -1193,7 +1265,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
.c_ulong,
|
.c_ulong,
|
||||||
.c_longlong,
|
.c_longlong,
|
||||||
.c_ulonglong,
|
.c_ulonglong,
|
||||||
.c_longdouble, => switch (chosen_key) {
|
.c_longdouble,
|
||||||
|
=> switch (chosen_key) {
|
||||||
.simple => |chosen_simple| switch (chosen_simple) {
|
.simple => |chosen_simple| switch (chosen_simple) {
|
||||||
.usize,
|
.usize,
|
||||||
.isize,
|
.isize,
|
||||||
@ -1205,11 +1278,12 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
.c_ulong,
|
.c_ulong,
|
||||||
.c_longlong,
|
.c_longlong,
|
||||||
.c_ulonglong,
|
.c_ulonglong,
|
||||||
.c_longdouble, => {
|
.c_longdouble,
|
||||||
|
=> {
|
||||||
const chosen_bits = chosen_key.intInfo(target, ip).bits;
|
const chosen_bits = chosen_key.intInfo(target, ip).bits;
|
||||||
const candidate_bits = candidate_key.intInfo(target, ip).bits;
|
const candidate_bits = candidate_key.intInfo(target, ip).bits;
|
||||||
|
|
||||||
if(chosen_bits < candidate_bits) {
|
if (chosen_bits < candidate_bits) {
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
}
|
}
|
||||||
@ -1253,7 +1327,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
.c_longlong,
|
.c_longlong,
|
||||||
.c_ulonglong,
|
.c_ulonglong,
|
||||||
.c_longdouble,
|
.c_longdouble,
|
||||||
.comptime_float, => continue,
|
.comptime_float,
|
||||||
|
=> continue,
|
||||||
.comptime_int => unreachable,
|
.comptime_int => unreachable,
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
@ -1292,11 +1367,12 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
.c_ulong,
|
.c_ulong,
|
||||||
.c_longlong,
|
.c_longlong,
|
||||||
.c_ulonglong,
|
.c_ulonglong,
|
||||||
.c_longdouble, => {
|
.c_longdouble,
|
||||||
|
=> {
|
||||||
const chosen_bits = chosen_key.intInfo(target, ip).bits;
|
const chosen_bits = chosen_key.intInfo(target, ip).bits;
|
||||||
const candidate_bits = candidate_key.intInfo(target, ip).bits;
|
const candidate_bits = candidate_key.intInfo(target, ip).bits;
|
||||||
|
|
||||||
if(chosen_bits < candidate_bits) {
|
if (chosen_bits < candidate_bits) {
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
}
|
}
|
||||||
@ -1368,13 +1444,13 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
const chosen_elem_ty = chosen_elem_info.array_type.child;
|
const chosen_elem_ty = chosen_elem_info.array_type.child;
|
||||||
const cand_elem_ty = candidate_elem_info.array_type.child;
|
const cand_elem_ty = candidate_elem_info.array_type.child;
|
||||||
|
|
||||||
const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, chosen_elem_ty, cand_elem_ty, !chosen_info.is_const, target);
|
const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, chosen_elem_ty, cand_elem_ty, chosen_info.is_const, target);
|
||||||
if (chosen_ok) {
|
if (chosen_ok) {
|
||||||
convert_to_slice = true;
|
convert_to_slice = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, cand_elem_ty, chosen_elem_ty, !candidate_info.is_const, target);
|
const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, cand_elem_ty, chosen_elem_ty, candidate_info.is_const, target);
|
||||||
if (cand_ok) {
|
if (cand_ok) {
|
||||||
convert_to_slice = true;
|
convert_to_slice = true;
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
@ -1394,8 +1470,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
// the one we will keep. If they're both OK then we keep the
|
// the one we will keep. If they're both OK then we keep the
|
||||||
// C pointer since it matches both single and many pointers.
|
// C pointer since it matches both single and many pointers.
|
||||||
if (candidate_info.size == .C or chosen_info.size == .C) {
|
if (candidate_info.size == .C or chosen_info.size == .C) {
|
||||||
const cand_ok = .ok == try ip.coerceInMemoryAllowed(arena, candidate_info.elem_type, chosen_info.elem_type, !candidate_info.is_const, target);
|
const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, candidate_info.elem_type, chosen_info.elem_type, candidate_info.is_const, target);
|
||||||
const chosen_ok = .ok == try ip.coerceInMemoryAllowed(arena, chosen_info.elem_type, candidate_info.elem_type, !chosen_info.is_const, target);
|
const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.elem_type, candidate_info.elem_type, chosen_info.is_const, target);
|
||||||
|
|
||||||
if (cand_ok) {
|
if (cand_ok) {
|
||||||
if (!chosen_ok or chosen_info.size != .C) {
|
if (!chosen_ok or chosen_info.size != .C) {
|
||||||
@ -1412,7 +1488,6 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
// "incompatible types" error below.
|
// "incompatible types" error below.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
},
|
},
|
||||||
.int_type => {
|
.int_type => {
|
||||||
if (candidate_info.size == .C) {
|
if (candidate_info.size == .C) {
|
||||||
@ -1457,7 +1532,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
if (candidate_info.is_const) {
|
if (candidate_info.is_const) {
|
||||||
const candidate_elem_key = ip.indexToKey(candidate_info.elem_type);
|
const candidate_elem_key = ip.indexToKey(candidate_info.elem_type);
|
||||||
if (candidate_elem_key == .function_type and
|
if (candidate_elem_key == .function_type and
|
||||||
.ok == try ip.coerceInMemoryAllowedFns(arena, chosen_info, candidate_elem_key.function_type, target))
|
.ok == try ip.coerceInMemoryAllowedFns(gpa, arena, chosen_info, candidate_elem_key.function_type, target))
|
||||||
{
|
{
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
@ -1472,18 +1547,13 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
.optional_type => |candidate_info| {
|
.optional_type => |candidate_info| {
|
||||||
const is_chosen_const_ptr = switch (chosen_key) {
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen, candidate_info.payload_type, true, target)) == .ok) {
|
||||||
.pointer_type => |chosen_info| chosen_info.is_const,
|
|
||||||
else => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((try ip.coerceInMemoryAllowed(arena, chosen, candidate_info.payload_type, false, target)) == .ok) {
|
|
||||||
seen_const = seen_const or ip.indexToKey(candidate_info.payload_type).isConstPtr();
|
seen_const = seen_const or ip.indexToKey(candidate_info.payload_type).isConstPtr();
|
||||||
any_are_null = true;
|
any_are_null = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen_const = seen_const or is_chosen_const_ptr;
|
seen_const = seen_const or chosen_key.isConstPtr();
|
||||||
any_are_null = false;
|
any_are_null = false;
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
@ -1518,10 +1588,10 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
.optional_type => |chosen_info| {
|
.optional_type => |chosen_info| {
|
||||||
if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) {
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.payload_type, candidate, true, target)) == .ok) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((try ip.coerceInMemoryAllowed(arena, candidate, chosen_info.payload_type, false, target)) == .ok) {
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, candidate, chosen_info.payload_type, true, target)) == .ok) {
|
||||||
any_are_null = true;
|
any_are_null = true;
|
||||||
chosen = candidate;
|
chosen = candidate;
|
||||||
chosen_i = candidate_i + 1;
|
chosen_i = candidate_i + 1;
|
||||||
@ -1529,7 +1599,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.error_union_type => |chosen_info| {
|
.error_union_type => |chosen_info| {
|
||||||
if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) {
|
if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.payload_type, candidate, true, target)) == .ok) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1539,6 +1609,78 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
|
|||||||
return Index.none;
|
return Index.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(chosen == .none) return chosen;
|
||||||
|
const chosen_key = ip.indexToKey(chosen);
|
||||||
|
|
||||||
|
if (convert_to_slice) {
|
||||||
|
// turn *[N]T => []T
|
||||||
|
const chosen_elem_key = ip.indexToKey(chosen_key.pointer_type.elem_type);
|
||||||
|
var info = chosen_key.pointer_type;
|
||||||
|
info.sentinel = chosen_elem_key.sentinel();
|
||||||
|
info.size = .Slice;
|
||||||
|
info.is_const = seen_const or chosen_elem_key.isConstPtr();
|
||||||
|
info.elem_type = chosen_elem_key.elemType2();
|
||||||
|
|
||||||
|
const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info });
|
||||||
|
const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty;
|
||||||
|
return opt_ptr_ty;
|
||||||
|
// const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty;
|
||||||
|
// return try ip.get(gpa, .{ .error_union_type = .{
|
||||||
|
// .error_set_type = set_ty,
|
||||||
|
// .payload_type = opt_ptr_ty,
|
||||||
|
// } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seen_const) {
|
||||||
|
// turn []T => []const T
|
||||||
|
switch (chosen_key) {
|
||||||
|
.error_union_type => |error_union_info| {
|
||||||
|
var info: Pointer = ip.indexToKey(error_union_info.payload_type).pointer_type;
|
||||||
|
info.is_const = true;
|
||||||
|
|
||||||
|
const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info });
|
||||||
|
const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty;
|
||||||
|
// const set_ty = if(err_set_ty != .none) err_set_ty else error_union_info.error_set_type;
|
||||||
|
const set_ty = error_union_info.error_set_type;
|
||||||
|
return try ip.get(gpa, .{ .error_union_type = .{
|
||||||
|
.error_set_type = set_ty,
|
||||||
|
.payload_type = opt_ptr_ty,
|
||||||
|
} });
|
||||||
|
},
|
||||||
|
.pointer_type => |pointer_info| {
|
||||||
|
var info = pointer_info;
|
||||||
|
info.is_const = true;
|
||||||
|
|
||||||
|
const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info });
|
||||||
|
const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty;
|
||||||
|
return opt_ptr_ty;
|
||||||
|
// const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty;
|
||||||
|
// return try ip.get(gpa, .{ .error_union_type = .{
|
||||||
|
// .error_set_type = set_ty,
|
||||||
|
// .payload_type = opt_ptr_ty,
|
||||||
|
// } });
|
||||||
|
},
|
||||||
|
else => return chosen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any_are_null) {
|
||||||
|
const opt_ty = switch (chosen_key) {
|
||||||
|
.simple => |simple| switch (simple) {
|
||||||
|
.null_type => chosen,
|
||||||
|
else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }),
|
||||||
|
},
|
||||||
|
.optional_type => chosen,
|
||||||
|
else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }),
|
||||||
|
};
|
||||||
|
return opt_ty;
|
||||||
|
// const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ty;
|
||||||
|
// return try ip.get(gpa, .{ .error_union_type = .{
|
||||||
|
// .error_set_type = set_ty,
|
||||||
|
// .payload_type = opt_ty,
|
||||||
|
// } });
|
||||||
|
}
|
||||||
|
|
||||||
return chosen;
|
return chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1653,22 +1795,21 @@ const InMemoryCoercionResult = union(enum) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// If pointers have the same representation in runtime memory, a bitcast AIR instruction
|
/// If pointers have the same representation in runtime memory
|
||||||
/// may be used for the coercion.
|
|
||||||
/// * `const` attribute can be gained
|
/// * `const` attribute can be gained
|
||||||
/// * `volatile` attribute can be gained
|
/// * `volatile` attribute can be gained
|
||||||
/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut
|
/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if dest_is_const
|
||||||
/// * alignment can be decreased
|
/// * alignment can be decreased
|
||||||
/// * bit offset attributes must match exactly
|
/// * bit offset attributes must match exactly
|
||||||
/// * `*`/`[*]` must match exactly, but `[*c]` matches either one
|
/// * `*`/`[*]` must match exactly, but `[*c]` matches either one
|
||||||
/// * sentinel-terminated pointers can coerce into `[*]`
|
/// * sentinel-terminated pointers can coerce into `[*]`
|
||||||
fn coerceInMemoryAllowed(
|
fn coerceInMemoryAllowed(
|
||||||
ip: *InternPool,
|
ip: *InternPool,
|
||||||
// gpa: Allocator,
|
gpa: Allocator,
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
dest_ty: Index,
|
dest_ty: Index,
|
||||||
src_ty: Index,
|
src_ty: Index,
|
||||||
dest_is_mut: bool,
|
dest_is_const: bool,
|
||||||
target: std.Target,
|
target: std.Target,
|
||||||
) error{OutOfMemory}!InMemoryCoercionResult {
|
) error{OutOfMemory}!InMemoryCoercionResult {
|
||||||
if (dest_ty == src_ty) return .ok;
|
if (dest_ty == src_ty) return .ok;
|
||||||
@ -1679,168 +1820,157 @@ fn coerceInMemoryAllowed(
|
|||||||
const dest_tag = dest_key.zigTypeTag();
|
const dest_tag = dest_key.zigTypeTag();
|
||||||
const src_tag = src_key.zigTypeTag();
|
const src_tag = src_key.zigTypeTag();
|
||||||
|
|
||||||
// integers with the same number of bits.
|
if (dest_tag != src_tag) {
|
||||||
if (dest_tag == .Int and src_tag == .Int) {
|
return InMemoryCoercionResult{ .no_match = .{
|
||||||
const dest_info = dest_key.intInfo(target, ip);
|
.actual = dest_ty,
|
||||||
const src_info = src_key.intInfo(target, ip);
|
.wanted = src_ty,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok;
|
switch (dest_tag) {
|
||||||
|
.Int => {
|
||||||
|
const dest_info = dest_key.intInfo(target, ip);
|
||||||
|
const src_info = src_key.intInfo(target, ip);
|
||||||
|
|
||||||
|
if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok;
|
||||||
|
|
||||||
if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
|
|
||||||
// small enough unsigned ints can get casted to large enough signed ints
|
|
||||||
(dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or
|
|
||||||
(dest_info.signedness == .unsigned and src_info.signedness == .signed))
|
|
||||||
{
|
|
||||||
return InMemoryCoercionResult{ .int_not_coercible = .{
|
return InMemoryCoercionResult{ .int_not_coercible = .{
|
||||||
.actual_signedness = src_info.signedness,
|
.actual_signedness = src_info.signedness,
|
||||||
.wanted_signedness = dest_info.signedness,
|
.wanted_signedness = dest_info.signedness,
|
||||||
.actual_bits = src_info.bits,
|
.actual_bits = src_info.bits,
|
||||||
.wanted_bits = dest_info.bits,
|
.wanted_bits = dest_info.bits,
|
||||||
} };
|
} };
|
||||||
}
|
},
|
||||||
}
|
.Float => {
|
||||||
|
const dest_bits = dest_key.floatBits(target);
|
||||||
// floats with the same number of bits.
|
const src_bits = src_key.floatBits(target);
|
||||||
if (dest_tag == .Float and src_tag == .Float and
|
if (dest_bits == src_bits) return .ok;
|
||||||
// this is an optimization because only a long double can have the same size as a other Float
|
// TODO return float_not_coercible
|
||||||
// SAFETY: every Float is a Simple
|
return InMemoryCoercionResult{ .no_match = .{
|
||||||
dest_key.simple == .c_longdouble or src_key.simple == .c_longdouble)
|
.actual = dest_ty,
|
||||||
{
|
.wanted = src_ty,
|
||||||
const dest_bits = dest_key.floatBits(target);
|
|
||||||
const src_bits = src_key.floatBits(target);
|
|
||||||
if (dest_bits == src_bits) return .ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pointers / Pointer-like Optionals
|
|
||||||
const maybe_dest_ptr_ty = try ip.typePtrOrOptionalPtrTy(dest_ty);
|
|
||||||
const maybe_src_ptr_ty = try ip.typePtrOrOptionalPtrTy(src_ty);
|
|
||||||
if (maybe_dest_ptr_ty != Index.none and maybe_src_ptr_ty != Index.none) {
|
|
||||||
@panic("TODO: implement coerceInMemoryAllowedPtrs");
|
|
||||||
// return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, maybe_dest_ptr_ty, maybe_src_ptr_ty, dest_is_mut, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slices
|
|
||||||
if (dest_key.isSlice() and src_key.isSlice()) {
|
|
||||||
@panic("TODO: implement coerceInMemoryAllowedPtrs");
|
|
||||||
// return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
if (dest_tag == .Fn and src_tag == .Fn) {
|
|
||||||
return try ip.coerceInMemoryAllowedFns(arena, dest_key.function_type, src_key.function_type, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error Unions
|
|
||||||
if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
|
|
||||||
const dest_payload = dest_key.error_union_type.payload_type;
|
|
||||||
const src_payload = src_key.error_union_type.payload_type;
|
|
||||||
const child = try ip.coerceInMemoryAllowed(arena, dest_payload, src_payload, dest_is_mut, target);
|
|
||||||
if (child != .ok) {
|
|
||||||
return InMemoryCoercionResult{ .error_union_payload = .{
|
|
||||||
.child = try child.dupe(arena),
|
|
||||||
.actual = src_payload,
|
|
||||||
.wanted = dest_payload,
|
|
||||||
} };
|
} };
|
||||||
}
|
},
|
||||||
const dest_set = dest_key.error_union_type.error_set_type;
|
.Pointer => {
|
||||||
const src_set = src_key.error_union_type.error_set_type;
|
return try ip.coerceInMemoryAllowedPtrs(gpa, arena, dest_ty, src_ty, dest_key, src_key, dest_is_const, target);
|
||||||
return try ip.coerceInMemoryAllowed(arena, dest_set, src_set, dest_is_mut, target);
|
},
|
||||||
|
.Optional => {
|
||||||
|
// Pointer-like Optionals
|
||||||
|
const maybe_dest_ptr_ty = try ip.optionalPtrTy(dest_key);
|
||||||
|
const maybe_src_ptr_ty = try ip.optionalPtrTy(src_key);
|
||||||
|
if (maybe_dest_ptr_ty != .none and maybe_src_ptr_ty != .none) {
|
||||||
|
const dest_ptr_info = ip.indexToKey(maybe_dest_ptr_ty);
|
||||||
|
const src_ptr_info = ip.indexToKey(maybe_src_ptr_ty);
|
||||||
|
return try ip.coerceInMemoryAllowedPtrs(gpa, arena, dest_ty, src_ty, dest_ptr_info, src_ptr_info, dest_is_const, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maybe_dest_ptr_ty != maybe_src_ptr_ty) {
|
||||||
|
return InMemoryCoercionResult{ .optional_shape = .{
|
||||||
|
.actual = src_ty,
|
||||||
|
.wanted = dest_ty,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest_child_type = dest_key.optional_type.payload_type;
|
||||||
|
const src_child_type = src_key.optional_type.payload_type;
|
||||||
|
|
||||||
|
const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_child_type, src_child_type, dest_is_const, target);
|
||||||
|
if (child != .ok) {
|
||||||
|
return InMemoryCoercionResult{ .optional_child = .{
|
||||||
|
.child = try child.dupe(arena),
|
||||||
|
.actual = src_child_type,
|
||||||
|
.wanted = dest_child_type,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
return .ok;
|
||||||
|
},
|
||||||
|
.Fn => {
|
||||||
|
return try ip.coerceInMemoryAllowedFns(gpa, arena, dest_key.function_type, src_key.function_type, target);
|
||||||
|
},
|
||||||
|
.ErrorUnion => {
|
||||||
|
const dest_payload = dest_key.error_union_type.payload_type;
|
||||||
|
const src_payload = src_key.error_union_type.payload_type;
|
||||||
|
const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_payload, src_payload, dest_is_const, target);
|
||||||
|
if (child != .ok) {
|
||||||
|
return InMemoryCoercionResult{ .error_union_payload = .{
|
||||||
|
.child = try child.dupe(arena),
|
||||||
|
.actual = src_payload,
|
||||||
|
.wanted = dest_payload,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
const dest_set = dest_key.error_union_type.error_set_type;
|
||||||
|
const src_set = src_key.error_union_type.error_set_type;
|
||||||
|
return try ip.coerceInMemoryAllowed(gpa, arena, dest_set, src_set, dest_is_const, target);
|
||||||
|
},
|
||||||
|
.ErrorSet => {
|
||||||
|
return .ok;
|
||||||
|
// TODO: implement coerceInMemoryAllowedErrorSets
|
||||||
|
// return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
|
||||||
|
},
|
||||||
|
.Array => {
|
||||||
|
const dest_info = dest_key.array_type;
|
||||||
|
const src_info = src_key.array_type;
|
||||||
|
if (dest_info.len != src_info.len) {
|
||||||
|
return InMemoryCoercionResult{ .array_len = .{
|
||||||
|
.actual = src_info.len,
|
||||||
|
.wanted = dest_info.len,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.child, src_info.child, dest_is_const, target);
|
||||||
|
if (child != .ok) {
|
||||||
|
return InMemoryCoercionResult{ .array_elem = .{
|
||||||
|
.child = try child.dupe(arena),
|
||||||
|
.actual = src_info.child,
|
||||||
|
.wanted = dest_info.child,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ok_sent = dest_info.sentinel == Index.none or
|
||||||
|
(src_info.sentinel != Index.none and
|
||||||
|
dest_info.sentinel == src_info.sentinel // is this enough for a value equality check?
|
||||||
|
);
|
||||||
|
if (!ok_sent) {
|
||||||
|
return InMemoryCoercionResult{ .array_sentinel = .{
|
||||||
|
.actual = src_info.sentinel,
|
||||||
|
.wanted = dest_info.sentinel,
|
||||||
|
.ty = dest_info.child,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
return .ok;
|
||||||
|
},
|
||||||
|
.Vector => {
|
||||||
|
const dest_len = dest_key.vector_type.len;
|
||||||
|
const src_len = src_key.vector_type.len;
|
||||||
|
|
||||||
|
if (dest_len != src_len) {
|
||||||
|
return InMemoryCoercionResult{ .vector_len = .{
|
||||||
|
.actual = src_len,
|
||||||
|
.wanted = dest_len,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest_elem_ty = dest_key.vector_type.child;
|
||||||
|
const src_elem_ty = src_key.vector_type.child;
|
||||||
|
const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_elem_ty, src_elem_ty, dest_is_const, target);
|
||||||
|
if (child != .ok) {
|
||||||
|
return InMemoryCoercionResult{ .vector_elem = .{
|
||||||
|
.child = try child.dupe(arena),
|
||||||
|
.actual = src_elem_ty,
|
||||||
|
.wanted = dest_elem_ty,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
return .ok;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
return InMemoryCoercionResult{ .no_match = .{
|
||||||
|
.actual = dest_ty,
|
||||||
|
.wanted = src_ty,
|
||||||
|
} };
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error Sets
|
|
||||||
if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
|
|
||||||
return .ok; // TODO: implement coerceInMemoryAllowedErrorSets
|
|
||||||
// return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrays
|
|
||||||
if (dest_tag == .Array and src_tag == .Array) {
|
|
||||||
const dest_info = dest_key.array_type;
|
|
||||||
const src_info = src_key.array_type;
|
|
||||||
if (dest_info.len != src_info.len) {
|
|
||||||
return InMemoryCoercionResult{ .array_len = .{
|
|
||||||
.actual = src_info.len,
|
|
||||||
.wanted = dest_info.len,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
const child = try ip.coerceInMemoryAllowed(arena, dest_info.child, src_info.child, dest_is_mut, target);
|
|
||||||
if (child != .ok) {
|
|
||||||
return InMemoryCoercionResult{ .array_elem = .{
|
|
||||||
.child = try child.dupe(arena),
|
|
||||||
.actual = src_info.child,
|
|
||||||
.wanted = dest_info.child,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
const ok_sent = dest_info.sentinel == Index.none or
|
|
||||||
(src_info.sentinel != Index.none and
|
|
||||||
dest_info.sentinel == src_info.sentinel // is this enough for a value equality check?
|
|
||||||
);
|
|
||||||
if (!ok_sent) {
|
|
||||||
return InMemoryCoercionResult{ .array_sentinel = .{
|
|
||||||
.actual = src_info.sentinel,
|
|
||||||
.wanted = dest_info.sentinel,
|
|
||||||
.ty = dest_info.child,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
return .ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vectors
|
|
||||||
if (dest_tag == .Vector and src_tag == .Vector) {
|
|
||||||
const dest_len = dest_key.vector_type.len;
|
|
||||||
const src_len = src_key.vector_type.len;
|
|
||||||
|
|
||||||
if (dest_len != src_len) {
|
|
||||||
return InMemoryCoercionResult{ .vector_len = .{
|
|
||||||
.actual = src_len,
|
|
||||||
.wanted = dest_len,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
const dest_elem_ty = dest_key.vector_type.child;
|
|
||||||
const src_elem_ty = src_key.vector_type.child;
|
|
||||||
const child = try ip.coerceInMemoryAllowed(arena, dest_elem_ty, src_elem_ty, dest_is_mut, target);
|
|
||||||
if (child != .ok) {
|
|
||||||
return InMemoryCoercionResult{ .vector_elem = .{
|
|
||||||
.child = try child.dupe(arena),
|
|
||||||
.actual = src_elem_ty,
|
|
||||||
.wanted = dest_elem_ty,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
return .ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionals
|
|
||||||
if (dest_tag == .Optional and src_tag == .Optional) {
|
|
||||||
if (maybe_dest_ptr_ty != maybe_src_ptr_ty) {
|
|
||||||
return InMemoryCoercionResult{ .optional_shape = .{
|
|
||||||
.actual = src_ty,
|
|
||||||
.wanted = dest_ty,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
const dest_child_type = dest_key.optional_type.payload_type;
|
|
||||||
const src_child_type = src_key.optional_type.payload_type;
|
|
||||||
|
|
||||||
const child = try ip.coerceInMemoryAllowed(arena, dest_child_type, src_child_type, dest_is_mut, target);
|
|
||||||
if (child != .ok) {
|
|
||||||
return InMemoryCoercionResult{ .optional_child = .{
|
|
||||||
.child = try child.dupe(arena),
|
|
||||||
.actual = src_child_type,
|
|
||||||
.wanted = dest_child_type,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
return .ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InMemoryCoercionResult{ .no_match = .{
|
|
||||||
.actual = dest_ty,
|
|
||||||
.wanted = src_ty,
|
|
||||||
} };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn coerceInMemoryAllowedErrorSets(
|
// fn coerceInMemoryAllowedErrorSets(
|
||||||
@ -1996,6 +2126,7 @@ fn coerceInMemoryAllowed(
|
|||||||
|
|
||||||
fn coerceInMemoryAllowedFns(
|
fn coerceInMemoryAllowedFns(
|
||||||
ip: *InternPool,
|
ip: *InternPool,
|
||||||
|
gpa: std.mem.Allocator,
|
||||||
arena: std.mem.Allocator,
|
arena: std.mem.Allocator,
|
||||||
dest_info: Fn,
|
dest_info: Fn,
|
||||||
src_info: Fn,
|
src_info: Fn,
|
||||||
@ -2020,7 +2151,7 @@ fn coerceInMemoryAllowedFns(
|
|||||||
const is_noreturn = return_type_key == .simple and return_type_key.simple == .noreturn;
|
const is_noreturn = return_type_key == .simple and return_type_key.simple == .noreturn;
|
||||||
|
|
||||||
if (!is_noreturn) {
|
if (!is_noreturn) {
|
||||||
const rt = try ip.coerceInMemoryAllowed(arena, dest_info.return_type, src_info.return_type, false, target);
|
const rt = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.return_type, src_info.return_type, true, target);
|
||||||
if (rt != .ok) {
|
if (rt != .ok) {
|
||||||
return InMemoryCoercionResult{ .fn_return_type = .{
|
return InMemoryCoercionResult{ .fn_return_type = .{
|
||||||
.child = try rt.dupe(arena),
|
.child = try rt.dupe(arena),
|
||||||
@ -2057,7 +2188,7 @@ fn coerceInMemoryAllowedFns(
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// // Note: Cast direction is reversed here.
|
// // Note: Cast direction is reversed here.
|
||||||
// const param = try ip.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
|
// const param = try ip.coerceInMemoryAllowed(gpa, src_param_ty, dest_param_ty, true, target);
|
||||||
// if (param != .ok) {
|
// if (param != .ok) {
|
||||||
// return InMemoryCoercionResult{ .fn_param = .{
|
// return InMemoryCoercionResult{ .fn_param = .{
|
||||||
// .child = try param.dupe(arena),
|
// .child = try param.dupe(arena),
|
||||||
@ -2071,22 +2202,129 @@ fn coerceInMemoryAllowedFns(
|
|||||||
return .ok;
|
return .ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For pointer-like optionals, it returns the pointer type. For pointers,
|
fn coerceInMemoryAllowedPtrs(
|
||||||
/// the type is returned unmodified.
|
ip: *InternPool,
|
||||||
/// This can return `error.AnalysisFail` because it sometimes requires resolving whether
|
gpa: std.mem.Allocator,
|
||||||
/// a type has zero bits, which can cause a "foo depends on itself" compile error.
|
arena: std.mem.Allocator,
|
||||||
/// This logic must be kept in sync with `Type.isPtrLikeOptional`.
|
dest_ty: Index,
|
||||||
fn typePtrOrOptionalPtrTy(
|
src_ty: Index,
|
||||||
ip: InternPool,
|
dest_ptr_info: Key,
|
||||||
ty: Index,
|
src_ptr_info: Key,
|
||||||
) !Index {
|
dest_is_const: bool,
|
||||||
const key = ip.indexToKey(ty);
|
target: std.Target,
|
||||||
switch (key) {
|
) !InMemoryCoercionResult {
|
||||||
.pointer_type => |pointer_info| switch (pointer_info.size) {
|
const dest_info = dest_ptr_info.pointer_type;
|
||||||
.Slice => return Index.none,
|
const src_info = src_ptr_info.pointer_type;
|
||||||
else => return ty,
|
|
||||||
},
|
|
||||||
|
|
||||||
|
const ok_ptr_size = src_info.size == dest_info.size or
|
||||||
|
src_info.size == .C or dest_info.size == .C;
|
||||||
|
if (!ok_ptr_size) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_size = .{
|
||||||
|
.actual = src_info.size,
|
||||||
|
.wanted = dest_info.size,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ok_cv_qualifiers =
|
||||||
|
(!src_info.is_const or dest_info.is_const) and
|
||||||
|
(!src_info.is_volatile or dest_info.is_volatile);
|
||||||
|
|
||||||
|
if (!ok_cv_qualifiers) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_qualifiers = .{
|
||||||
|
.actual_const = src_info.is_const,
|
||||||
|
.wanted_const = dest_info.is_const,
|
||||||
|
.actual_volatile = src_info.is_volatile,
|
||||||
|
.wanted_volatile = dest_info.is_volatile,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest_info.address_space != src_info.address_space) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_addrspace = .{
|
||||||
|
.actual = src_info.address_space,
|
||||||
|
.wanted = dest_info.address_space,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.elem_type, src_info.elem_type, dest_info.is_const, target);
|
||||||
|
if (child != .ok) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_child = .{
|
||||||
|
.child = try child.dupe(arena),
|
||||||
|
.actual = src_info.elem_type,
|
||||||
|
.wanted = dest_info.elem_type,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest_allow_zero = dest_ptr_info.ptrAllowsZero(ip);
|
||||||
|
const src_allow_zero = src_ptr_info.ptrAllowsZero(ip);
|
||||||
|
|
||||||
|
const ok_allows_zero = (dest_allow_zero and (src_allow_zero or dest_is_const)) or (!dest_allow_zero and !src_allow_zero);
|
||||||
|
if (!ok_allows_zero) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_allowzero = .{
|
||||||
|
.actual = src_ty,
|
||||||
|
.wanted = dest_ty,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (src_info.host_size != dest_info.host_size or
|
||||||
|
// src_info.bit_offset != dest_info.bit_offset)
|
||||||
|
// {
|
||||||
|
// return InMemoryCoercionResult{ .ptr_bit_range = .{
|
||||||
|
// .actual_host = src_info.host_size,
|
||||||
|
// .wanted_host = dest_info.host_size,
|
||||||
|
// .actual_offset = src_info.bit_offset,
|
||||||
|
// .wanted_offset = dest_info.bit_offset,
|
||||||
|
// } };
|
||||||
|
// }
|
||||||
|
|
||||||
|
const ok_sent = dest_info.sentinel == .none or src_info.size == .C or dest_info.sentinel == src_info.sentinel; // is this enough for a value equality check?
|
||||||
|
if (!ok_sent) {
|
||||||
|
return InMemoryCoercionResult{ .ptr_sentinel = .{
|
||||||
|
.actual = if(src_info.sentinel != .none) src_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}),
|
||||||
|
.wanted = if(dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}),
|
||||||
|
.ty = dest_info.elem_type,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both pointers have alignment 0, it means they both want ABI alignment.
|
||||||
|
// In this case, if they share the same child type, no need to resolve
|
||||||
|
// pointee type alignment. Otherwise both pointee types must have their alignment
|
||||||
|
// resolved and we compare the alignment numerically.
|
||||||
|
alignment: {
|
||||||
|
if (src_info.alignment == 0 and dest_info.alignment == 0 and
|
||||||
|
dest_info.elem_type == src_info.elem_type // is this enough for a value equality check?
|
||||||
|
)
|
||||||
|
{
|
||||||
|
break :alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const src_align = if (src_info.alignment != 0)
|
||||||
|
// src_info.alignment
|
||||||
|
// else
|
||||||
|
// src_info.elem_type.abiAlignment(target);
|
||||||
|
|
||||||
|
// const dest_align = if (dest_info.alignment != 0)
|
||||||
|
// dest_info.alignment
|
||||||
|
// else
|
||||||
|
// dest_info.elem_type.abiAlignment(target);
|
||||||
|
|
||||||
|
// if (dest_align > src_align) {
|
||||||
|
// return InMemoryCoercionResult{ .ptr_alignment = .{
|
||||||
|
// .actual = src_align,
|
||||||
|
// .wanted = dest_align,
|
||||||
|
// } };
|
||||||
|
// }
|
||||||
|
|
||||||
|
break :alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optionalPtrTy(
|
||||||
|
ip: InternPool,
|
||||||
|
ty: Key,
|
||||||
|
) !Index {
|
||||||
|
switch (ty) {
|
||||||
.optional_type => |optional_info| {
|
.optional_type => |optional_info| {
|
||||||
const child_type = optional_info.payload_type;
|
const child_type = optional_info.payload_type;
|
||||||
const child_key = ip.indexToKey(child_type);
|
const child_key = ip.indexToKey(child_type);
|
||||||
@ -2108,8 +2346,7 @@ fn typePtrOrOptionalPtrTy(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
else => unreachable,
|
||||||
else => return Index.none,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2196,32 +2433,34 @@ test "pointer type" {
|
|||||||
try std.testing.expect(i32_type_0 == i32_type_1);
|
try std.testing.expect(i32_type_0 == i32_type_1);
|
||||||
try std.testing.expect(i32_type_0 != u32_type);
|
try std.testing.expect(i32_type_0 != u32_type);
|
||||||
|
|
||||||
var ptr: Pointer = .{
|
const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
.elem_type = undefined,
|
.elem_type = i32_type_0,
|
||||||
.sentinel = Index.none,
|
.size = .One,
|
||||||
.alignment = 0, // TODO
|
} });
|
||||||
.size = std.builtin.Type.Pointer.Size.One,
|
const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
.is_const = false,
|
.elem_type = i32_type_0,
|
||||||
.is_volatile = false,
|
.size = .One,
|
||||||
.is_allowzero = false,
|
} });
|
||||||
.address_space = std.builtin.AddressSpace.generic,
|
const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
};
|
.elem_type = i32_type_1,
|
||||||
|
.size = .One,
|
||||||
ptr.elem_type = i32_type_0;
|
} });
|
||||||
const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = ptr });
|
const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
ptr.elem_type = i32_type_0;
|
.elem_type = u32_type,
|
||||||
const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = ptr });
|
.size = .One,
|
||||||
ptr.elem_type = i32_type_1;
|
} });
|
||||||
const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = ptr });
|
|
||||||
ptr.elem_type = u32_type;
|
|
||||||
const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr });
|
|
||||||
|
|
||||||
try std.testing.expect(i32_pointer_type_0 == i32_pointer_type_1);
|
try std.testing.expect(i32_pointer_type_0 == i32_pointer_type_1);
|
||||||
try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2);
|
try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2);
|
||||||
try std.testing.expect(i32_pointer_type_0 != u32_pointer_type);
|
try std.testing.expect(i32_pointer_type_0 != u32_pointer_type);
|
||||||
|
|
||||||
ptr.is_const = true;
|
const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr });
|
.elem_type = u32_type,
|
||||||
|
.size = .One,
|
||||||
|
.is_const = true,
|
||||||
|
} });
|
||||||
|
|
||||||
|
try std.testing.expect(const_u32_pointer_type != u32_pointer_type);
|
||||||
|
|
||||||
try testExpectFmtType(&ip, i32_pointer_type_0, "*i32");
|
try testExpectFmtType(&ip, i32_pointer_type_0, "*i32");
|
||||||
try testExpectFmtType(&ip, u32_pointer_type, "*u32");
|
try testExpectFmtType(&ip, u32_pointer_type, "*u32");
|
||||||
@ -2263,12 +2502,10 @@ test "array type" {
|
|||||||
const i32_3_array_type_0 = try ip.get(gpa, .{ .array_type = .{
|
const i32_3_array_type_0 = try ip.get(gpa, .{ .array_type = .{
|
||||||
.len = 3,
|
.len = 3,
|
||||||
.child = i32_type_0,
|
.child = i32_type_0,
|
||||||
.sentinel = Index.none,
|
|
||||||
} });
|
} });
|
||||||
const i32_3_array_type_1 = try ip.get(gpa, .{ .array_type = .{
|
const i32_3_array_type_1 = try ip.get(gpa, .{ .array_type = .{
|
||||||
.len = 3,
|
.len = 3,
|
||||||
.child = i32_type_1,
|
.child = i32_type_1,
|
||||||
.sentinel = Index.none,
|
|
||||||
} });
|
} });
|
||||||
const u32_0_0_array_type = try ip.get(gpa, .{ .array_type = .{
|
const u32_0_0_array_type = try ip.get(gpa, .{ .array_type = .{
|
||||||
.len = 3,
|
.len = 3,
|
||||||
@ -2379,6 +2616,9 @@ test "resolvePeerTypes integers and floats" {
|
|||||||
|
|
||||||
try ip.testResolvePeerTypes(comptime_float_type, comptime_int_type, comptime_float_type);
|
try ip.testResolvePeerTypes(comptime_float_type, comptime_int_type, comptime_float_type);
|
||||||
|
|
||||||
|
try ip.testResolvePeerTypes(f16_type, f32_type, f32_type);
|
||||||
|
try ip.testResolvePeerTypes(f32_type, f64_type, f64_type);
|
||||||
|
|
||||||
try ip.testResolvePeerTypes(comptime_int_type, f16_type, f16_type);
|
try ip.testResolvePeerTypes(comptime_int_type, f16_type, f16_type);
|
||||||
try ip.testResolvePeerTypes(comptime_int_type, f32_type, f32_type);
|
try ip.testResolvePeerTypes(comptime_int_type, f32_type, f32_type);
|
||||||
try ip.testResolvePeerTypes(comptime_int_type, f64_type, f64_type);
|
try ip.testResolvePeerTypes(comptime_int_type, f64_type, f64_type);
|
||||||
@ -2404,12 +2644,66 @@ test "resolvePeerTypes integers and floats" {
|
|||||||
try ip.testResolvePeerTypes(bool_type, f32_type, Index.none);
|
try ip.testResolvePeerTypes(bool_type, f32_type, Index.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "resolvePeerTypes pointers" {
|
||||||
|
const gpa = std.testing.allocator;
|
||||||
|
|
||||||
|
var ip: InternPool = .{};
|
||||||
|
defer ip.deinit(gpa);
|
||||||
|
|
||||||
|
const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } });
|
||||||
|
const comptime_int_type = try ip.get(gpa, .{ .simple = .comptime_int });
|
||||||
|
const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float });
|
||||||
|
const bool_type = try ip.get(gpa, .{ .simple = .bool });
|
||||||
|
|
||||||
|
const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } });
|
||||||
|
|
||||||
|
const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{
|
||||||
|
.len = 1,
|
||||||
|
.child = u32_type,
|
||||||
|
.sentinel = Index.none,
|
||||||
|
} });
|
||||||
|
const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{
|
||||||
|
.len = 2,
|
||||||
|
.child = u32_type,
|
||||||
|
.sentinel = Index.none,
|
||||||
|
} });
|
||||||
|
|
||||||
|
const @"*[1]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[1]u32", .size = .One } });
|
||||||
|
const @"*[2]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[2]u32", .size = .One } });
|
||||||
|
|
||||||
|
const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } });
|
||||||
|
|
||||||
|
const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{
|
||||||
|
.elem_type = u32_type,
|
||||||
|
.size = .One,
|
||||||
|
.is_const = true,
|
||||||
|
} });
|
||||||
|
|
||||||
|
const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } });
|
||||||
|
|
||||||
|
const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice } });
|
||||||
|
|
||||||
|
try ip.testResolvePeerTypes(@"[*c]u32", comptime_int_type, @"[*c]u32");
|
||||||
|
try ip.testResolvePeerTypes(@"[*c]u32", u32_type, @"[*c]u32");
|
||||||
|
try ip.testResolvePeerTypes(@"[*c]u32", comptime_float_type, Index.none);
|
||||||
|
try ip.testResolvePeerTypes(@"[*c]u32", bool_type, Index.none);
|
||||||
|
|
||||||
|
try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32");
|
||||||
|
try ip.testResolvePeerTypes(@"[]u32", @"*[2]u32", @"[]u32");
|
||||||
|
|
||||||
|
try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32");
|
||||||
|
try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32");
|
||||||
|
try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32");
|
||||||
|
|
||||||
|
try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32");
|
||||||
|
}
|
||||||
|
|
||||||
fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void {
|
fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void {
|
||||||
try ip.testResolvePeerTypesInOrder(a, b, expected);
|
try ip.testResolvePeerTypesInOrder(a, b, expected);
|
||||||
try ip.testResolvePeerTypesInOrder(b, a, expected);
|
try ip.testResolvePeerTypesInOrder(b, a, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void {
|
fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void {
|
||||||
const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{lhs, rhs}, builtin.target);
|
const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{ lhs, rhs }, builtin.target);
|
||||||
try std.testing.expectEqual(expected, actual);
|
try std.testing.expectEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user