From c1d3330d91c3debeb4139747d45817df95d146ae Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Nov 2023 14:46:50 +0200 Subject: [PATCH 1/2] Type: begin working on an interner --- src/aro/Type.zig | 332 ++-------------------------------- src/aro/Type/Interner.zig | 362 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 313 deletions(-) create mode 100644 src/aro/Type/Interner.zig diff --git a/src/aro/Type.zig b/src/aro/Type.zig index af59c60b..49edb374 100644 --- a/src/aro/Type.zig +++ b/src/aro/Type.zig @@ -1,13 +1,14 @@ const std = @import("std"); -const Tree = @import("Tree.zig"); -const TokenIndex = Tree.TokenIndex; -const NodeIndex = Tree.NodeIndex; -const Parser = @import("Parser.zig"); -const Compilation = @import("Compilation.zig"); const Attribute = @import("Attribute.zig"); +const Compilation = @import("Compilation.zig"); +const Interner = @import("Type/Interner.zig"); +const Parser = @import("Parser.zig"); const StringInterner = @import("StringInterner.zig"); const StringId = StringInterner.StringId; const target_util = @import("target.zig"); +const Tree = @import("Tree.zig"); +const TokenIndex = Tree.TokenIndex; +const NodeIndex = Tree.NodeIndex; const LangOpts = @import("LangOpts.zig"); pub const Qualifiers = packed struct { @@ -16,9 +17,6 @@ pub const Qualifiers = packed struct { @"volatile": bool = false, restrict: bool = false, - // for function parameters only, stored here since it fits in the padding - register: bool = false, - pub fn any(quals: Qualifiers) bool { return quals.@"const" or quals.restrict or quals.@"volatile" or quals.atomic; } @@ -28,7 +26,6 @@ pub const Qualifiers = packed struct { if (quals.atomic) try w.writeAll("_Atomic "); if (quals.@"volatile") try w.writeAll("volatile "); if (quals.restrict) try w.writeAll("restrict "); - if (quals.register) try w.writeAll("register "); } /// Merge the const/volatile qualifiers, used by type resolution @@ -47,7 +44,6 @@ pub const Qualifiers = packed struct { .atomic = a.atomic or b.atomic, .@"volatile" = a.@"volatile" or b.@"volatile", .restrict = a.restrict or b.restrict, - .register = a.register or b.register, }; } @@ -59,14 +55,6 @@ pub const Qualifiers = packed struct { return true; } - /// register is a storage class and not actually a qualifier - /// so it is not preserved by typeof() - pub fn inheritFromTypeof(quals: Qualifiers) Qualifiers { - var res = quals; - res.register = false; - return res; - } - pub const Builder = struct { @"const": ?TokenIndex = null, atomic: ?TokenIndex = null, @@ -91,102 +79,19 @@ pub const Qualifiers = packed struct { }; }; -// TODO improve memory usage -pub const Func = struct { - return_type: Type, - params: []Param, - - pub const Param = struct { - ty: Type, - name: StringId, - name_tok: TokenIndex, - }; - - fn eql(a: *const Func, b: *const Func, a_spec: Specifier, b_spec: Specifier, comp: *const Compilation) bool { - // return type cannot have qualifiers - if (!a.return_type.eql(b.return_type, comp, false)) return false; - if (a.params.len == 0 and b.params.len == 0) return true; - - if (a.params.len != b.params.len) { - if (a_spec == .old_style_func or b_spec == .old_style_func) { - const maybe_has_params = if (a_spec == .old_style_func) b else a; - for (maybe_has_params.params) |param| { - if (param.ty.undergoesDefaultArgPromotion(comp)) return false; - } - return true; - } - return false; - } - if ((a_spec == .func) != (b_spec == .func)) return false; - // TODO validate this - for (a.params, b.params) |param, b_qual| { - var a_unqual = param.ty; - a_unqual.qual.@"const" = false; - a_unqual.qual.@"volatile" = false; - var b_unqual = b_qual.ty; - b_unqual.qual.@"const" = false; - b_unqual.qual.@"volatile" = false; - if (!a_unqual.eql(b_unqual, comp, true)) return false; - } - return true; - } -}; - -pub const Array = struct { - len: u64, - elem: Type, -}; - -pub const Expr = struct { - node: NodeIndex, - ty: Type, -}; - -pub const Attributed = struct { - attributes: []Attribute, - base: Type, - - pub fn create(allocator: std.mem.Allocator, base: Type, existing_attributes: []const Attribute, attributes: []const Attribute) !*Attributed { - const attributed_type = try allocator.create(Attributed); - errdefer allocator.destroy(attributed_type); - - const all_attrs = try allocator.alloc(Attribute, existing_attributes.len + attributes.len); - @memcpy(all_attrs[0..existing_attributes.len], existing_attributes); - @memcpy(all_attrs[existing_attributes.len..], attributes); - - attributed_type.* = .{ - .attributes = all_attrs, - .base = base, +pub const Signedness = enum { + signed, + unsigned, + unspecified, + platform_dependant, + + pub fn unwrap(s: Signedness, comp: *const Compilation) std.builtin.Signedness { + return switch (s) { + .signed => .signed, + .unsigned => .unsigned, + .unspecified => .signed, + .platform_dependant => comp.getCharSignedness(), }; - return attributed_type; - } -}; - -// TODO improve memory usage -pub const Enum = struct { - fields: []Field, - tag_ty: Type, - name: StringId, - fixed: bool, - - pub const Field = struct { - ty: Type, - name: StringId, - name_tok: TokenIndex, - node: NodeIndex, - }; - - pub fn isIncomplete(e: Enum) bool { - return e.fields.len == std.math.maxInt(usize); - } - - pub fn create(allocator: std.mem.Allocator, name: StringId, fixed_ty: ?Type) !*Enum { - var e = try allocator.create(Enum); - e.name = name; - e.fields.len = std.math.maxInt(usize); - if (fixed_ty) |some| e.tag_ty = some; - e.fixed = fixed_ty != null; - return e; } }; @@ -236,208 +141,9 @@ pub const FieldLayout = struct { } }; -// TODO improve memory usage -pub const Record = struct { - fields: []Field, - type_layout: TypeLayout, - /// If this is null, none of the fields have attributes - /// Otherwise, it's a pointer to N items (where N == number of fields) - /// and the item at index i is the attributes for the field at index i - field_attributes: ?[*][]const Attribute, - name: StringId, - - pub const Field = struct { - ty: Type, - name: StringId, - /// zero for anonymous fields - name_tok: TokenIndex = 0, - bit_width: ?u32 = null, - layout: FieldLayout = .{ - .offset_bits = 0, - .size_bits = 0, - }, - - pub fn isNamed(f: *const Field) bool { - return f.name_tok != 0; - } - - pub fn isAnonymousRecord(f: Field) bool { - return !f.isNamed() and f.ty.isRecord(); - } - - /// false for bitfields - pub fn isRegularField(f: *const Field) bool { - return f.bit_width == null; - } - - /// bit width as specified in the C source. Asserts that `f` is a bitfield. - pub fn specifiedBitWidth(f: *const Field) u32 { - return f.bit_width.?; - } - }; - - pub fn isIncomplete(r: Record) bool { - return r.fields.len == std.math.maxInt(usize); - } - - pub fn create(allocator: std.mem.Allocator, name: StringId) !*Record { - var r = try allocator.create(Record); - r.name = name; - r.fields.len = std.math.maxInt(usize); - r.field_attributes = null; - r.type_layout = .{ - .size_bits = 8, - .field_alignment_bits = 8, - .pointer_alignment_bits = 8, - .required_alignment_bits = 8, - }; - return r; - } - - pub fn hasFieldOfType(self: *const Record, ty: Type, comp: *const Compilation) bool { - if (self.isIncomplete()) return false; - for (self.fields) |f| { - if (ty.eql(f.ty, comp, false)) return true; - } - return false; - } -}; - -pub const Specifier = enum { - /// A NaN-like poison value - invalid, - - /// GNU auto type - /// This is a placeholder specifier - it must be replaced by the actual type specifier (determined by the initializer) - auto_type, - /// C23 auto, behaves like auto_type - c23_auto, - - void, - bool, - - // integers - char, - schar, - uchar, - short, - ushort, - int, - uint, - long, - ulong, - long_long, - ulong_long, - int128, - uint128, - complex_char, - complex_schar, - complex_uchar, - complex_short, - complex_ushort, - complex_int, - complex_uint, - complex_long, - complex_ulong, - complex_long_long, - complex_ulong_long, - complex_int128, - complex_uint128, - - // data.int - bit_int, - complex_bit_int, - - // floating point numbers - fp16, - float16, - float, - double, - long_double, - float80, - float128, - complex_float, - complex_double, - complex_long_double, - complex_float80, - complex_float128, - - // data.sub_type - pointer, - unspecified_variable_len_array, - // data.func - /// int foo(int bar, char baz) and int (void) - func, - /// int foo(int bar, char baz, ...) - var_args_func, - /// int foo(bar, baz) and int foo() - /// is also var args, but we can give warnings about incorrect amounts of parameters - old_style_func, - - // data.array - array, - static_array, - incomplete_array, - vector, - // data.expr - variable_len_array, - - // data.record - @"struct", - @"union", - - // data.enum - @"enum", - - /// typeof(type-name) - typeof_type, - - /// typeof(expression) - typeof_expr, - - /// data.attributed - attributed, - - /// C23 nullptr_t - nullptr_t, -}; - const Type = @This(); -/// All fields of Type except data may be mutated -data: union { - sub_type: *Type, - func: *Func, - array: *Array, - expr: *Expr, - @"enum": *Enum, - record: *Record, - attributed: *Attributed, - none: void, - int: struct { - bits: u16, - signedness: std.builtin.Signedness, - }, -} = .{ .none = {} }, -specifier: Specifier, -qual: Qualifiers = .{}, -decayed: bool = false, - -pub const int = Type{ .specifier = .int }; -pub const invalid = Type{ .specifier = .invalid }; - -/// Determine if type matches the given specifier, recursing into typeof -/// types if necessary. -pub fn is(ty: Type, specifier: Specifier) bool { - std.debug.assert(specifier != .typeof_type and specifier != .typeof_expr); - return ty.get(specifier) != null; -} - -pub fn withAttributes(self: Type, allocator: std.mem.Allocator, attributes: []const Attribute) !Type { - if (attributes.len == 0) return self; - const attributed_type = try Type.Attributed.create(allocator, self, self.getAttributes(), attributes); - return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed }; -} +ref: Interner.Ref, pub fn isCallable(ty: Type) ?Type { return switch (ty.specifier) { diff --git a/src/aro/Type/Interner.zig b/src/aro/Type/Interner.zig new file mode 100644 index 00000000..bbdbe23c --- /dev/null +++ b/src/aro/Type/Interner.zig @@ -0,0 +1,362 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const Hash = std.hash.Wyhash; +const Attribute = @import("../Attribute.zig"); +const Compilation = @import("Compilation.zig"); +const StringInterner = @import("../StringInterner.zig"); +const StringId = StringInterner.StringId; +const Tree = @import("../Tree.zig"); +const TokenIndex = Tree.TokenIndex; +const NodeIndex = Tree.NodeIndex; +const Type = @import("../Type.zig"); + +const Interner = @This(); + +map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, +items: std.MultiArrayList(struct { + tag: Tag, + data: u32, +}) = .{}, +extra: std.ArrayListUnmanaged(u32) = .{}, +named: struct { + wchar: Type, + uint_least16_t: Type, + uint_least32_t: Type, + ptrdiff: Type, + size: Type, + va_list: Type, + pid_t: Type, + ns_constant_string: Type, + file: Type, + jmp_buf: Type, + sigjmp_buf: Type, + ucontext_t: Type, + intmax: Type, + intptr: Type, + int16: Type, + int64: Type, +}, + +const KeyAdapter = struct { + interner: *const Interner, + + pub fn eql(adapter: KeyAdapter, a: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + return adapter.interner.get(@as(Ref, @enumFromInt(b_map_index))).eql(a); + } + + pub fn hash(adapter: KeyAdapter, a: Key) u32 { + _ = adapter; + return a.hash(); + } +}; + +pub const Key = union(enum) { + /// A NaN-like poison value + invalid, + + /// GNU auto type + /// This is a placeholder specifier - it must be replaced by the actual type specifier (determined by the initializer) + auto_type: Type.Qualifiers, + /// C23 auto, behaves like auto_type + c23_auto: Type.Qualifiers, + + /// C23 nullptr_t + nullptr_t: Type.Qualifiers, + + /// _Bool / bool + bool: Type.Qualifiers, + + void: Type.Qualifiers, + + int: Int, + complex_int: Int, + imaginary_int: Int, + + float: Float, + complex_float: Float, + imaginary_float: Float, + + /// int foo(int bar, char baz) and int (void) + func: Func, + /// int foo(int bar, char baz, ...) + var_args_func: Func, + /// int foo(bar, baz) and int foo() + /// is also var args, but we can give warnings about incorrect amounts of parameters + old_style_func: Func, + + pointer: Pointer, + unspecified_variable_len_array: Pointer, + + array: Array, + static_array: Array, + incomplete_array: Array, + vector: Array, + variable_len_array: Expr, + + @"struct": Record, + @"union": Record, + + @"enum": Enum, + + /// typeof(type-name) + typeof_type: struct { + qual: Type.Qualifiers, + ty: Type, + }, + /// typeof(expression) + typeof_expr: Expr, + + typedef: struct { + qual: Type.Qualifiers, + ty: Type, + }, + + decayed: struct { + qual: Type.Qualifiers, + ty: Type, + }, + + attributed: struct { + qual: Type.Qualifiers, + ty: Type, + attributes: []const Attribute, + }, + + pub const Int = struct { + qual: Type.Qualifiers, + signedness: Type.Signedness, + bits: u16, + name: enum { + char, + short, + int, + long, + long_long, + int128, + bit_int, + }, + }; + + pub const Float = struct { + qual: Type.Qualifiers, + bits: u16, + name: enum { + fp16, + float16, + float, + double, + long_double, + float80, + float128, + }, + }; + + pub const Func = struct { + return_type: Type, + params: []const Param, + + pub const Param = struct { + ty: Type, + name: StringId, + name_tok: TokenIndex, + }; + }; + + pub const Pointer = struct { + qual: Type.Qualifiers, + ty: Type, + }; + + pub const Array = struct { + qual: Type.Qualifiers, + elem: Type, + len: u64, + }; + + pub const Expr = struct { + qual: Type.Qualifiers, + ty: Type, + node: NodeIndex, + }; + + pub const Record = struct { + fields: []Field, + type_layout: Type.TypeLayout, + field_attributes: []const []const Attribute, + name: StringId, + + pub const Field = struct { + ty: Type, + name: StringId, + /// zero for anonymous fields + name_tok: TokenIndex = 0, + bit_width: ?u32 = null, + layout: Type.FieldLayout = .{ + .offset_bits = 0, + .size_bits = 0, + }, + + pub fn isNamed(f: *const Field) bool { + return f.name_tok != 0; + } + + pub fn isAnonymousRecord(f: Field) bool { + return !f.isNamed() and f.ty.isRecord(); + } + + /// false for bitfields + pub fn isRegularField(f: *const Field) bool { + return f.bit_width == null; + } + + /// bit width as specified in the C source. Asserts that `f` is a bitfield. + pub fn specifiedBitWidth(f: *const Field) u32 { + return f.bit_width.?; + } + }; + + pub fn isIncomplete(r: Record) bool { + return r.fields.len == std.math.maxInt(usize); + } + + pub fn hasFieldOfType(self: *const Record, ty: Type, comp: *const Compilation) bool { + if (self.isIncomplete()) return false; + for (self.fields) |f| { + if (ty.eql(f.ty, comp, false)) return true; + } + return false; + } + }; + + pub const Enum = struct { + fields: []Field, + tag_ty: Type, + name: StringId, + fixed: bool, + + pub const Field = struct { + ty: Type, + name: StringId, + name_tok: TokenIndex, + node: NodeIndex, + }; + + pub fn isIncomplete(e: Enum) bool { + return e.fields.len == std.math.maxInt(usize); + } + }; + + pub fn hash(key: Key) u32 { + var hasher = Hash.init(0); + const tag = std.meta.activeTag(key); + std.hash.autoHash(&hasher, tag); + switch (key) { + inline else => |info| { + std.hash.autoHash(&hasher, info); + }, + } + return @truncate(hasher.final()); + } + + pub fn eql(a: Key, b: Key) bool { + const KeyTag = std.meta.Tag(Key); + const a_tag: KeyTag = a; + const b_tag: KeyTag = b; + if (a_tag != b_tag) return false; + switch (a) { + inline else => |a_info, tag| { + const b_info = @field(b, @tagName(tag)); + return std.meta.eql(a_info, b_info); + }, + } + } + + fn toRef(key: Key) ?Ref { + switch (key) { + else => {}, + } + return null; + } +}; + +pub const Ref = enum(u32) { + invalid = std.math.maxInt(u32), + _, +}; + +pub const Tag = enum(u8) {}; + +pub fn deinit(i: *Interner, gpa: Allocator) void { + i.map.deinit(gpa); + i.items.deinit(gpa); + i.extra.deinit(gpa); +} + +pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { + if (key.toRef()) |some| return some; + const adapter: KeyAdapter = .{ .interner = i }; + const gop = try i.map.getOrPutAdapted(gpa, key, adapter); + if (gop.found_existing) return @enumFromInt(gop.index); + try i.items.ensureUnusedCapacity(gpa, 1); + + switch (key) {} + + return @enumFromInt(gop.index); +} + +fn addExtra(i: *Interner, gpa: Allocator, extra: anytype) Allocator.Error!u32 { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + try i.extra.ensureUnusedCapacity(gpa, fields.len); + return i.addExtraAssumeCapacity(extra); +} + +fn addExtraAssumeCapacity(i: *Interner, extra: anytype) u32 { + const result = @as(u32, @intCast(i.extra.items.len)); + inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| { + i.extra.appendAssumeCapacity(switch (field.type) { + Ref => @intFromEnum(@field(extra, field.name)), + u32 => @field(extra, field.name), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }); + } + return result; +} + +pub fn get(i: *const Interner, ref: Ref) Key { + return i.getExtra(ref, .deep); +} + +pub fn getExtra(i: *const Interner, ref: Ref, search: enum { deep, shallow }) Key { + _ = search; + switch (ref) { + else => {}, + } + + while (true) { + const item = i.items.get(@intFromEnum(ref)); + switch (item.tag) {} + } +} + +fn extraData(i: *const Interner, comptime T: type, index: usize) T { + return i.extraDataTrail(T, index).data; +} + +fn extraDataTrail(i: *const Interner, comptime T: type, index: usize) struct { data: T, end: u32 } { + var result: T = undefined; + const fields = @typeInfo(T).Struct.fields; + inline for (fields, 0..) |field, field_i| { + const int32 = i.extra.items[field_i + index]; + @field(result, field.name) = switch (field.type) { + Ref => @enumFromInt(int32), + u32 => int32, + else => @compileError("bad field type: " ++ @typeName(field.type)), + }; + } + return .{ + .data = result, + .end = @intCast(index + fields.len), + }; +} From 9ab24df921199b2d005540608b3d793c2bbfea3b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Nov 2023 23:01:39 +0200 Subject: [PATCH 2/2] wip --- src/aro/Type.zig | 12 +-- src/aro/Type/Interner.zig | 165 +++++++++++++++++++++++++++++++------- 2 files changed, 136 insertions(+), 41 deletions(-) diff --git a/src/aro/Type.zig b/src/aro/Type.zig index 49edb374..404c38c5 100644 --- a/src/aro/Type.zig +++ b/src/aro/Type.zig @@ -83,16 +83,6 @@ pub const Signedness = enum { signed, unsigned, unspecified, - platform_dependant, - - pub fn unwrap(s: Signedness, comp: *const Compilation) std.builtin.Signedness { - return switch (s) { - .signed => .signed, - .unsigned => .unsigned, - .unspecified => .signed, - .platform_dependant => comp.getCharSignedness(), - }; - } }; // might not need all 4 of these when finished, @@ -145,6 +135,8 @@ const Type = @This(); ref: Interner.Ref, +pub const invalid = Type{ .ref = .invalid }; + pub fn isCallable(ty: Type) ?Type { return switch (ty.specifier) { .func, .var_args_func, .old_style_func => ty, diff --git a/src/aro/Type/Interner.zig b/src/aro/Type/Interner.zig index bbdbe23c..f1469751 100644 --- a/src/aro/Type/Interner.zig +++ b/src/aro/Type/Interner.zig @@ -3,13 +3,14 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const Hash = std.hash.Wyhash; const Attribute = @import("../Attribute.zig"); -const Compilation = @import("Compilation.zig"); +const Compilation = @import("../Compilation.zig"); const StringInterner = @import("../StringInterner.zig"); const StringId = StringInterner.StringId; const Tree = @import("../Tree.zig"); const TokenIndex = Tree.TokenIndex; const NodeIndex = Tree.NodeIndex; const Type = @import("../Type.zig"); +const target_util = @import("../target.zig"); const Interner = @This(); @@ -20,23 +21,33 @@ items: std.MultiArrayList(struct { }) = .{}, extra: std.ArrayListUnmanaged(u32) = .{}, named: struct { - wchar: Type, - uint_least16_t: Type, - uint_least32_t: Type, - ptrdiff: Type, - size: Type, - va_list: Type, - pid_t: Type, - ns_constant_string: Type, - file: Type, - jmp_buf: Type, - sigjmp_buf: Type, - ucontext_t: Type, - intmax: Type, - intptr: Type, - int16: Type, - int64: Type, -}, + wchar: Type = Type.invalid, + uint_least16_t: Type = Type.invalid, + uint_least32_t: Type = Type.invalid, + ptrdiff: Type = Type.invalid, + size: Type = Type.invalid, + va_list: Type = Type.invalid, + pid_t: Type = Type.invalid, + ns_constant_string: Type = Type.invalid, + file: Type = Type.invalid, + jmp_buf: Type = Type.invalid, + sigjmp_buf: Type = Type.invalid, + ucontext_t: Type = Type.invalid, + intmax: Type = Type.invalid, + intptr: Type = Type.invalid, + int16: Type = Type.invalid, + int64: Type = Type.invalid, +} = .{}, +target_specific: struct { + char_sign: std.builtin.Signedness = .signed, + short_bits: u16 = 16, + int_bits: u16 = 32, + long_bits: u16 = 64, + long_long_bits: u16 = 64, + float_bits: u16 = 32, + double_bits: u16 = 64, + long_double_bits: u16 = 128, +} = .{}, const KeyAdapter = struct { interner: *const Interner, @@ -53,9 +64,6 @@ const KeyAdapter = struct { }; pub const Key = union(enum) { - /// A NaN-like poison value - invalid, - /// GNU auto type /// This is a placeholder specifier - it must be replaced by the actual type specifier (determined by the initializer) auto_type: Type.Qualifiers, @@ -111,6 +119,7 @@ pub const Key = union(enum) { typedef: struct { qual: Type.Qualifiers, ty: Type, + name: StringId, }, decayed: struct { @@ -128,7 +137,9 @@ pub const Key = union(enum) { qual: Type.Qualifiers, signedness: Type.Signedness, bits: u16, - name: enum { + name: Name, + + pub const Name = enum { char, short, int, @@ -136,13 +147,15 @@ pub const Key = union(enum) { long_long, int128, bit_int, - }, + }; }; pub const Float = struct { qual: Type.Qualifiers, bits: u16, - name: enum { + name: Name, + + pub const Name = enum { fp16, float16, float, @@ -150,7 +163,7 @@ pub const Key = union(enum) { long_double, float80, float128, - }, + }; }; pub const Func = struct { @@ -282,11 +295,105 @@ pub const Key = union(enum) { }; pub const Ref = enum(u32) { + /// A NaN-like poison value invalid = std.math.maxInt(u32), _, }; -pub const Tag = enum(u8) {}; +pub const Tag = enum(u8) { + /// `data` is `Type.Qualifiers` + auto_type, + /// `data` is `Type.Qualifiers` + c23_auto, + /// `data` is `Type.Qualifiers` + nullptr_t, + /// `data` is `Type.Qualifiers` + bool, + /// `data` is `Type.Qualifiers` + void, + /// `data` is `PackedInt` + int, + /// `data` is `PackedInt` + complex_int, + /// `data` is `PackedInt` + imaginary_int, + /// `data` is `PackedFloat` + float, + /// `data` is `PackedFloat` + complex_float, + /// `data` is `PackedFloat` + imaginary_float, + + // func, + // var_args_func, + // old_style_func, + + // pointer: Pointer, + // unspecified_variable_len_array: Pointer, + + // array: Array, + // static_array: Array, + // incomplete_array: Array, + // vector: Array, + // variable_len_array: Expr, + + // @"struct": Record, + // @"union": Record, + + // @"enum": Enum, + + // /// typeof(type-name) + // typeof_type: struct { + // qual: Type.Qualifiers, + // ty: Type, + // }, + // /// typeof(expression) + // typeof_expr: Expr, + + // typedef: struct { + // qual: Type.Qualifiers, + // ty: Type, + // name: StringId, + // }, + + // decayed: struct { + // qual: Type.Qualifiers, + // ty: Type, + // }, + + // attributed: struct { + // qual: Type.Qualifiers, + // ty: Type, + // attributes: []const Attribute, + // }, + + pub const PackedInt = packed struct(u32) { + qual: Type.Qualifiers, + signedness: Type.Signedness, + bits: u16, + name: Key.Int.Name, + }; + + pub const PackedFloat = packed struct(u32) { + qual: Type.Qualifiers, + bits: u16, + name: Key.Float.Name, + }; +}; + +pub fn initNamed(i: *Interner, comp: *Compilation) void { + // Set target specific information. + i.target_specific.char_sign = comp.getCharSignedness(); + i.short_bits = comp.target.c_type_bit_size(.short); + i.int_bits = comp.target.c_type_bit_size(.int); + i.long_bits = comp.target.c_type_bit_size(.long); + i.long_long_bits = comp.target.c_type_bit_size(.longlong); + i.float_bits = comp.target.c_type_bit_size(.float); + i.double_bits = comp.target.c_type_bit_size(.double); + i.long_double_bits = comp.target.c_type_bit_size(.longdouble); + + // TODO +} pub fn deinit(i: *Interner, gpa: Allocator) void { i.map.deinit(gpa); @@ -295,7 +402,6 @@ pub fn deinit(i: *Interner, gpa: Allocator) void { } pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { - if (key.toRef()) |some| return some; const adapter: KeyAdapter = .{ .interner = i }; const gop = try i.map.getOrPutAdapted(gpa, key, adapter); if (gop.found_existing) return @enumFromInt(gop.index); @@ -330,10 +436,7 @@ pub fn get(i: *const Interner, ref: Ref) Key { pub fn getExtra(i: *const Interner, ref: Ref, search: enum { deep, shallow }) Key { _ = search; - switch (ref) { - else => {}, - } - + assert(ref != .invalid); while (true) { const item = i.items.get(@intFromEnum(ref)); switch (item.tag) {}