Commit 2cdbb5f472
Changed files (8)
lib/std/math/big/int.zig
@@ -237,7 +237,7 @@ pub const Int = struct {
return bits;
}
- fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
+ pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
if (self.eqZero()) {
return true;
}
lib/std/target.zig
@@ -761,7 +761,7 @@ pub const Target = struct {
};
}
- pub fn ptrBitWidth(arch: Arch) u32 {
+ pub fn ptrBitWidth(arch: Arch) u16 {
switch (arch) {
.avr,
.msp430,
src/ir.cpp
@@ -11289,9 +11289,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
ir_add_error_node(ira, instruction->base.source_node,
- buf_sprintf("integer value %s has no representation in type '%s'",
- buf_ptr(val_buf),
- buf_ptr(&other_type->name)));
+ buf_sprintf("type %s cannot represent integer value %s",
+ buf_ptr(&other_type->name),
+ buf_ptr(val_buf)));
return false;
}
if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {
src-self-hosted/ir/text.zig
@@ -28,6 +28,7 @@ pub const Inst = struct {
@"export",
primitive,
fntype,
+ intcast,
};
pub fn TagToType(tag: Tag) type {
@@ -44,6 +45,7 @@ pub const Inst = struct {
.@"export" => Export,
.primitive => Primitive,
.fntype => FnType,
+ .intcast => IntCast,
};
}
@@ -243,6 +245,17 @@ pub const Inst = struct {
cc: std.builtin.CallingConvention = .Unspecified,
},
};
+
+ pub const IntCast = struct {
+ pub const base_tag = Tag.intcast;
+ base: Inst,
+
+ positionals: struct {
+ dest_type: *Inst,
+ value: *Inst,
+ },
+ kw_args: struct {},
+ };
};
pub const ErrorMsg = struct {
@@ -315,6 +328,7 @@ pub const Module = struct {
.@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
.primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
.fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
+ .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
}
}
src-self-hosted/c_int.zig
@@ -1,169 +0,0 @@
-const Target = @import("std").Target;
-
-pub const CInt = struct {
- id: Id,
- zig_name: []const u8,
- c_name: []const u8,
- is_signed: bool,
-
- pub const Id = enum {
- Short,
- UShort,
- Int,
- UInt,
- Long,
- ULong,
- LongLong,
- ULongLong,
- };
-
- pub const list = [_]CInt{
- CInt{
- .id = .Short,
- .zig_name = "c_short",
- .c_name = "short",
- .is_signed = true,
- },
- CInt{
- .id = .UShort,
- .zig_name = "c_ushort",
- .c_name = "unsigned short",
- .is_signed = false,
- },
- CInt{
- .id = .Int,
- .zig_name = "c_int",
- .c_name = "int",
- .is_signed = true,
- },
- CInt{
- .id = .UInt,
- .zig_name = "c_uint",
- .c_name = "unsigned int",
- .is_signed = false,
- },
- CInt{
- .id = .Long,
- .zig_name = "c_long",
- .c_name = "long",
- .is_signed = true,
- },
- CInt{
- .id = .ULong,
- .zig_name = "c_ulong",
- .c_name = "unsigned long",
- .is_signed = false,
- },
- CInt{
- .id = .LongLong,
- .zig_name = "c_longlong",
- .c_name = "long long",
- .is_signed = true,
- },
- CInt{
- .id = .ULongLong,
- .zig_name = "c_ulonglong",
- .c_name = "unsigned long long",
- .is_signed = false,
- },
- };
-
- pub fn sizeInBits(cint: CInt, self: Target) u32 {
- const arch = self.cpu.arch;
- switch (self.os.tag) {
- .freestanding, .other => switch (self.cpu.arch) {
- .msp430 => switch (cint.id) {
- .Short,
- .UShort,
- .Int,
- .UInt,
- => return 16,
- .Long,
- .ULong,
- => return 32,
- .LongLong,
- .ULongLong,
- => return 64,
- },
- else => switch (cint.id) {
- .Short,
- .UShort,
- => return 16,
- .Int,
- .UInt,
- => return 32,
- .Long,
- .ULong,
- => return self.cpu.arch.ptrBitWidth(),
- .LongLong,
- .ULongLong,
- => return 64,
- },
- },
-
- .linux,
- .macosx,
- .freebsd,
- .openbsd,
- => switch (cint.id) {
- .Short,
- .UShort,
- => return 16,
- .Int,
- .UInt,
- => return 32,
- .Long,
- .ULong,
- => return self.cpu.arch.ptrBitWidth(),
- .LongLong,
- .ULongLong,
- => return 64,
- },
-
- .windows, .uefi => switch (cint.id) {
- .Short,
- .UShort,
- => return 16,
- .Int,
- .UInt,
- => return 32,
- .Long,
- .ULong,
- .LongLong,
- .ULongLong,
- => return 64,
- },
-
- .ananas,
- .cloudabi,
- .dragonfly,
- .fuchsia,
- .ios,
- .kfreebsd,
- .lv2,
- .netbsd,
- .solaris,
- .haiku,
- .minix,
- .rtems,
- .nacl,
- .cnk,
- .aix,
- .cuda,
- .nvcl,
- .amdhsa,
- .ps4,
- .elfiamcu,
- .tvos,
- .watchos,
- .mesa3d,
- .contiki,
- .amdpal,
- .hermit,
- .hurd,
- .wasi,
- .emscripten,
- => @panic("TODO specify the C integer type sizes for this OS"),
- }
- }
-};
src-self-hosted/ir.zig
@@ -6,6 +6,7 @@ const Type = @import("type.zig").Type;
const assert = std.debug.assert;
const text = @import("ir/text.zig");
const BigInt = std.math.big.Int;
+const Target = std.Target;
/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
/// of instructions that correspond to the ZIR text format.
@@ -99,6 +100,8 @@ pub const ErrorMsg = struct {
};
pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
+ const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
+
var ctx = Analyze{
.allocator = allocator,
.arena = std.heap.ArenaAllocator.init(allocator),
@@ -107,6 +110,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
.decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
.exports = std.ArrayList(Module.Export).init(allocator),
.fns = std.ArrayList(Module.Fn).init(allocator),
+ .target = native_info.target,
};
defer ctx.errors.deinit();
defer ctx.decl_table.deinit();
@@ -135,6 +139,7 @@ const Analyze = struct {
decl_table: std.AutoHashMap(*text.Inst, NewDecl),
exports: std.ArrayList(Module.Export),
fns: std.ArrayList(Module.Fn),
+ target: Target,
const NewDecl = struct {
/// null means a semantic analysis error happened
@@ -336,6 +341,7 @@ const Analyze = struct {
.@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
.fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
+ .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
}
}
@@ -402,6 +408,38 @@ const Analyze = struct {
return self.coerce(dest_type, new_inst);
}
+ fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
+ const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
+ const new_inst = try self.resolveInst(func, intcast.positionals.value);
+
+ const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
+ .ComptimeInt => true,
+ .Int => false,
+ else => return self.fail(
+ intcast.positionals.dest_type.src,
+ "expected integer type, found '{}'",
+ .{
+ dest_type,
+ },
+ ),
+ };
+
+ switch (new_inst.ty.zigTypeTag()) {
+ .ComptimeInt, .Int => {},
+ else => return self.fail(
+ intcast.positionals.value.src,
+ "expected integer type, found '{}'",
+ .{new_inst.ty},
+ ),
+ }
+
+ if (dest_is_comptime_int or new_inst.value() != null) {
+ return self.coerce(dest_type, new_inst);
+ }
+
+ return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
+ }
+
fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
if (in_memory_result == .ok) {
@@ -420,6 +458,17 @@ const Analyze = struct {
return self.coerceArrayPtrToSlice(dest_type, inst);
}
}
+
+ // comptime_int to fixed-width integer
+ if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
+ // The representation is already correct; we only need to make sure it fits in the destination type.
+ const val = inst.value().?; // comptime_int always has comptime known value
+ if (!val.intFitsInType(dest_type, self.target)) {
+ return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
+ }
+ return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
+ }
+
return self.fail(inst.src, "TODO implement type coercion", .{});
}
src-self-hosted/type.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const Value = @import("value.zig").Value;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
+const Target = std.Target;
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
/// It's important for this struct to be small.
@@ -333,6 +334,44 @@ pub const Type = extern union {
};
}
+ /// Asserts the type is a fixed-width integer.
+ pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
+ return switch (self.tag()) {
+ .@"f16",
+ .@"f32",
+ .@"f64",
+ .@"f128",
+ .@"c_longdouble",
+ .@"c_void",
+ .@"bool",
+ .@"void",
+ .@"type",
+ .@"anyerror",
+ .@"comptime_int",
+ .@"comptime_float",
+ .@"noreturn",
+ .fn_naked_noreturn_no_args,
+ .array,
+ .single_const_pointer,
+ .array_u8_sentinel_0,
+ .const_slice_u8,
+ => unreachable,
+
+ .@"u8" => .{ .signed = false, .bits = 8 },
+ .@"i8" => .{ .signed = true, .bits = 8 },
+ .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
+ .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
+ .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
+ .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
+ .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
+ .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
+ .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
+ .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
+ .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
+ .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
+ };
+ }
+
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
@@ -401,3 +440,126 @@ pub const Type = extern union {
};
};
};
+
+pub const CInteger = enum {
+ short,
+ ushort,
+ int,
+ uint,
+ long,
+ ulong,
+ longlong,
+ ulonglong,
+
+ pub fn sizeInBits(self: CInteger, target: Target) u16 {
+ const arch = target.cpu.arch;
+ switch (target.os.tag) {
+ .freestanding, .other => switch (target.cpu.arch) {
+ .msp430 => switch (self) {
+ .short,
+ .ushort,
+ .int,
+ .uint,
+ => return 16,
+ .long,
+ .ulong,
+ => return 32,
+ .longlong,
+ .ulonglong,
+ => return 64,
+ },
+ else => switch (self) {
+ .short,
+ .ushort,
+ => return 16,
+ .int,
+ .uint,
+ => return 32,
+ .long,
+ .ulong,
+ => return target.cpu.arch.ptrBitWidth(),
+ .longlong,
+ .ulonglong,
+ => return 64,
+ },
+ },
+
+ .linux,
+ .macosx,
+ .freebsd,
+ .netbsd,
+ .dragonfly,
+ .openbsd,
+ .wasi,
+ .emscripten,
+ => switch (self) {
+ .short,
+ .ushort,
+ => return 16,
+ .int,
+ .uint,
+ => return 32,
+ .long,
+ .ulong,
+ => return target.cpu.arch.ptrBitWidth(),
+ .longlong,
+ .ulonglong,
+ => return 64,
+ },
+
+ .windows, .uefi => switch (self) {
+ .short,
+ .ushort,
+ => return 16,
+ .int,
+ .uint,
+ .long,
+ .ulong,
+ => return 32,
+ .longlong,
+ .ulonglong,
+ => return 64,
+ },
+
+ .ios => switch (self) {
+ .short,
+ .ushort,
+ => return 16,
+ .int,
+ .uint,
+ => return 32,
+ .long,
+ .ulong,
+ .longlong,
+ .ulonglong,
+ => return 64,
+ },
+
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .tvos,
+ .watchos,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ => @panic("TODO specify the C integer type sizes for this OS"),
+ }
+ }
+};
src-self-hosted/value.zig
@@ -3,6 +3,7 @@ const Type = @import("type.zig").Type;
const log2 = std.math.log2;
const assert = std.debug.assert;
const BigInt = std.math.big.Int;
+const Target = std.Target;
/// This is the raw data, with no bookkeeping, no memory awareness,
/// no de-duplication, and no type system awareness.
@@ -198,6 +199,78 @@ pub const Value = extern union {
};
}
+ /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
+ pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
+ switch (self.tag()) {
+ .ty,
+ .u8_type,
+ .i8_type,
+ .isize_type,
+ .usize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .fn_naked_noreturn_no_args_type,
+ .const_slice_u8_type,
+ .void_value,
+ .noreturn_value,
+ .bool_true,
+ .bool_false,
+ .function,
+ .ref,
+ .bytes,
+ => unreachable,
+
+ .int_u64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.cast(Payload.Int_u64).?.int;
+ const info = ty.intInfo(target);
+ const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
+ return info.bits >= needed_bits;
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_i64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.cast(Payload.Int_i64).?.int;
+ const info = ty.intInfo(target);
+ if (!info.signed and x < 0)
+ return false;
+ @panic("TODO implement i64 intFitsInType");
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_big => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ }
+ }
+
/// This type is not copyable since it may contain pointers to its inner data.
pub const Payload = struct {
tag: Tag,