authorchloe caruso <git@paperclover.net> 2024-05-25 02:35:49-07:00
committerchloe caruso <git@paperclover.net> 2024-05-25 02:35:49-07:00
log974964a59da977061f958f51c142256ece5f30ea
tree6daf797485f1a83f62305003a12c84e8aac1c071
parent2ff59a3d2386e34541dcf0b1404ed591f6bc1051

split InternPool.Index into Type + Value

this ends up regressing a lot of the compiler's ability to work, but i have found many many many cases where a comptime value should have only ever been a type, but we previously allowed any value. also a revelation that `validateType` is not going to cut it- we need to have a Teg ref for any type validation, and it should be a *coersion*. the difference is these new coersion functions return !Value or !Air.Inst.Ref instead of just a !void (error being a coersion error)

20 files changed, 683 insertions(+), 416 deletions(-)

README.md+6-6
......@@ -46,7 +46,7 @@ let hello = hello * world;
4646@js("console").log(hello);
4747```
4848
49`var` bindings create mutable memory locations at runtime. This means they can be re-assigned to in arbitrary ways.
49`var` bindings create mutable memory locations at runtime. This means they can be re-assigned to:
5050
5151```
5252var message = "first message";
......@@ -58,7 +58,7 @@ if @js("Math").random() > 0.5 {
5858@js("console").log(message);
5959```
6060
61`let` is preferred whenever possible, as it will lead to more readable code, and the compiler is able to optimize it better. The above can be rewritten as:
61Using `let` is preferred whenever possible, as it will lead to more readable code, and the compiler is able to optimize it better. The above can be rewritten as:
6262
6363```
6464let message = if @js("Math").random() > 0.5 {
......@@ -70,10 +70,10 @@ let message = if @js("Math").random() > 0.5 {
7070@js("console").log(message);
7171```
7272
73The initializer for a `let` and `var` are always required, but the type annotations can be omitted when the data type can be inferred. For `let`, this is always the case. `var`, being runtime-mutable memory, only supports runtime representable data types. This means for numerical literals, an explicit type annotation is needed to determine weather to store an integer or a floating point number.
73It should be noted that numeric literals are a comptime-only data type, meaning it cannot be assigned to a `var` without an explicit type annotation. Since `int` is a strict subset of valid `float`s, the coersion will implicitly be a float, but a compiler warning will be emitted.
7474
7575```
76var a = 5; // error
77var b: int = 5; // ok
78var c: float = 5; // ok
76var number = 0; // `number` is a `float`, but not explicit
77var number: int = 0; // `number` is `int`
78var number: float = 0; // `number` is `float`
7979```
demo.wl created+1
......@@ -0,0 +1 @@
1let x: int = 1.5;
notes created+19
......@@ -0,0 +1,19 @@
10.1.2
2- teg: useless discard
3- teg: dangling identifier
4- teg: remove `let _ = ` error
5- sema: .as instruction
6- sema: binary op type resolution (float + num_literal -> float)
70.1.3
8- parse/teg/sema: implement the comptime keyword
9- implement all the binary operators
10- implement all the unary operators
11- implement string escapes
12- implement proper float printing
13- js/link: add parenthesis when needed
140.1.4
15- parse: impl module, struct define, struct init, defer, and optionals
16- teg: impl the above, except anonymous struct initializers
17- sema: implement some built-in functions on floats
18 - float.intTruncate() to perform lossy int conversion
19
src/compiler/Air.Dump.zig+9-10
......@@ -2,7 +2,7 @@
22air: Air,
33ip: *const InternPool,
44
5const State = struct {
5pub const State = struct {
66 arena: std.mem.Allocator,
77 air: *const Air,
88 ip: *const InternPool,
......@@ -67,7 +67,7 @@ const State = struct {
6767 try w.print("{d}", .{arg.arg_number});
6868 try tty.setColor(w, .reset);
6969 try w.writeAll(": ");
70 try s.dumpValue(w, arg.type);
70 try s.dumpValue(w, arg.type.value());
7171 },
7272 .@"break" => {
7373 const brk: Air.Break = air.instData(.@"break", inst);
......@@ -113,7 +113,7 @@ const State = struct {
113113 try w.writeAll("(");
114114 try s.dumpRef(w, memory.init);
115115 try w.writeAll(", type: ");
116 try s.dumpValue(w, memory.ty);
116 try s.dumpValue(w, memory.ty.value());
117117 try w.writeAll(")");
118118 },
119119 .field => {
......@@ -175,7 +175,7 @@ const State = struct {
175175 try s.tty.setColor(w, .reset);
176176 }
177177
178 fn dumpValue(s: *State, w: anytype, val: Value) !void {
178 pub fn dumpValue(s: *State, w: anytype, val: Value) !void {
179179 const tty = s.tty;
180180 switch (val.toKey(s.ip)) {
181181 .simple_type => |t| {
......@@ -198,14 +198,14 @@ const State = struct {
198198 try w.writeAll(",");
199199 try tty.setColor(w, .reset);
200200 }
201 try s.dumpValue(w, Value.fromInterned(param));
201 try s.dumpValue(w, param.value());
202202 }
203203 try tty.setColor(w, .blue);
204204 try w.writeAll(") -> ");
205205 try tty.setColor(w, .reset);
206 try s.dumpValue(w, Value.fromInterned(ty.return_type));
206 try s.dumpValue(w, ty.return_type.value());
207207 },
208 inline .float, .int32 => |data, tag| {
208 inline .float, .int32, .comptime_float => |data, tag| {
209209 try tty.setColor(w, .bright_black);
210210 try w.writeAll("<");
211211 try tty.setColor(w, .blue);
......@@ -235,7 +235,7 @@ const State = struct {
235235 .func => |func| {
236236 try tty.setColor(w, .bright_black);
237237 try w.writeAll("<");
238 try s.dumpValue(w, Value.fromInterned(func.ty));
238 try s.dumpValue(w, func.ty);
239239 try tty.setColor(w, .bright_black);
240240 try w.writeAll(", ");
241241 const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip);
......@@ -310,8 +310,7 @@ const assert = std.debug.assert;
310310const Air = @import("Air.zig");
311311const Inst = Air.Inst;
312312
313const Value = @import("Value.zig");
314
315313const InternPool = @import("InternPool.zig");
314const Value = InternPool.Value;
316315
317316const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter;
src/compiler/Air.zig+22-21
......@@ -77,7 +77,7 @@ pub const Inst = struct {
7777
7878 pub fn decode(ref: Ref) Decoded {
7979 return switch (ref.kind) {
80 .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) },
80 .value => .{ .value = @enumFromInt(ref.data) },
8181 .inst => .{ .inst = @enumFromInt(ref.data) },
8282 };
8383 }
......@@ -97,7 +97,7 @@ pub const Inst = struct {
9797
9898 pub fn isVoid(ref: Ref) bool {
9999 return ref.kind == .value and
100 ref.decode().value.toIntern() == .void_value;
100 ref.decode().value == .void;
101101 }
102102
103103 pub fn eql(ref: Ref, other: Ref) bool {
......@@ -203,7 +203,7 @@ pub const Call = struct {
203203};
204204
205205pub const Arg = struct {
206 type: Value,
206 type: Type,
207207 arg_number: u32,
208208};
209209
......@@ -214,13 +214,13 @@ pub const If = struct {
214214 pub const Data = struct {
215215 yes: Inst.Ref,
216216 no: Inst.Ref,
217 union_ty: Value,
217 union_ty: Type,
218218 };
219219};
220220
221221pub const Memory = struct {
222222 init: Inst.Ref,
223 ty: Value,
223 ty: Type,
224224};
225225
226226pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
......@@ -249,16 +249,16 @@ pub fn instPayload(air: *const Air, comptime tag: Inst.Tag, index: Inst.Index) t
249249 return air.instData(tag, index).extra_index.get(air);
250250}
251251
252pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index {
252pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Type {
253253 return switch (ref.decode()) {
254 .value => |v| v.type(ip).toIntern(),
254 .value => |v| v.typeOf(ip),
255255 .inst => |i| air.typeOfIndex(i, ip),
256256 };
257257}
258258
259pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPool.Index {
259pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPool.Type {
260260 return switch (air.instTag(index)) {
261 .js_global_ref => .opaque_type,
261 .js_global_ref => .@"opaque",
262262
263263 .block => {
264264 const items = air.instData(.block, index).slice(air);
......@@ -267,28 +267,28 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo
267267
268268 inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip),
269269
270 .lt, .gt, .eq, .bool_not => .bool_type,
270 .lt, .gt, .eq, .bool_not => .bool,
271271
272 .arg => air.instData(.arg, index).type.toIntern(),
272 .arg => air.instData(.arg, index).type,
273273
274274 .negate => air.typeOf(air.instData(.negate, index).operand, ip),
275275
276 .@"break" => .noreturn_type,
276 .@"break" => .noreturn,
277277
278278 // todo:
279 .field => .opaque_type,
279 .field => .@"opaque",
280280
281281 .call => {
282282 const base_type = air.typeOf(air.instData(.call, index).base, ip);
283 if (base_type == .opaque_type) return .opaque_type;
284 return base_type.toKey(ip).type_function.return_type;
283 if (base_type == .@"opaque") return .@"opaque";
284 return base_type.value().toKey(ip).type_function.return_type;
285285 },
286286
287 .memory => air.instData(.memory, index).ty.toIntern(),
287 .memory => air.instData(.memory, index).ty,
288288
289 .store => air.instData(.memory, air.instData(.store, index).memory).ty.toIntern(),
289 .store => air.instData(.memory, air.instData(.store, index).memory).ty,
290290
291 .@"if" => air.instData(.@"if", index).data.get(air).union_ty.toIntern(),
291 .@"if" => air.instData(.@"if", index).data.get(air).union_ty,
292292 };
293293}
294294
......@@ -310,10 +310,11 @@ const std = @import("std");
310310const mem = std.mem;
311311const assert = std.debug.assert;
312312
313const Teg = @import("Teg.zig");
314313const InternPool = @import("InternPool.zig");
314const Value = InternPool.Value;
315const Type = InternPool.Type;
316
317const Teg = @import("Teg.zig");
315318const dod = @import("dod.zig");
316319
317320const Unused = enum(u32) { zero = 0 };
318
319const Value = @import("Value.zig");
src/compiler/Comp.zig+1-2
......@@ -214,7 +214,7 @@ pub const File = struct {
214214
215215pub const Decl = struct {
216216 name: InternPool.NullTerminatedString,
217 val: Value,
217 val: InternPool.Value,
218218 /// The direct parent namespace of the Decl. In the case of the Decl
219219 /// corresponding to a file, this is the namespace of the struct, since
220220 /// there is no parent.
......@@ -432,7 +432,6 @@ const Ast = @import("Ast.zig");
432432const Air = @import("Air.zig");
433433const Teg = @import("Teg.zig");
434434const Cache = @import("Cache.zig");
435const Value = @import("Value.zig");
436435const Sema = @import("Sema.zig");
437436const Transform = @import("Transform.zig");
438437const JsNodeGen = @import("JsNodeGen.zig");
src/compiler/InternPool.zig+156-41
......@@ -78,21 +78,25 @@ pub const Tag = enum(u8) {
7878 /// A function type
7979 /// data is an extra index into a `FuncType`
8080 type_function,
81 /// A type that can be represented with only an enum tag.
82 /// data is SimpleType enum value.
83 simple_type,
8184
8285 // --- before this are types ---
8386 // --- past this are values ---
8487
85 /// A type that can be represented with only an enum tag.
86 /// data is SimpleType enum value.
87 simple_type,
8888 /// A value that can be represented with only an enum tag.
8989 /// data is SimpleValue enum value.
9090 simple_value,
91 /// A numeric literal that can be represented as a float.
92 /// Integers greater than 2^53 known at compile time are currently not supported.
93 /// data is an extra index into a `Float`
94 comptime_float,
9195 /// An int32 value
9296 /// data is i32
9397 int32,
9498 /// A float value
95 /// data is an extra index into an `Float`
99 /// data is an extra index into a `Float`
96100 float,
97101 /// A function
98102 /// data is an extra index into a `FuncDecl`
......@@ -122,7 +126,7 @@ pub const Tag = enum(u8) {
122126 .simple_value,
123127 .int32,
124128 => unreachable, // does not have a payload
125 .float => Float,
129 .float, .comptime_float => Float,
126130 .func => FuncDecl,
127131 .type_function => TypeFunction,
128132 .string => String.WithLen,
......@@ -133,7 +137,7 @@ pub const Tag = enum(u8) {
133137 pub const FuncDecl = struct {
134138 analysis: FuncAnalysis,
135139 owner_decl: DeclIndex,
136 ty: Index,
140 ty: Value,
137141 teg_body_inst: TrackedInst.Index,
138142 };
139143
......@@ -141,26 +145,26 @@ pub const Tag = enum(u8) {
141145 /// 0. param_type: Index for each params_len
142146 pub const TypeFunction = struct {
143147 params_len: u32,
144 return_type: Index,
148 return_type: Type,
145149 };
146150};
147151
148pub const Index = enum(u32) {
149 pub const first_type: Index = .opaque_type;
150 pub const last_type: Index = .noreturn_type;
151 pub const first_value: Index = .void_value;
152 pub const last_value: Index = .empty_string;
152pub const Value = enum(u32) {
153 pub const first_type: Value = .opaque_type;
154 pub const last_type: Value = .noreturn_type;
155 pub const first_value: Value = .void;
156 pub const last_value: Value = .empty_string;
153157
154158 opaque_type,
155159 bool_type,
156160 int32_type,
157161 float_type,
158162 string_type,
159 void_type,
160163 noreturn_type,
161164 type_type,
165 numeric_literal_type,
162166
163 void_value,
167 void,
164168 bool_true,
165169 bool_false,
166170 int32_zero,
......@@ -178,36 +182,140 @@ pub const Index = enum(u32) {
178182 start: u32,
179183 len: u32,
180184
181 pub fn get(slice: Slice, ip: *const InternPool) []Index {
185 pub fn get(slice: Slice, ip: *const InternPool) []Value {
182186 return @ptrCast(ip.extra.list.items[slice.start..][0..slice.len]);
183187 }
184188 };
185189
186190 /// Used for a map of `Index` values to the index within a list of `Index` values.
187191 const Adapter = struct {
188 indexes: []const Index,
192 indexes: []const Value,
189193
190 pub fn eql(ctx: @This(), a: Index, b_void: void, b_map_index: usize) bool {
194 pub fn eql(ctx: @This(), a: Value, b_void: void, b_map_index: usize) bool {
191195 _ = b_void;
192196 return a == ctx.indexes[b_map_index];
193197 }
194198
195 pub fn hash(ctx: @This(), a: Index) u32 {
199 pub fn hash(ctx: @This(), a: Value) u32 {
196200 _ = ctx;
197201 return std.hash.uint32(@intFromEnum(a));
198202 }
199203 };
200204
201 pub fn toAir(index: Index) Air.Inst.Ref {
202 return .{ .kind = .value, .data = @intCast(@intFromEnum(index)) };
205 pub fn valueTag(value: Value, ip: *const InternPool) Tag {
206 return ip.items.items(.tag)[@intFromEnum(value)];
207 }
208
209 pub fn valueData(value: Value, ip: *const InternPool, comptime tag: Tag) tag.Data() {
210 return ip.items.items(.data)[@intFromEnum(value)].decode(tag, value.valueTag(ip));
211 }
212
213 pub fn toAir(value: Value) Air.Inst.Ref {
214 return .{ .kind = .value, .data = @intCast(@intFromEnum(value)) };
215 }
216
217 pub fn toKey(value: Value, ip: *const InternPool) Key {
218 return ip.indexToKey(value);
219 }
220
221 pub fn dump(value: Value, ip: *const InternPool) std.fmt.Formatter(Value.formatValue) {
222 return .{ .data = .{ .val = value, .ip = ip } };
223 }
224
225 fn formatValue(
226 ctx: struct { val: Value, ip: *const InternPool },
227 comptime _: []const u8,
228 _: std.fmt.FormatOptions,
229 writer: anytype,
230 ) !void {
231 // TODO: move the formatter out of Air.Dump
232 var state: Air.Dump.State = undefined;
233 state.ip = ctx.ip;
234 state.tty = @import("util.zig").detectTTYFromWriter(writer);
235 try Air.Dump.State.dumpValue(&state, writer, ctx.val);
236 }
237
238 pub fn typeOf(value: Value, ip: *const InternPool) Type {
239 return switch (value.valueTag(ip)) {
240 .removed => unreachable, // UAF
241
242 .type_function => .type,
243 .simple_type => .type,
244
245 .simple_value => switch (value.valueData(ip, .simple_value)) {
246 .true => .bool,
247 .false => .bool,
248 .void => .void,
249 },
250
251 .comptime_float => .numeric_literal,
252 .int32 => .int32,
253 .float => .float,
254 .string => .string,
255
256 .func => @panic("TODO"),
257 };
258 }
259
260 /// Try to re-interpret this `Value` as a `Type`.
261 /// Returns `null` if the value is not a type.
262 pub fn toType(value: Value, ip: *const InternPool) ?Type {
263 return switch (value.valueTag(ip)) {
264 .removed => unreachable, // UAF
265
266 .type_function,
267 .simple_type,
268 => @enumFromInt(@intFromEnum(value)),
269
270 // exception: void is a value AND a type
271 .simple_value => if (value == .void) .void else null,
272
273 else => null,
274 };
275 }
276
277 pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
278 return val.valueData(ip, .string).get(ip);
279 }
280
281 pub fn toFloat(val: Value, ip: *const InternPool) ?f64 {
282 return switch (val.valueTag(ip)) {
283 inline .comptime_float, .float => |tag| val.valueData(ip, tag).get(ip).decode(),
284 else => null,
285 };
203286 }
287};
288
289pub const Type = enum(u32) {
290 @"opaque" = @intFromEnum(Value.opaque_type),
291 bool = @intFromEnum(Value.bool_type),
292 int32 = @intFromEnum(Value.int32_type),
293 float = @intFromEnum(Value.float_type),
294 string = @intFromEnum(Value.string_type),
295 noreturn = @intFromEnum(Value.noreturn_type),
296 type = @intFromEnum(Value.type_type),
297 numeric_literal = @intFromEnum(Value.numeric_literal_type),
298 void = @intFromEnum(Value.void),
299 _,
204300
205 pub fn toValue(index: Index) @import("Value.zig") {
206 return .{ .ip_index = index };
301 /// An array of `Index` existing within the `extra` array.
302 /// This type exists to provide a struct with lifetime that is
303 /// not invalidated when items are added to the `InternPool`.
304 pub const Slice = struct {
305 start: u32,
306 len: u32,
307
308 pub fn get(slice: Slice, ip: *const InternPool) []Type {
309 return @ptrCast(ip.extra.list.items[slice.start..][0..slice.len]);
310 }
311 };
312
313 pub fn value(ty: Type) Value {
314 return @enumFromInt(@intFromEnum(ty));
207315 }
208316
209 pub fn toKey(index: Index, ip: *const InternPool) Key {
210 return ip.indexToKey(index);
317 pub fn dump(ty: Type, ip: *const InternPool) std.fmt.Formatter(Value.formatValue) {
318 return ty.value().dump(ip);
211319 }
212320};
213321
......@@ -217,9 +325,9 @@ pub const static_keys = [_]Key{
217325 SimpleType.int32.key(),
218326 SimpleType.float.key(),
219327 SimpleType.string.key(),
220 SimpleType.void.key(),
221328 SimpleType.noreturn.key(),
222329 SimpleType.type.key(),
330 SimpleType.numeric_literal.key(),
223331
224332 SimpleValue.void.key(),
225333 SimpleValue.true.key(),
......@@ -461,11 +569,11 @@ pub const NamespaceIndex = enum(u32) {
461569/// * comptime-known value (where we store the value)
462570/// * decl val (so that we can analyze the value lazily)
463571/// * decl ref (so that we can analyze the reference lazily)
464pub const CaptureValue = packed struct(u32) {
572pub const Capture = packed struct(u32) {
465573 tag: enum(u2) { @"comptime", runtime, decl_val, decl_ref },
466574 idx: u30,
467575
468 pub fn wrap(val: Unwrapped) CaptureValue {
576 pub fn wrap(val: Unwrapped) Capture {
469577 return switch (val) {
470578 .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@intFromEnum(i)) },
471579 .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@intFromEnum(i)) },
......@@ -473,7 +581,7 @@ pub const CaptureValue = packed struct(u32) {
473581 .decl_ref => |i| .{ .tag = .decl_ref, .idx = @intCast(@intFromEnum(i)) },
474582 };
475583 }
476 pub fn unwrap(val: CaptureValue) Unwrapped {
584 pub fn unwrap(val: Capture) Unwrapped {
477585 return switch (val.tag) {
478586 .@"comptime" => .{ .@"comptime" = @enumFromInt(val.idx) },
479587 .runtime => .{ .runtime = @enumFromInt(val.idx) },
......@@ -484,9 +592,9 @@ pub const CaptureValue = packed struct(u32) {
484592
485593 pub const Unwrapped = union(enum) {
486594 /// Index refers to the value.
487 @"comptime": Index,
595 @"comptime": Value,
488596 /// Index refers to the type.
489 runtime: Index,
597 runtime: Value,
490598 decl_val: DeclIndex,
491599 decl_ref: DeclIndex,
492600 };
......@@ -494,7 +602,7 @@ pub const CaptureValue = packed struct(u32) {
494602 pub const Slice = struct {
495603 start: u32,
496604 len: u32,
497 pub fn get(slice: Slice, ip: *const InternPool) []CaptureValue {
605 pub fn get(slice: Slice, ip: *const InternPool) []Capture {
498606 return @ptrCast(ip.extra.items[slice.start..][0..slice.len]);
499607 }
500608 };
......@@ -516,9 +624,9 @@ const SimpleType = enum {
516624 float,
517625 string,
518626 bool,
519 void,
520627 noreturn,
521628 type,
629 numeric_literal,
522630
523631 pub fn key(self: SimpleType) Key {
524632 return .{ .simple_type = self };
......@@ -526,6 +634,7 @@ const SimpleType = enum {
526634};
527635
528636pub const Key = union(enum) {
637 comptime_float: f64,
529638 int32: i32,
530639 float: f64,
531640 string: []const u8,
......@@ -549,8 +658,8 @@ pub const Key = union(enum) {
549658 };
550659
551660 pub const TypeFunction = struct {
552 param_types: []Index,
553 return_type: Index,
661 param_types: []Type,
662 return_type: Type,
554663
555664 pub fn hash(self: TypeFunction, hasher: *Hash) void {
556665 const t = tracer.trace(@src());
......@@ -563,13 +672,13 @@ pub const Key = union(enum) {
563672 }
564673
565674 pub fn eql(a: TypeFunction, b: TypeFunction) bool {
566 return std.mem.eql(Index, a.param_types, b.param_types) and
675 return std.mem.eql(Type, a.param_types, b.param_types) and
567676 a.return_type == b.return_type;
568677 }
569678 };
570679
571680 pub const Func = struct {
572 ty: Index,
681 ty: Value,
573682 owner_decl: DeclIndex,
574683 teg_body_inst: TrackedInst.Index,
575684
......@@ -600,7 +709,7 @@ pub const Key = union(enum) {
600709 => |x| Hash.hash(seed, asBytes(&x)),
601710 .string,
602711 => |x| Hash.hash(seed, x),
603 .float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))),
712 .float, .comptime_float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))),
604713 .type_function => |x| {
605714 var hasher = Hash.init(seed);
606715 x.hash(&hasher);
......@@ -631,6 +740,7 @@ pub const Key = union(enum) {
631740 .string,
632741 => |x| std.mem.eql(u8, x, b.string),
633742 .float => |x| Float.assertPure(x) == Float.assertPure(b.float),
743 .comptime_float => |x| Float.assertPure(x) == Float.assertPure(b.comptime_float),
634744 .type_function => |x| x.eql(b.type_function),
635745 .func => |a_info| {
636746 const b_info = b.func;
......@@ -747,7 +857,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
747857 ip.* = undefined;
748858}
749859
750pub fn indexToKey(ip: *const InternPool, index: Index) Key {
860pub fn indexToKey(ip: *const InternPool, index: Value) Key {
751861 assert(index != .none);
752862 const item: Item = ip.items.get(@intFromEnum(index));
753863 const data = item.data;
......@@ -762,6 +872,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
762872 .simple_value => .{ .simple_value = data.decode(.simple_value, item.tag) },
763873 .int32 => .{ .int32 = data.decode(.int32, item.tag) },
764874 .float => .{ .float = data.decode(.float, item.tag).get(ip).decode() },
875 .comptime_float => .{ .comptime_float = data.decode(.comptime_float, item.tag).get(ip).decode() },
765876 .string => .{ .string = data.decode(.string, item.tag).get(ip).toSlice(ip) },
766877 .func => {
767878 const func = data.decode(.func, item.tag).get(ip);
......@@ -774,7 +885,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
774885 };
775886}
776887
777pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index {
888pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
778889 const t = tracer.traceFmt(@src(), "{s}", .{@tagName(key)});
779890 defer t.end();
780891
......@@ -806,6 +917,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index {
806917 .float,
807918 try ip.extra.append(gpa, Float, Float.encode(f)),
808919 ),
920 .comptime_float => |f| Item.encode(
921 .comptime_float,
922 try ip.extra.append(gpa, Float, Float.encode(f)),
923 ),
809924 .func => |f| brk: {
810925 const extra = try ip.extra.append(gpa, Tag.FuncDecl, .{
811926 .analysis = .{
......@@ -829,7 +944,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index {
829944 },
830945 };
831946 ip.items.appendAssumeCapacity(item);
832 const index: Index = @enumFromInt(ip.items.len - 1);
947 const index: Value = @enumFromInt(ip.items.len - 1);
833948 if (std.debug.runtime_safety) {
834949 assert(ip.indexToKey(index).eql(key, ip));
835950 }
......@@ -841,7 +956,7 @@ fn extraTypeFunction(
841956 extra_index: ExtraIndex(Tag.TypeFunction),
842957) Key.TypeFunction {
843958 const type_function, var trail = extra_index.getTrail(ip);
844 const param_types = trail.readInternPoolIndexSlice(type_function.params_len);
959 const param_types = trail.readTypeSlice(type_function.params_len);
845960 const result: Key.TypeFunction = .{
846961 .return_type = type_function.return_type,
847962 .param_types = param_types.get(ip),
src/compiler/JsLink.zig+2
......@@ -221,6 +221,8 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
221221 // TODO: escapes
222222 .string => |val| try writer.print("\"{s}\"", .{val}),
223223
224 .comptime_float => unreachable, // was not lowered to float
225
224226 else => unreachable, // this value is not a plain_value
225227 }
226228 },
src/compiler/JsNodeGen.zig+29-25
......@@ -198,23 +198,23 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {
198198 if ((tag == .add or tag == .sub) and
199199 inner_data.rhs.kind == .value)
200200 {
201 if (inner_data.rhs.decode().value.toFloat(c.ip)) |f| {
202 if (f == 1) {
203 // convert this into ++ or --
204 const node = try c.add(
205 if (tag == .add)
206 .pre_inc
207 else
208 .pre_dec,
209 .{
210 .operand = target,
211 },
212 );
213 try c.markExpression(inst, node);
214
215 return;
216 }
217 }
201 // if (inner_data.rhs.decode().value.toFloat(c.ip)) |f| {
202 // if (f == 1) {
203 // // convert this into ++ or --
204 // const node = try c.add(
205 // if (tag == .add)
206 // .pre_inc
207 // else
208 // .pre_dec,
209 // .{
210 // .operand = target,
211 // },
212 // );
213 // try c.markExpression(inst, node);
214
215 // return;
216 // }
217 // }
218218 }
219219
220220 // convert into a compound assignment
......@@ -442,15 +442,20 @@ fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index {
442442 const t = tracer.trace(@src());
443443 defer t.end();
444444
445 const intern_tag = c.ip.items.items(.tag)[@intFromEnum(v.toIntern())];
445 const intern_tag = v.valueTag(c.ip);
446446 switch (intern_tag) {
447447 .removed => unreachable, // UAF
448448
449 .type_function, .simple_type => unreachable, // Type values are not allowed here
449 .type_function,
450 .simple_type,
451 => unreachable, // Type values are not allowed here
450452
451 .int32, .float, .string, .simple_value => {
452 return try c.add(.plain_value, .{ .value = v });
453 },
453 .int32,
454 .float,
455 .comptime_float,
456 .string,
457 .simple_value,
458 => return try c.add(.plain_value, .{ .value = v }),
454459
455460 // funcs always point to decls
456461 .func => {
......@@ -493,7 +498,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br
493498 var requires_label = false;
494499 var items = switch (inst.decode()) {
495500 .value => |v| items: {
496 if (v.ip_index == .void_value)
501 if (v == .void)
497502 return null;
498503 if (temporary) |temp| {
499504 const node = try c.valueToExpression(v);
......@@ -678,6 +683,7 @@ const assert = std.debug.assert;
678683const Allocator = std.mem.Allocator;
679684
680685const InternPool = @import("InternPool.zig");
686const Value = InternPool.Value;
681687const NullTerminatedString = InternPool.NullTerminatedString;
682688
683689const Comp = @import("Comp.zig");
......@@ -690,7 +696,5 @@ const LocalIndex = JsTree.LocalIndex;
690696
691697const Air = @import("Air.zig");
692698
693const Value = @import("Value.zig");
694
695699const dod = @import("dod.zig");
696700const tracer = @import("tracer");
src/compiler/JsTree.zig+2-2
......@@ -244,6 +244,8 @@ const std = @import("std");
244244const assert = std.debug.assert;
245245
246246const InternPool = @import("InternPool.zig");
247const Value = InternPool.Value;
248
247249const NullTerminatedString = InternPool.NullTerminatedString;
248250const String = InternPool.String;
249251
......@@ -252,6 +254,4 @@ const Decl = Comp.Decl;
252254
253255const dod = @import("dod.zig");
254256
255const Value = @import("Value.zig");
256
257257const Unused = @import("Ast.zig").Unused;
src/compiler/Parse.zig+4-1
......@@ -265,7 +265,10 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex {
265265 const params = try p.parseParamList();
266266
267267 const return_type = if (p.eatToken(.minus_lt)) |_|
268 try p.parseExpression(false)
268 if (!p.isExpressionStart())
269 return p.failUnexpectedToken(.expression)
270 else
271 try p.parseExpression(false)
269272 else
270273 null;
271274
src/compiler/Report.zig+108-56
......@@ -24,62 +24,6 @@ pub const Item = struct {
2424 pub const Type = enum { @"error", warn };
2525};
2626
27/// A variant of std.builtin.StackTrace that stores it's data within itself
28/// instead of being a pointer. This allows capturing the stack trace and
29/// saving it for later printing.
30pub const StoredTrace = struct {
31 data: [32]usize,
32 index: usize,
33
34 pub const empty = .{
35 .data = .{0} ** 32,
36 .index = 0,
37 };
38
39 fn trace(stored: *StoredTrace) std.builtin.StackTrace {
40 return .{
41 .index = stored.index,
42 .instruction_addresses = &stored.data,
43 };
44 }
45
46 pub fn capture(begin: ?usize) StoredTrace {
47 var stored: StoredTrace = undefined;
48 var frame = stored.trace();
49 std.debug.captureStackTrace(begin orelse @returnAddress(), &frame);
50 stored.index = frame.index;
51 return stored;
52 }
53
54 pub fn from(stack_trace: ?*std.builtin.StackTrace) StoredTrace {
55 if (stack_trace) |stack| {
56 var data: [32]usize = undefined;
57 @memset(&data, 0);
58 const items = @min(stack.instruction_addresses.len, 32);
59 @memcpy(data[0..items], stack.instruction_addresses[0..items]);
60 return .{
61 .data = data,
62 .index = @min(items, stack.index),
63 };
64 } else {
65 return empty;
66 }
67 }
68};
69
70pub const DebugStoredTrace = if (builtin.mode == .Debug)
71 StoredTrace
72else
73 struct {
74 pub fn capture() @This() {
75 return .{};
76 }
77
78 pub fn from(_: ?*std.builtin.StackTrace) @This() {
79 return .{};
80 }
81 };
82
8327pub const Err = union(enum) {
8428 pub const FieldEnum = std.meta.FieldEnum(Err);
8529
......@@ -189,6 +133,15 @@ pub const Err = union(enum) {
189133 );
190134 }
191135 },
136 comptime_float_to_int: struct {
137 val: f64,
138 src: LazyNode,
139
140 fn build(data: @This(), ctx: *MsgContext) !void {
141 const node = try data.src.get(ctx);
142 try ctx.msgNode(node, "numeric literal \"{d}\" cannot be coerced to \"int\"", .{data.val});
143 }
144 },
192145};
193146
194147pub const Warn = union(enum) {
......@@ -221,8 +174,74 @@ pub const Warn = union(enum) {
221174 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});
222175 }
223176 },
177 numeric_int_var_ambiguity: struct {
178 node: LazyNode,
179
180 fn build(data: @This(), ctx: *MsgContext) !void {
181 const var_decl_node = try data.node.get(ctx);
182 const init_node = ctx.ast.nodeData(var_decl_node).var_decl.init;
183 try ctx.msgNode(init_node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
184 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});
185 }
186 },
224187};
225188
189/// A variant of std.builtin.StackTrace that stores it's data within itself
190/// instead of being a pointer. This allows capturing the stack trace and
191/// saving it for later printing.
192pub const StoredTrace = struct {
193 data: [32]usize,
194 index: usize,
195
196 pub const empty = .{
197 .data = .{0} ** 32,
198 .index = 0,
199 };
200
201 fn trace(stored: *StoredTrace) std.builtin.StackTrace {
202 return .{
203 .index = stored.index,
204 .instruction_addresses = &stored.data,
205 };
206 }
207
208 pub fn capture(begin: ?usize) StoredTrace {
209 var stored: StoredTrace = undefined;
210 var frame = stored.trace();
211 std.debug.captureStackTrace(begin orelse @returnAddress(), &frame);
212 stored.index = frame.index;
213 return stored;
214 }
215
216 pub fn from(stack_trace: ?*std.builtin.StackTrace) StoredTrace {
217 if (stack_trace) |stack| {
218 var data: [32]usize = undefined;
219 @memset(&data, 0);
220 const items = @min(stack.instruction_addresses.len, 32);
221 @memcpy(data[0..items], stack.instruction_addresses[0..items]);
222 return .{
223 .data = data,
224 .index = @min(items, stack.index),
225 };
226 } else {
227 return empty;
228 }
229 }
230};
231
232pub const DebugStoredTrace = if (builtin.mode == .Debug)
233 StoredTrace
234else
235 struct {
236 pub fn capture() @This() {
237 return .{};
238 }
239
240 pub fn from(_: ?*std.builtin.StackTrace) @This() {
241 return .{};
242 }
243 };
244
226245pub const UnusedKind = enum {
227246 param,
228247 constant,
......@@ -509,6 +528,7 @@ pub const Builder = struct {
509528 pub fn deinit(b: *Builder) void {
510529 b.errors.deinit(b.gpa);
511530 b.warnings.deinit(b.gpa);
531 b.string_bytes.deinit(b.gpa);
512532 }
513533
514534 pub fn finalize(b: *Builder) !Report {
......@@ -592,6 +612,38 @@ pub const MsgContext = struct {
592612 }
593613};
594614
615pub const LazyNode = struct {
616 decl: Comp.Decl.Index,
617 node_offset: i32,
618
619 pub fn get(lazy: LazyNode, ctx: *MsgContext) !NodeIndex {
620 const decl = ctx.comp.intern_pool.declPtrConst(lazy.decl);
621 const ns = ctx.comp.intern_pool.namespacePtr(decl.src_namespace);
622
623 // TODO: This might change if we have errors like
624 // - function arguments not matching at <callsite>
625 // - function declared <definition>
626 // And they are both in different files.
627 // To resolve this, `Component` needs to store which file it is in.
628 assert(ns.file_scope == ctx.file);
629
630 if (ns.parent.unwrap() != null) {
631 @panic("TODO: LazyNode.get when the decl is in a nested namespace");
632 }
633
634 const teg = &(ctx.file.teg orelse
635 // this will be required once incremental compilation hits
636 @panic("TODO: teg was unloaded"));
637
638 const decl_inst = decl.teg_decl_index.unwrap() orelse
639 unreachable; // file was not analyzed, yet teg loaded?
640
641 const teg_decl = teg.instData(.declaration, decl_inst.resolve(&ctx.comp.intern_pool));
642
643 return @intCast(teg_decl.src_node - lazy.node_offset);
644 }
645};
646
595647const Report = @This();
596648const std = @import("std");
597649const ArrayListUnmanaged = std.ArrayListUnmanaged;
src/compiler/Sema.zig+224-121
......@@ -16,9 +16,10 @@ inst_map: InstMap = undefined,
1616/// scratch memory. can be used in any place you might temporarily allocate an arraylist
1717/// this is most notably used to store block contents before the block is finalized
1818scratch: std.ArrayListUnmanaged(u32) = .{},
19params: []InternPool.Index = undefined,
19params: []Type = undefined,
2020
2121// outputs
22report: Report.Builder = undefined,
2223air_instructions: std.MultiArrayList(Air.Inst) = .{},
2324air_extra: dod.ExtraStore(.air, .@"var") = .{},
2425
......@@ -27,6 +28,8 @@ pub const Error = error{
2728 AnalysisFail,
2829 /// This analysis is waiting on other dependencies to finish
2930 Interrupt,
31
32 OutOfMemory,
3033};
3134
3235/// Instead of using a hash map for this, just pre-allocate a list long enough to hold everything.
......@@ -74,6 +77,11 @@ pub fn init(s: *Sema) !void {
7477 const t = tracer.trace(@src());
7578 defer t.end();
7679
80 s.report = .{
81 .gpa = s.gpa,
82 .current_file = s.ip.namespacePtr(s.namespace_index).file_scope,
83 };
84
7785 try s.comp.current_compile_decls.append(s.gpa, s.decl_index);
7886
7987 s.inst_map = try InstMap.init(
......@@ -87,6 +95,10 @@ pub fn deinit(s: *Sema) void {
8795 const t = tracer.trace(@src());
8896 defer t.end();
8997
98 const report = s.report.finalize() catch @panic("OOM");
99 report.dumpToStdErr(s.comp) catch @panic("Error");
100 report.deinit(s.gpa);
101
90102 defer s.* = undefined;
91103 s.inst_map.deinit(s.arena);
92104 s.scratch.deinit(s.arena);
......@@ -121,14 +133,14 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
121133 const decl, const fn_decl = s.code.fullFuncDecl(s.inst_map.start);
122134 assert(decl.kind == .function);
123135
124 s.params = try s.gpa.alloc(InternPool.Index, fn_decl.params.len);
136 s.params = try s.gpa.alloc(Type, fn_decl.params.len);
125137 defer s.gpa.free(s.params);
126138
127139 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
128 s.params[i] = (try s.resolveRefComptime(param)).toIntern();
140 s.params[i] = (try s.resolveRefComptime(param)).toType(s.ip).?;
129141 }
130142
131 const return_type = try s.resolveRefComptime(fn_decl.return_type);
143 const return_type = (try s.resolveRefComptime(fn_decl.return_type)).toType(s.ip).?;
132144
133145 const root_block = fn_decl.body.toIndex() orelse
134146 @panic("TODO: please change this to Index. Ref is not allowed here anymore");
......@@ -143,17 +155,16 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
143155 const func_prototype = try s.ip.get(s.gpa, .{
144156 .type_function = .{
145157 .param_types = s.params,
146 .return_type = return_type.toIntern(),
158 .return_type = return_type,
147159 },
148160 });
149 const func_value = try s.ip.get(s.gpa, .{
161 const value = try s.ip.get(s.gpa, .{
150162 .func = .{
151163 .ty = func_prototype,
152164 .owner_decl = s.decl_index,
153165 .teg_body_inst = s.decl.teg_decl_index.unwrap().?,
154166 },
155167 });
156 const value = Value.fromInterned(func_value);
157168 s.decl.val = value;
158169 return value;
159170}
......@@ -221,11 +232,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
221232 },
222233 .number => {
223234 const number: dod.Float = s.code.instPayload(.number, in);
224 return s.makeValueAir(.{ .float = number.decode() });
235 return s.makeValueAir(.{ .comptime_float = number.decode() });
225236 },
226237 .js => {
227 const operand = try s.resolveRefComptime(s.code.instData(.js, in).operand);
228 try s.validateType(operand.type(s.ip), .string_type);
238 const operand_ref = s.code.instData(.js, in).operand;
239 const operand = try s.resolveRefComptime(operand_ref);
240 // const operand_str = try s.coerceValue(operand_ref.toIndex().?, operand, .string);
229241 return s.addInstRef(.js_global_ref, operand.asString(s.ip));
230242 },
231243 .field => {
......@@ -233,12 +245,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
233245 const base = try s.resolveRefRuntime(field.lhs);
234246 const rhs = try s.resolveRefComptime(field.rhs);
235247
236 try s.validateType(rhs.type(s.ip), .string_type);
248 // try s.validateType(rhs.type(s.ip), .string_type);
237249 const string = rhs.asString(s.ip);
238250
239251 const base_type = s.typeOfRef(base);
240252 switch (base_type) {
241 .opaque_type => {
253 .@"opaque" => {
242254 return s.addInstRef(.field, .{
243255 .base = base,
244256 .field = try string.tryToNullTerminatedString(s.ip, s.gpa),
......@@ -259,12 +271,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
259271 return error.AnalysisFail;
260272 };
261273 const resolved = try s.resolveDecl(decl_index);
262 return resolved.toIntern().toAir();
274 return resolved.toAir();
263275 },
264276 .param => {
265277 const param: Teg.Inst.DataNode = s.code.instData(.param, in);
266278 return s.addInstRef(.arg, .{
267 .type = Value.fromInterned(s.params[param.data]),
279 .type = s.params[param.data],
268280 .arg_number = param.data,
269281 });
270282 },
......@@ -325,83 +337,86 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
325337 });
326338 },
327339 .call => {
328 const call: Teg.Inst.Call = s.code.instPayload(.call, in);
329 const base = try s.resolveRefRuntime(call.base);
330 const base_type = s.typeOfRef(base).toKey(s.ip);
331
332 const is_opaque = base_type == .simple_type and base_type.simple_type == .@"opaque";
333
334 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
335 defer s.arena.free(scratch);
336 if (!is_opaque) {
337 if (base_type != .type_function) {
338 std.debug.print("Attempt to call non-function {}", .{base_type});
339 return error.AnalysisFail;
340 }
341
342 const proto = base_type.type_function;
343 if (proto.param_types.len != call.args.len) {
344 std.debug.print("Incorrect args. Expect {d}, Got {d}", .{
345 proto.param_types.len,
346 call.args.len,
347 });
348 return error.AnalysisFail;
349 }
350
351 // validate function args
352 for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
353 const ref = try s.resolveRefRuntime(arg);
354 scratch[i] = ref;
355 try s.validateType(
356 Value.fromInterned(s.typeOfRef(ref)),
357 param,
358 );
359 }
360 } else {
361 // just copy the args in without checking
362 for (call.args.slice(&s.code), 0..) |arg, i| {
363 const ref = try s.resolveRefRuntime(arg);
364 scratch[i] = ref;
365 }
366 }
367
368 const start = s.air_extra.list.items.len;
369 try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));
370
371 return s.addInstRef(.call, .{
372 .base = base,
373 .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{
374 .args = .{
375 .start = @intCast(start),
376 .len = @intCast(scratch.len),
377 },
378 }),
379 });
340 @panic("TODO: re-implement .call");
341 // const call: Teg.Inst.Call = s.code.instPayload(.call, in);
342 // const base = try s.resolveRefRuntime(call.base);
343 // const base_type = s.typeOfRef(base).toKey(s.ip);
344
345 // const is_opaque = base_type == .simple_type and base_type.simple_type == .@"opaque";
346
347 // const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
348 // defer s.arena.free(scratch);
349 // if (!is_opaque) {
350 // if (base_type != .type_function) {
351 // std.debug.print("Attempt to call non-function {}", .{base_type});
352 // return error.AnalysisFail;
353 // }
354
355 // const proto = base_type.type_function;
356 // if (proto.param_types.len != call.args.len) {
357 // std.debug.print("Incorrect args. Expect {d}, Got {d}", .{
358 // proto.param_types.len,
359 // call.args.len,
360 // });
361 // return error.AnalysisFail;
362 // }
363
364 // // validate function args
365 // for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
366 // const ref = try s.resolveRefRuntime(arg);
367 // scratch[i] = ref;
368 // _ = param;
369 // // try s.validateType(
370 // // Value.fromInterned(s.typeOfRef(ref)),
371 // // param,
372 // // );
373 // }
374 // } else {
375 // // just copy the args in without checking
376 // for (call.args.slice(&s.code), 0..) |arg, i| {
377 // const ref = try s.resolveRefRuntime(arg);
378 // scratch[i] = ref;
379 // }
380 // }
381
382 // const start = s.air_extra.list.items.len;
383 // try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));
384
385 // return s.addInstRef(.call, .{
386 // .base = base,
387 // .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{
388 // .args = .{
389 // .start = @intCast(start),
390 // .len = @intCast(scratch.len),
391 // },
392 // }),
393 // });
380394 },
381395 .ensure_void => {
382396 const operand = try s.resolveRefRuntimeExtra(s.code.instData(.ensure_void, in).operand, false);
383 const t = Value.fromInterned(s.typeOfRef(operand));
384 if (!t.isEnsureVoid(s.ip)) {
385 std.debug.print("oh no, we are discarding {} on accident\n", .{
386 t.fmt(s.ip),
387 });
388 }
389 return InternPool.Index.void_value.toAir();
397 const t = s.typeOfRef(operand);
398 _ = t;
399 // if (!t.isEnsureVoid(s.ip)) {
400 // std.debug.print("oh no, we are discarding {} on accident\n", .{
401 // t.fmt(s.ip),
402 // });
403 // }
404 return InternPool.Value.void.toAir();
390405 },
391406 .discard => {
392407 const operand = try s.resolveRefRuntimeExtra(s.code.instData(.discard, in).operand, false);
393 const t = Value.fromInterned(s.typeOfRef(operand));
394 if (t.ip_index == .void_value) {
408 const t = s.typeOfRef(operand);
409 if (t == .void) {
395410 std.debug.print("oh no, we have a useless _ = void;\n", .{});
396411 }
397 return InternPool.Index.void_value.toAir();
412 return InternPool.Value.void.toAir();
398413 },
399414 .@"if" => {
400415 const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code);
401416
402417 const cond = try s.resolveRefRuntime(data.cond);
403418
404 try s.validateType(Value.fromInterned(s.typeOfRef(cond)), .bool_type);
419 // try s.validateType(s.typeOfRef(cond), .bool_type);
405420
406421 // If known at comptime, only analyze the taken path
407422 if (cond.kind == .value) {
......@@ -417,7 +432,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
417432 .data = try s.air_extra.append(s.gpa, Air.If.Data, .{
418433 .yes = yes_block,
419434 .no = no_block,
420 .union_ty = s.unionType(Value.fromInterned(s.typeOfRef(yes_block)), Value.fromInterned(s.typeOfRef(no_block))),
435 .union_ty = s.unionType(s.typeOfRef(yes_block), s.typeOfRef(no_block)),
421436 }),
422437 });
423438 },
......@@ -434,11 +449,24 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
434449 .memory => {
435450 const data = s.code.instData(.memory, in);
436451
437 const initial_value = try s.resolveRefRuntime(data.operand);
452 var initial_value = try s.resolveRefRuntime(data.operand);
453
454 switch (initial_value.decode()) {
455 .value => |v| switch (v.toKey(s.ip)) {
456 .comptime_float => |val| {
457 try s.report.warn(.numeric_int_var_ambiguity, .{
458 .node = s.nodeLoc(in),
459 });
460 initial_value = (try s.makeValue(.{ .float = val })).toAir();
461 },
462 else => {},
463 },
464 else => {},
465 }
438466
439467 return try s.addInstRef(.memory, .{
440468 .init = initial_value,
441 .ty = s.typeOfRef(initial_value).toValue(),
469 .ty = s.typeOfRef(initial_value),
442470 });
443471 },
444472 .store => {
......@@ -448,16 +476,22 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
448476 const location = s.mapTeg(data.memory.toRef()).decode().inst;
449477 const operand = try s.resolveRefRuntime(data.operand);
450478
451 try s.validateType(
452 s.typeOfRef(operand).toValue(),
453 s.tempAir().instData(.memory, location).ty.ip_index,
454 );
479 // try s.validateType(
480 // s.typeOfRef(operand).toValue(),
481 // s.tempAir().instData(.memory, location).ty.ip_index,
482 // );
455483
456484 return try s.addInstRef(.store, .{
457485 .memory = location,
458486 .operand = operand,
459487 });
460488 },
489 .as => {
490 const as: Teg.Inst.As = s.code.instPayload(.as, in);
491 const coerce_to = try s.resolveRefComptime(as.type);
492 const operand = try s.resolveRefRuntime(as.operand);
493 return try s.coerceRef(in, operand, coerce_to.toType(s.ip).?);
494 },
461495 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
462496 }
463497}
......@@ -498,7 +532,7 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
498532
499533fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.Inst.Ref {
500534 const index = ref.toIndex() orelse {
501 return ref.toInterned().?.toAir();
535 return ref.toValue().?.toAir();
502536 };
503537
504538 if (s.inst_map.get(index)) |air| {
......@@ -519,20 +553,21 @@ fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
519553}
520554
521555fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
522 const in = ref.toIndex() orelse {
523 return Value{ .ip_index = ref.toInterned().? };
556 const index = switch (ref.decode()) {
557 .value => |value| return value,
558 .index => |index| index,
524559 };
525560
526 if (s.inst_map.get(in)) |air| {
561 if (s.inst_map.get(index)) |air| {
527562 switch (air.decode()) {
528563 .value => |value| return value,
529564 else => {},
530565 }
531566 }
532567
533 switch (s.code.instTag(in)) {
568 switch (s.code.instTag(index)) {
534569 .string => {
535 const string: Teg.SlicedString = s.code.instPayload(.string, in);
570 const string: Teg.SlicedString = s.code.instPayload(.string, index);
536571 return s.makeValue(.{ .string = string.slice(s.code.string_bytes) });
537572 },
538573
......@@ -540,6 +575,70 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
540575 }
541576}
542577
578/// Coerce `value` runtime ref to `coerce_type`, and emit an error at `teg_ref`'s location
579fn coerceRef(
580 s: *Sema,
581 teg_ref: Teg.Inst.Index,
582 value: Air.Inst.Ref,
583 coerce_to: Type,
584) !Air.Inst.Ref {
585 _ = teg_ref;
586 _ = s;
587 _ = value;
588 _ = coerce_to;
589 // const value_type = s.typeOfRef(value);
590 // switch (coerce_type.toKey(s.ip)) {
591 // .simple_type => |simple_type| switch (simple_type) {
592 // // .@"opaque",
593 // .int32 => {
594 // switch (value_type.toKey(s.ip)) {
595 // .simple_type => switch (simple_type) {
596 // // .int32 => return value,
597 // // .numeric_literal => {
598 // // const val = value.decode().value.toKey(s.ip).comptime_float;
599 // // std.debug.print("buh {d}\n", .{val});
600 // // if (!math.isFinite(val) or
601 // // val < math.minInt(i32) or
602 // // val > math.maxInt(i32))
603 // // {
604 // // try s.fail(.comptime_float_to_int, .{
605 // // .val = val,
606 // // .src = s.nodeLoc(teg_ref),
607 // // });
608 // // }
609 // // return (try s.makeValue(.{ .int32 = @intFromFloat(val) })).toAir();
610 // // },
611 // else => |tag| std.debug.panic("TODO: coerceTo value_type simple_type .{s}", .{@tagName(tag)}),
612 // },
613 // else => |tag| std.debug.panic("TODO: coerceTo value_type .{s}", .{@tagName(tag)}),
614 // }
615 // },
616 // // .float,
617 // // .string,
618 // // .bool,
619 // // .noreturn,
620 // // .type,
621 // // .numeric_literal,
622 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .simple_type = .{s}", .{@tagName(tag)}),
623 // },
624 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .{s}", .{@tagName(tag)}),
625 // }
626 @panic("TODO");
627}
628
629fn coerceValue(
630 s: *Sema,
631 teg_ref: Teg.Inst.Index,
632 value: Value,
633 coerce_to: Type,
634) !Value {
635 _ = s;
636 _ = teg_ref;
637 _ = value;
638 _ = coerce_to;
639 @panic("TODO");
640}
641
543642fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value {
544643 const decl = s.ip.declPtr(decl_index);
545644 switch (decl.analysis) {
......@@ -579,27 +678,22 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value {
579678 }
580679}
581680
582fn unionType(s: *Sema, a: Value, b: Value) Value {
583 if (std.debug.runtime_safety) {
584 assert(a.type(s.ip).toIntern() == .type_type);
585 assert(b.type(s.ip).toIntern() == .type_type);
586 }
587
588 if (a.ip_index == b.ip_index) {
681fn unionType(s: *Sema, a: Type, b: Type) Type {
682 if (a == b) {
589683 return a;
590684 }
591685
592 if (a.ip_index == .noreturn_type)
686 if (a == .noreturn)
593687 return b;
594688
595 if (b.ip_index == .noreturn_type)
689 if (b == .noreturn)
596690 return a;
597691
598692 std.debug.panic(
599693 "TODO: when two non-equal types implicitly merge (if/match), this should generate a union type.\na: {}\nb: {}",
600694 .{
601 a.fmt(s.ip),
602 b.fmt(s.ip),
695 a.dump(s.ip),
696 b.dump(s.ip),
603697 },
604698 );
605699 // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } });
......@@ -650,7 +744,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
650744 else => comptime unreachable,
651745 };
652746 return @as(
653 InternPool.Index,
747 InternPool.Value,
654748 if (result) .bool_true else .bool_false,
655749 ).toAir();
656750 },
......@@ -660,7 +754,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
660754 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq");
661755 const result = l == r;
662756 return @as(
663 InternPool.Index,
757 InternPool.Value,
664758 if (result) .bool_true else .bool_false,
665759 ).toAir();
666760 },
......@@ -709,15 +803,15 @@ fn setInstData(
709803}
710804
711805fn makeValue(s: *Sema, key: InternPool.Key) !Value {
712 return .{ .ip_index = try s.ip.get(s.gpa, key) };
806 return try s.ip.get(s.gpa, key);
713807}
714808
715809fn makeValueAir(s: *Sema, key: InternPool.Key) !Air.Inst.Ref {
716 return (try s.makeValue(key)).ip_index.toAir();
810 return (try s.makeValue(key)).toAir();
717811}
718812
719813fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref {
720 if (inst.toInterned()) |interned| {
814 if (inst.toValue()) |interned| {
721815 return interned.toAir();
722816 }
723817 const index = inst.toIndex().?;
......@@ -729,13 +823,6 @@ fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref {
729823 };
730824}
731825
732fn validateType(s: *Sema, checked_type: Value, wanted_type: InternPool.Index) !void {
733 _ = s; // autofix
734 if (checked_type.toIntern() != wanted_type) {
735 return error.TypeCheckFailed;
736 }
737}
738
739826fn scratchToIndexList(s: *Sema, scratch_top: usize) !Air.IndexList {
740827 const items = s.scratch.items[scratch_top..];
741828 const extra_top = s.air_extra.list.items.len;
......@@ -753,7 +840,7 @@ fn tempAir(s: *const Sema) Air {
753840 };
754841}
755842
756fn typeOfRef(s: *Sema, ref: Air.Inst.Ref) InternPool.Index {
843fn typeOfRef(s: *Sema, ref: Air.Inst.Ref) InternPool.Type {
757844 return s.tempAir().typeOf(ref, s.ip);
758845}
759846
......@@ -769,10 +856,28 @@ fn lookupDecl(s: *Sema, name: NullTerminatedString) ?InternPool.DeclIndex {
769856 }
770857}
771858
859fn nodeLoc(s: *const Sema, in: Teg.Inst.Index) Report.LazyNode {
860 return s.code.instSrcLoc(in, s.decl_index);
861}
862
863pub fn addWarn(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void {
864 try s.report.warn(kind, data);
865}
866
867pub fn addError(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void {
868 try s.report.err(kind, data);
869}
870
871pub fn fail(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) Error!void {
872 try s.report.err(kind, data);
873 return Error.AnalysisFail;
874}
875
772876const Sema = @This();
773877
774878const std = @import("std");
775879const mem = std.mem;
880const math = std.math;
776881const Allocator = std.mem.Allocator;
777882const assert = std.debug.assert;
778883const log = std.log.scoped(.sema);
......@@ -791,14 +896,12 @@ const Decl = Comp.Decl;
791896const Namespace = Comp.Namespace;
792897
793898const Air = @import("Air.zig");
794
795899const Teg = @import("Teg.zig");
900const Report = @import("Report.zig");
901const dod = @import("dod.zig");
902const tracer = @import("tracer");
796903
797const Value = @import("Value.zig");
798904const InternPool = @import("InternPool.zig");
905const Value = InternPool.Value;
906const Type = InternPool.Type;
799907const NullTerminatedString = InternPool.NullTerminatedString;
800const dod = @import("dod.zig");
801
802const Report = @import("Report.zig");
803
804const tracer = @import("tracer");
src/compiler/Teg.Dump.zig+8
......@@ -22,6 +22,14 @@ const State = struct {
2222
2323 const tag = s.teg.instTag(inst);
2424
25 try tty.setColor(writer, .bright_black);
26 const node_offset = teg.instSrcLoc(inst, @enumFromInt(0)).node_offset;
27 try writer.print("{c}{d} ", .{
28 @as(u8, if (node_offset >= 0) '+' else '-'),
29 @abs(node_offset),
30 });
31 try tty.setColor(writer, .reset);
32
2533 try tty.setColor(writer, .cyan);
2634 try writer.print("{s}", .{@tagName(tag)});
2735 try tty.setColor(writer, .reset);
src/compiler/Teg.zig+42-14
......@@ -209,11 +209,11 @@ pub const Inst = struct {
209209 int32_type,
210210 float_type,
211211 string_type,
212 void_type,
213212 noreturn_type,
214213 type_type,
214 numeric_literal_type,
215215
216 void_value,
216 void,
217217 bool_true,
218218 bool_false,
219219 int32_zero,
......@@ -227,9 +227,22 @@ pub const Inst = struct {
227227
228228 _,
229229
230 pub fn toIndex(inst: Ref) ?Index {
231 assert(inst != .none);
232 const ref_int = @intFromEnum(inst);
230 pub const Decoded = union(enum) {
231 value: InternPool.Value,
232 index: Teg.Inst.Index,
233 };
234
235 pub fn decode(ref: Ref) Decoded {
236 const ref_int = @intFromEnum(ref);
237 return if (ref_int >= Index.static_len)
238 .{ .index = @enumFromInt(ref_int - Index.static_len) }
239 else
240 .{ .value = @enumFromInt(ref_int) };
241 }
242
243 pub fn toIndex(ref: Ref) ?Index {
244 assert(ref != .none);
245 const ref_int = @intFromEnum(ref);
233246 if (ref_int >= Index.static_len) {
234247 return @enumFromInt(ref_int - Index.static_len);
235248 } else {
......@@ -242,7 +255,7 @@ pub const Inst = struct {
242255 return toIndex(inst);
243256 }
244257
245 pub fn toInterned(inst: Ref) ?InternPool.Index {
258 pub fn toValue(inst: Ref) ?InternPool.Value {
246259 const ref_int = @intFromEnum(inst);
247260
248261 // todo: is this > or >=
......@@ -383,18 +396,30 @@ pub fn declData(teg: *const Teg, index: Inst.Index) Inst.Decl {
383396 return teg.instData(.declaration, index).extra_index.get(teg);
384397}
385398
399pub fn instSrcLoc(teg: *const Teg, inst: Inst.Index, decl: Comp.Decl.Index) LazyNode {
400 switch (teg.instTag(inst)) {
401 inline else => |tag| {
402 const data = teg.instData(tag, inst);
403 return .{
404 .node_offset = data.src_node,
405 .decl = decl,
406 };
407 },
408 }
409}
410
386411pub fn moduleDeclarations(teg: *const Teg, module_index: Inst.Index) []const Inst.Index {
387412 const module = teg.instData(.module, module_index);
388413 const module_data: IndexList = module.extra_index.get(teg);
389414 const slice = module_data.slice(teg);
390 // if (std.debug.runtime_safety) {
391 // for (slice) |item| {
392 // if (teg.instTag(item) != .declaration) {
393 // std.debug.print("\ngot unknown tag {s}\n", .{@tagName(teg.instTag(item))});
394 // unreachable;
395 // }
396 // }
397 // }
415 if (std.debug.runtime_safety) {
416 for (slice) |item| {
417 if (teg.instTag(item) != .declaration) {
418 std.debug.print("\ngot unknown tag {s}\n", .{@tagName(teg.instTag(item))});
419 unreachable;
420 }
421 }
422 }
398423 return slice;
399424}
400425
......@@ -441,4 +466,7 @@ const InternPool = @import("InternPool.zig");
441466
442467const Air = @import("Air.zig");
443468
469const Comp = @import("Comp.zig");
470
444471const Report = @import("Report.zig");
472const LazyNode = Report.LazyNode;
src/compiler/Tokenizer.zig+1-1
......@@ -192,7 +192,7 @@ pub const Token = struct {
192192 .pipe_pipe => "||",
193193 .percent => "%",
194194 .plus => "+",
195 .minus_lt => "<",
195 .minus_lt => "->",
196196 .semi => ";",
197197 .slash => "/",
198198 .star => "*",
src/compiler/Transform.zig+18-18
......@@ -202,7 +202,7 @@ const BlockContext = struct {
202202const Liveness = enum { alive, dead };
203203
204204const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{
205 .{ "void", .void_value },
205 .{ "void", .void },
206206 .{ "true", .bool_true },
207207 .{ "false", .bool_false },
208208 .{ "int", .int32_type },
......@@ -289,7 +289,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
289289
290290 trail.writeStruct(Inst.DeclFn, .{
291291 .params = .{ .start = 0, .len = 0 },
292 .return_type = Inst.Ref.void_type,
292 .return_type = Inst.Ref.void,
293293 .body = block.toRef(),
294294 });
295295
......@@ -378,7 +378,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
378378 const return_type = if (fn_decl.return_type) |return_type_node|
379379 try t.transformExpressionToBlock(decl, return_type_node, false)
380380 else
381 Inst.Ref.void_type;
381 Inst.Ref.void;
382382
383383 const body = try t.transformExpressionToBlock(decl, fn_decl.body, true);
384384
......@@ -528,7 +528,7 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc
528528 .node = param,
529529 .prev = sym.value_ptr.node,
530530 });
531 return .void_value;
531 return .void;
532532 }
533533
534534 sym.value_ptr.* = .{
......@@ -558,7 +558,7 @@ const Builtin = enum {
558558
559559 if (args.len != 1) {
560560 // try t.report.err(.invalid_builtin_args, .{ .id = "js" });
561 return .void_value;
561 return .void;
562562 }
563563
564564 const source = try t.transformExpression(decl, args[0]) orelse return null;
......@@ -600,7 +600,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
600600
601601 const lookup = t.lookupId(id) orelse {
602602 try t.report.err(.unknown_identifier, .{ .node = node });
603 return .void_value;
603 return .void;
604604 };
605605
606606 lookup.used = true;
......@@ -815,7 +815,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
815815 => |tag| {
816816 const statements = t.ast.nodeStatements(node);
817817
818 var last: Inst.Ref = .void_value;
818 var last: Inst.Ref = .void;
819819 for (statements, 1..) |statement, i| {
820820 const is_last = i == statements.len;
821821 last = try t.transformBlockStatement(
......@@ -827,14 +827,14 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
827827 };
828828 }
829829
830 return if (tag == .block_implicit_return) last else .void_value;
830 return if (tag == .block_implicit_return) last else .void;
831831 },
832832
833833 .builtin_call,
834834 => {
835835 const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse {
836836 try t.report.err(.invalid_builtin, .{ .node = node });
837 return .void_value;
837 return .void;
838838 };
839839
840840 const base = t.ast.nodeData(node).list;
......@@ -939,9 +939,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
939939 false,
940940 )
941941 else
942 Inst.Ref.void_value;
942 Inst.Ref.void;
943943
944 if (yes == .void_value and no == .void_value) {
944 if (yes == .void and no == .void) {
945945 // when both branches do nothing, we can omit the branch,
946946 // however we still need to type-check the condition operand
947947 _ = try t.addInstRef(.{
......@@ -951,7 +951,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
951951 .operand = cond,
952952 }),
953953 });
954 return .void_value;
954 return .void;
955955 }
956956
957957 return try t.addInstRef(.{
......@@ -990,7 +990,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
990990 try t.transformExpression(decl.withReturn(null), expr) orelse
991991 return null
992992 else
993 .void_value;
993 .void;
994994
995995 _ = try t.addInstRef(.{
996996 .tag = .@"break",
......@@ -1022,7 +1022,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
10221022 try t.transformExpression(decl.withReturn(null), expr) orelse
10231023 return null
10241024 else
1025 .void_value;
1025 .void;
10261026
10271027 _ = try t.addInstRef(.{
10281028 .tag = .@"break",
......@@ -1063,12 +1063,12 @@ fn transformBlockStatement(
10631063 if (mem.eql(u8, var_decl.id, "_")) {
10641064 // TODO: relax this restriction into just creating a discard node
10651065 try t.report.err(.invalid_discard, .{ .node = node });
1066 return .void_value;
1066 return .void;
10671067 }
10681068
10691069 if (BuiltInIds.has(var_decl.id)) {
10701070 try t.report.err(.shadow_builtin_variable, .{ .node = node });
1071 return .void_value;
1071 return .void;
10721072 }
10731073
10741074 const type_ref = if (var_decl.type) |data_type|
......@@ -1132,7 +1132,7 @@ fn transformBlockStatement(
11321132 },
11331133 };
11341134
1135 return .void_value;
1135 return .void;
11361136 },
11371137
11381138 .fn_decl,
......@@ -1188,7 +1188,7 @@ fn transformBlockStatement(
11881188 const result = try t.transformExpression(decl, node) orelse
11891189 return null;
11901190
1191 if (!is_implicit_return_expression and result != .void_value) {
1191 if (!is_implicit_return_expression and result != .void) {
11921192 return try t.addInstRef(.{
11931193 .tag = .ensure_void,
11941194 .data = EncodedData.encode(.ensure_void, .{
src/compiler/Value.zig deleted-96
......@@ -1,96 +0,0 @@
1ip_index: InternPool.Index,
2
3pub fn fromInterned(i: InternPool.Index) Value {
4 assert(i != .none);
5 return .{ .ip_index = i };
6}
7
8pub fn toIntern(val: Value) InternPool.Index {
9 assert(val.ip_index != .none);
10 return val.ip_index;
11}
12
13pub fn toKey(val: Value, ip: *const InternPool) InternPool.Key {
14 return val.toIntern().toKey(ip);
15}
16
17pub fn asBool(val: Value) i32 {
18 return switch (val.toIntern()) {
19 .bool_true => true,
20 .bool_false => false,
21 else => unreachable,
22 };
23}
24
25pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
26 const index = val.toIntern();
27 return ip.items.items(.data)[@intFromEnum(index)].decode(
28 .string,
29 ip.items.items(.tag)[@intFromEnum(index)],
30 )
31 .get(ip);
32}
33
34pub fn @"type"(val: Value, ip: *const InternPool) Value {
35 const v: InternPool.Index = switch (val.toKey(ip)) {
36 .simple_type, .type_function => .type_type,
37
38 .int32 => .int32_type,
39 .float => .float_type,
40 .string => .string_type,
41 .simple_value => |v| switch (v) {
42 .true, .false => .bool_type,
43 .void => .void_type,
44 },
45 .func => |f| f.ty,
46 };
47 return Value.fromInterned(v);
48}
49
50pub fn toFloat(val: Value, ip: *const InternPool) ?f64 {
51 return switch (val.toKey(ip)) {
52 .int32 => |i| @floatFromInt(i),
53 .float => |f| f,
54 else => null,
55 };
56}
57
58/// used for validating ensure_void instructions
59pub fn isEnsureVoid(val: Value, ip: *const InternPool) bool {
60 const intern = val.toIntern();
61 const i = @intFromEnum(intern);
62
63 return switch (ip.items.items(.tag)[i]) {
64 .simple_type => switch (ip.items.items(.data)[i].decode(.simple_type, .simple_type)) {
65 .@"opaque", .void => true,
66 else => true,
67 },
68 else => false,
69 };
70}
71
72const Value = @This();
73
74const std = @import("std");
75const assert = std.debug.assert;
76
77pub const InternPool = @import("InternPool.zig");
78
79pub fn fmt(v: Value, ip: *InternPool) Format {
80 return .{ .value = v, .ip = ip };
81}
82
83pub const Format = struct {
84 value: Value,
85 ip: *InternPool,
86
87 pub fn format(
88 self: Format,
89 comptime unused_fmt_str: []const u8,
90 _: std.fmt.FormatOptions,
91 writer: anytype,
92 ) !void {
93 comptime assert(unused_fmt_str.len == 0);
94 try writer.print("{any}", .{self.value.toKey(self.ip)});
95 }
96};
src/compiler/dod.zig+9-1
......@@ -240,7 +240,15 @@ pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutabil
240240 return ip.get(T, trail.index);
241241 }
242242
243 pub fn readInternPoolIndexSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Index.Slice {
243 pub fn readValueSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Value.Slice {
244 defer trail.index += len;
245 return .{
246 .start = @intCast(trail.index),
247 .len = len,
248 };
249 }
250
251 pub fn readTypeSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Type.Slice {
244252 defer trail.index += len;
245253 return .{
246254 .start = @intCast(trail.index),
src/compiler/util.zig+22-1
......@@ -1,4 +1,3 @@
1/// TODO: move to io utility file
21pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config {
32 if (@hasField(@TypeOf(writer), "unbuffered_writer")) {
43 return detectTTYFromWriter(writer.unbuffered_writer);
......@@ -17,4 +16,26 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config {
1716 return .no_color;
1817}
1918
19pub fn Ordinal(Int: type) type {
20 comptime assert(@typeInfo(Int).Int.signedness == .unsigned);
21 return enum(Int) {
22 _,
23 pub fn fromZeroBased(int: Int) @This() {
24 assert(int != std.math.maxInt(Int));
25 return @enumFromInt(int);
26 }
27 pub fn fromOneBased(int: Int) @This() {
28 assert(int > 0);
29 return @enumFromInt(int - 1);
30 }
31 pub fn zeroBased(ord: @This()) Int {
32 return @intFromEnum(ord);
33 }
34 pub fn oneBased(ord: @This()) Int {
35 return @intFromEnum(ord) + 1;
36 }
37 };
38}
39
2040const std = @import("std");
41const assert = std.debug.assert;