Commit ce65ca4345

Andrew Kelley <andrew@ziglang.org>
2021-10-24 21:06:29
stage2: refactor coercePeerTypes and fix C ptr cmp with null
1 parent f7b090d
Changed files (2)
src/Sema.zig
@@ -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 });
 }
 
+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(
     sema: *Sema,
     block: *Block,
@@ -4636,19 +4640,23 @@ fn zirOptionalPayload(
     const src = inst_data.src();
     const operand = sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
-    const opt_type = operand_ty;
-    if (opt_type.zigTypeTag() != .Optional) {
-        return sema.fail(block, src, "expected optional type, found {}", .{opt_type});
-    }
-
-    const child_type = try opt_type.optionalChildAlloc(sema.arena);
+    const result_ty = switch (operand_ty.zigTypeTag()) {
+        .Optional => try operand_ty.optionalChildAlloc(sema.arena),
+        .Pointer => t: {
+            if (operand_ty.ptrSize() != .C) {
+                return sema.failWithExpectedOptionalType(block, src, operand_ty);
+            }
+            break :t operand_ty;
+        },
+        else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
+    };
 
     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
         if (val.isNull()) {
             return sema.fail(block, src, "unable to unwrap null", .{});
         }
         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);
@@ -4656,7 +4664,7 @@ fn zirOptionalPayload(
         const is_non_null = try block.addUnOp(.is_non_null, operand);
         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
@@ -8135,11 +8143,13 @@ fn zirCmpEq(
         rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
     {
         // 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);
     }
     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) {
         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 chosen_ty_tag = chosen_ty.zigTypeTag();
 
-        if (candidate_ty_tag == .NoReturn)
-            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;
-        }
+        switch (candidate_ty_tag) {
+            .NoReturn, .Undefined => continue,
 
-        if (chosen_ty_tag == .ComptimeInt and candidate_ty.isInt()) {
-            chosen = candidate;
-            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;
-        }
+            .Null => {
+                any_are_null = true;
+                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;
+            .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 => {},
         }
 
-        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;
-            }
-            if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
-                any_are_null = true;
+        switch (chosen_ty_tag) {
+            .NoReturn, .Undefined => {
                 chosen = candidate;
                 chosen_i = candidate_i + 1;
                 continue;
-            }
-        }
-        if (candidate_ty_tag == .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) {
+            },
+            .Null => {
+                any_are_null = true;
                 chosen = candidate;
                 chosen_i = candidate_i + 1;
                 continue;
-            }
-            if (coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target) == .ok) {
-                any_are_null = true;
-                continue;
-            }
+            },
+            .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;
+                }
+                if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
+                    any_are_null = true;
+                    chosen = candidate;
+                    chosen_i = candidate_i + 1;
+                    continue;
+                }
+            },
+            else => {},
         }
 
         // At this point, we hit a compile error. We need to recover
src/value.zig
@@ -1790,12 +1790,31 @@ pub const Value = extern union {
         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 {
         return switch (self.tag()) {
             .null_value => true,
             .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,
             .unreachable_value => unreachable,
             .inferred_alloc => unreachable,