Commit f5a67dba08

Andrew Kelley <superjoe30@gmail.com>
2018-07-20 07:46:49
self-hosted: implicit cast comptime ints to other ints
we now have successful exit codes from main linking against libc
1 parent 33fbd8c
Changed files (4)
src-self-hosted
std
math
test
src-self-hosted/ir.zig
@@ -139,7 +139,15 @@ pub const Inst = struct {
         }
     }
 
+    fn copyVal(base: *Inst, comp: *Compilation) !*Value {
+        if (base.parent.?.ref_count == 0) {
+            return base.val.KnownValue.derefAndCopy(comp);
+        }
+        return base.val.KnownValue.copy(comp);
+    }
+
     fn getAsParam(param: *Inst) !*Inst {
+        param.ref_count -= 1;
         const child = param.child orelse return error.SemanticAnalysisFailed;
         switch (child.val) {
             IrVal.Unknown => return error.SemanticAnalysisFailed,
@@ -1715,8 +1723,37 @@ const Analyze = struct {
         //    }
         //}
 
+        // cast from comptime-known integer to another integer where the value fits
+        if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: {
+            const target_val = target.val.KnownValue;
+            const from_int = &target_val.cast(Value.Int).?.big_int;
+            const fits = fits: {
+                if (dest_type.cast(Type.ComptimeInt)) |ctint| {
+                    break :fits true;
+                }
+                if (dest_type.cast(Type.Int)) |int| {
+                    break :fits (from_int.positive or from_int.eqZero() or int.key.is_signed) and
+                        int.key.bit_count >= from_int.bitcount();
+                }
+                break :cast;
+            };
+            if (!fits) {
+                try ira.addCompileError(
+                    source_instr.span,
+                    "integer value '{}' cannot be stored in type '{}'",
+                    from_int,
+                    dest_type.name,
+                );
+                return error.SemanticAnalysisFailed;
+            }
+
+            const new_val = try target.copyVal(ira.irb.comp);
+            new_val.setType(dest_type, ira.irb.comp);
+            return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val);
+        }
+
         // cast from number literal to another type
-        //// cast from number literal to *const integer
+        // cast from number literal to *const integer
         //if (actual_type->id == TypeTableEntryIdComptimeFloat ||
         //    actual_type->id == TypeTableEntryIdComptimeInt)
         //{
src-self-hosted/value.zig
@@ -5,6 +5,7 @@ const Compilation = @import("compilation.zig").Compilation;
 const ObjectFile = @import("codegen.zig").ObjectFile;
 const llvm = @import("llvm.zig");
 const Buffer = std.Buffer;
+const assert = std.debug.assert;
 
 /// Values are ref-counted, heap-allocated, and copy-on-write
 /// If there is only 1 ref then write need not copy
@@ -34,6 +35,12 @@ pub const Value = struct {
         }
     }
 
+    pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
+        base.typeof.base.deref(comp);
+        new_type.base.ref();
+        base.typeof = new_type;
+    }
+
     pub fn getRef(base: *Value) *Value {
         base.ref();
         return base;
@@ -60,6 +67,28 @@ pub const Value = struct {
         }
     }
 
+    pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
+        if (self.ref_count.get() == 1) {
+            // ( ͡° ͜ʖ ͡°)
+            return self;
+        }
+
+        assert(self.ref_count.decr() != 1);
+        return self.copy(comp);
+    }
+
+    pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
+        switch (base.id) {
+            Id.Type => unreachable,
+            Id.Fn => unreachable,
+            Id.Void => unreachable,
+            Id.Bool => unreachable,
+            Id.NoReturn => unreachable,
+            Id.Ptr => unreachable,
+            Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
+        }
+    }
+
     pub const Id = enum {
         Type,
         Fn,
@@ -256,6 +285,26 @@ pub const Value = struct {
             }
         }
 
+        pub fn copy(old: *Int, comp: *Compilation) !*Int {
+            old.base.typeof.base.ref();
+            errdefer old.base.typeof.base.deref(comp);
+
+            const new = try comp.gpa().create(Value.Int{
+                .base = Value{
+                    .id = Value.Id.Int,
+                    .typeof = old.base.typeof,
+                    .ref_count = std.atomic.Int(usize).init(1),
+                },
+                .big_int = undefined,
+            });
+            errdefer comp.gpa().destroy(new);
+
+            new.big_int = try old.big_int.clone();
+            errdefer new.big_int.deinit();
+
+            return new;
+        }
+
         pub fn destroy(self: *Int, comp: *Compilation) void {
             self.big_int.deinit();
             comp.gpa().destroy(self);
std/math/big/int.zig
@@ -118,7 +118,7 @@ pub const Int = struct {
 
     fn bitcount(self: Int) usize {
         const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
-        return usize(@boolToInt(!self.positive)) + u_bit_count;
+        return @boolToInt(!self.positive) + u_bit_count;
     }
 
     pub fn sizeInBase(self: Int, base: usize) usize {
@@ -275,6 +275,7 @@ pub const Int = struct {
         self.positive = positive;
     }
 
+    /// TODO make this call format instead of the other way around
     pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
         if (base < 2 or base > 16) {
             return error.InvalidBase;
@@ -357,6 +358,21 @@ pub const Int = struct {
         return s;
     }
 
+    /// for the std lib format function
+    /// TODO make this non-allocating
+    pub fn format(
+        self: Int,
+        comptime fmt: []const u8,
+        context: var,
+        comptime FmtError: type,
+        output: fn (@typeOf(context), []const u8) FmtError!void,
+    ) FmtError!void {
+        // TODO look at fmt and support other bases
+        const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
+        defer self.allocator.free(str);
+        return output(context, str);
+    }
+
     // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
     pub fn cmpAbs(a: Int, b: Int) i8 {
         if (a.len < b.len) {
test/stage2/compile_errors.zig
@@ -21,4 +21,10 @@ pub fn addCases(ctx: *TestContext) !void {
         \\    defer return;
         \\}
     , "1.zig", 2, 11, "cannot return from defer expression");
+
+    try ctx.testCompileError(
+        \\export fn entry() c_int {
+        \\    return 36893488147419103232;
+        \\}
+    , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
 }