From f5a67dba08b81c7109c52f6ad957aefdd646b56e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Jul 2018 01:46:49 -0400 Subject: [PATCH] self-hosted: implicit cast comptime ints to other ints we now have successful exit codes from main linking against libc --- src-self-hosted/ir.zig | 39 ++++++++++++++++++++++++++- src-self-hosted/value.zig | 49 ++++++++++++++++++++++++++++++++++ std/math/big/int.zig | 18 ++++++++++++- test/stage2/compile_errors.zig | 6 +++++ 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 259c0a99f..04023980e 100644 --- a/src-self-hosted/ir.zig +++ b/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) //{ diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index b8563b00b..16c148883 100644 --- a/src-self-hosted/value.zig +++ b/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); diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 8b9e89254..9af033bd0 100644 --- a/std/math/big/int.zig +++ b/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) { diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig index d360c07b2..2cecd7865 100644 --- a/test/stage2/compile_errors.zig +++ b/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'"); }