stage2: refactor coercePeerTypes and fix C ptr cmp with null

This commit is contained in:
Andrew Kelley 2021-10-24 12:06:29 -07:00
parent f7b090d707
commit ce65ca4345
2 changed files with 155 additions and 126 deletions

View file

@ -1233,6 +1233,10 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T
return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
} }
fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError {
return sema.fail(block, src, "expected optional type, found {}", .{optional_ty});
}
fn failWithErrorSetCodeMissing( fn failWithErrorSetCodeMissing(
sema: *Sema, sema: *Sema,
block: *Block, block: *Block,
@ -4636,19 +4640,23 @@ fn zirOptionalPayload(
const src = inst_data.src(); const src = inst_data.src();
const operand = sema.resolveInst(inst_data.operand); const operand = sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand); const operand_ty = sema.typeOf(operand);
const opt_type = operand_ty; const result_ty = switch (operand_ty.zigTypeTag()) {
if (opt_type.zigTypeTag() != .Optional) { .Optional => try operand_ty.optionalChildAlloc(sema.arena),
return sema.fail(block, src, "expected optional type, found {}", .{opt_type}); .Pointer => t: {
} if (operand_ty.ptrSize() != .C) {
return sema.failWithExpectedOptionalType(block, src, operand_ty);
const child_type = try opt_type.optionalChildAlloc(sema.arena); }
break :t operand_ty;
},
else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
};
if (try sema.resolveDefinedValue(block, src, operand)) |val| { if (try sema.resolveDefinedValue(block, src, operand)) |val| {
if (val.isNull()) { if (val.isNull()) {
return sema.fail(block, src, "unable to unwrap null", .{}); return sema.fail(block, src, "unable to unwrap null", .{});
} }
const sub_val = val.castTag(.opt_payload).?.data; const sub_val = val.castTag(.opt_payload).?.data;
return sema.addConstant(child_type, sub_val); return sema.addConstant(result_ty, sub_val);
} }
try sema.requireRuntimeBlock(block, src); try sema.requireRuntimeBlock(block, src);
@ -4656,7 +4664,7 @@ fn zirOptionalPayload(
const is_non_null = try block.addUnOp(.is_non_null, operand); const is_non_null = try block.addUnOp(.is_non_null, operand);
try sema.addSafetyCheck(block, is_non_null, .unwrap_null); try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
} }
return block.addTyOp(.optional_payload, child_type, operand); return block.addTyOp(.optional_payload, result_ty, operand);
} }
/// Value in, value out /// Value in, value out
@ -8135,11 +8143,13 @@ fn zirCmpEq(
rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
{ {
// comparing null with optionals // comparing null with optionals
const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; const opt_operand = if (lhs_ty_tag == .Null) rhs else lhs;
return sema.analyzeIsNull(block, src, opt_operand, op == .neq); return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
} }
if (((lhs_ty_tag == .Null and rhs_ty.isCPtr()) or (rhs_ty_tag == .Null and lhs_ty.isCPtr()))) { if (((lhs_ty_tag == .Null and rhs_ty.isCPtr()) or (rhs_ty_tag == .Null and lhs_ty.isCPtr()))) {
return sema.fail(block, src, "TODO implement C pointer cmp", .{}); // comparing null with C pointers
const opt_operand = if (lhs_ty_tag == .Null) rhs else lhs;
return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
} }
if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
@ -13598,127 +13608,127 @@ fn resolvePeerTypes(
const candidate_ty_tag = candidate_ty.zigTypeTag(); const candidate_ty_tag = candidate_ty.zigTypeTag();
const chosen_ty_tag = chosen_ty.zigTypeTag(); const chosen_ty_tag = chosen_ty.zigTypeTag();
if (candidate_ty_tag == .NoReturn) switch (candidate_ty_tag) {
continue; .NoReturn, .Undefined => continue,
if (chosen_ty_tag == .NoReturn) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (candidate_ty_tag == .Undefined)
continue;
if (chosen_ty_tag == .Undefined) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty.isInt() and
candidate_ty.isInt() and
chosen_ty.isSignedInt() == candidate_ty.isSignedInt())
{
if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) {
chosen = candidate;
chosen_i = candidate_i + 1;
}
continue;
}
if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
chosen = candidate;
chosen_i = candidate_i + 1;
}
continue;
}
if (chosen_ty_tag == .ComptimeInt and candidate_ty.isInt()) { .Null => {
chosen = candidate; any_are_null = true;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty.isInt() and candidate_ty_tag == .ComptimeInt) {
continue;
}
if ((chosen_ty_tag == .ComptimeFloat or chosen_ty_tag == .ComptimeInt) and
candidate_ty.isRuntimeFloat())
{
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty.isRuntimeFloat() and
(candidate_ty_tag == .ComptimeFloat or candidate_ty_tag == .ComptimeInt))
{
continue;
}
if (chosen_ty_tag == .Enum and candidate_ty_tag == .EnumLiteral) {
continue;
}
if (chosen_ty_tag == .EnumLiteral and candidate_ty_tag == .Enum) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() == .C and
(candidate_ty_tag == .Int or candidate_ty_tag == .ComptimeInt))
{
continue;
}
if (candidate_ty_tag == .Pointer and candidate_ty.ptrSize() == .C and
(chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt))
{
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty_tag == .ComptimeFloat and candidate_ty_tag == .ComptimeInt)
continue;
if (chosen_ty_tag == .ComptimeInt and candidate_ty_tag == .ComptimeFloat) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty_tag == .Null) {
any_are_null = true;
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (candidate_ty_tag == .Null) {
any_are_null = true;
continue;
}
if (chosen_ty_tag == .Optional) {
var opt_child_buf: Type.Payload.ElemType = undefined;
const opt_child_ty = chosen_ty.optionalChild(&opt_child_buf);
if (coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target) == .ok) {
continue; continue;
} },
if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
.Int => switch (chosen_ty_tag) {
.ComptimeInt => {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
},
.Int => {
if (chosen_ty.isSignedInt() == candidate_ty.isSignedInt()) {
if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) {
chosen = candidate;
chosen_i = candidate_i + 1;
}
continue;
}
},
.Pointer => if (chosen_ty.ptrSize() == .C) continue,
else => {},
},
.ComptimeInt => switch (chosen_ty_tag) {
.Int, .Float, .ComptimeFloat => continue,
.Pointer => if (chosen_ty.ptrSize() == .C) continue,
else => {},
},
.Float => switch (chosen_ty_tag) {
.Float => {
if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
chosen = candidate;
chosen_i = candidate_i + 1;
}
continue;
},
.ComptimeFloat, .ComptimeInt => {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
},
else => {},
},
.ComptimeFloat => switch (chosen_ty_tag) {
.Float => continue,
.ComptimeInt => {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
},
else => {},
},
.Enum => switch (chosen_ty_tag) {
.EnumLiteral => {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
},
else => {},
},
.EnumLiteral => switch (chosen_ty_tag) {
.Enum => continue,
else => {},
},
.Pointer => {
if (candidate_ty.ptrSize() == .C) {
if (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() != .Slice) {
continue;
}
}
},
.Optional => {
var opt_child_buf: Type.Payload.ElemType = undefined;
const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf);
if (coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target) == .ok) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target) == .ok) {
any_are_null = true;
continue;
}
},
else => {},
}
switch (chosen_ty_tag) {
.NoReturn, .Undefined => {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
},
.Null => {
any_are_null = true; any_are_null = true;
chosen = candidate; chosen = candidate;
chosen_i = candidate_i + 1; chosen_i = candidate_i + 1;
continue; continue;
} },
} .Optional => {
if (candidate_ty_tag == .Optional) { var opt_child_buf: Type.Payload.ElemType = undefined;
var opt_child_buf: Type.Payload.ElemType = undefined; const opt_child_ty = chosen_ty.optionalChild(&opt_child_buf);
const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf); if (coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target) == .ok) {
if (coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target) == .ok) { continue;
chosen = candidate; }
chosen_i = candidate_i + 1; if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
continue; any_are_null = true;
} chosen = candidate;
if (coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target) == .ok) { chosen_i = candidate_i + 1;
any_are_null = true; continue;
continue; }
} },
else => {},
} }
// At this point, we hit a compile error. We need to recover // At this point, we hit a compile error. We need to recover

View file

@ -1790,12 +1790,31 @@ pub const Value = extern union {
return self.tag() == .undef; return self.tag() == .undef;
} }
/// Valid for all types. Asserts the value is not undefined and not unreachable. /// Asserts the value is not undefined and not unreachable.
/// Integer value 0 is considered null because of C pointers.
pub fn isNull(self: Value) bool { pub fn isNull(self: Value) bool {
return switch (self.tag()) { return switch (self.tag()) {
.null_value => true, .null_value => true,
.opt_payload => false, .opt_payload => false,
// If it's not one of those two tags then it must be a C pointer value,
// in which case the value 0 is null and other values are non-null.
.zero,
.bool_false,
.the_only_possible_value,
=> true,
.one,
.bool_true,
=> false,
.int_u64,
.int_i64,
.int_big_positive,
.int_big_negative,
=> compareWithZero(self, .eq),
.undef => unreachable, .undef => unreachable,
.unreachable_value => unreachable, .unreachable_value => unreachable,
.inferred_alloc => unreachable, .inferred_alloc => unreachable,