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; |
| 46 | 46 | @js("console").log(hello); |
| 47 | 47 | ``` |
| 48 | 48 | |
| 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: | |
| 50 | 50 | |
| 51 | 51 | ``` |
| 52 | 52 | var message = "first message"; |
| ... | ... | @@ -58,7 +58,7 @@ if @js("Math").random() > 0.5 { |
| 58 | 58 | @js("console").log(message); |
| 59 | 59 | ``` |
| 60 | 60 | |
| 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: | |
| 61 | Using `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: | |
| 62 | 62 | |
| 63 | 63 | ``` |
| 64 | 64 | let message = if @js("Math").random() > 0.5 { |
| ... | ... | @@ -70,10 +70,10 @@ let message = if @js("Math").random() > 0.5 { |
| 70 | 70 | @js("console").log(message); |
| 71 | 71 | ``` |
| 72 | 72 | |
| 73 | The 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. | |
| 73 | It 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. | |
| 74 | 74 | |
| 75 | 75 | ``` |
| 76 | var a = 5; // error | |
| 77 | var b: int = 5; // ok | |
| 78 | var c: float = 5; // ok | |
| 76 | var number = 0; // `number` is a `float`, but not explicit | |
| 77 | var number: int = 0; // `number` is `int` | |
| 78 | var number: float = 0; // `number` is `float` | |
| 79 | 79 | ``` |
demo.wl created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | let x: int = 1.5; |
notes created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | 0.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) | |
| 7 | 0.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 | |
| 14 | 0.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 @@ |
| 2 | 2 | air: Air, |
| 3 | 3 | ip: *const InternPool, |
| 4 | 4 | |
| 5 | const State = struct { | |
| 5 | pub const State = struct { | |
| 6 | 6 | arena: std.mem.Allocator, |
| 7 | 7 | air: *const Air, |
| 8 | 8 | ip: *const InternPool, |
| ... | ... | @@ -67,7 +67,7 @@ const State = struct { |
| 67 | 67 | try w.print("{d}", .{arg.arg_number}); |
| 68 | 68 | try tty.setColor(w, .reset); |
| 69 | 69 | try w.writeAll(": "); |
| 70 | try s.dumpValue(w, arg.type); | |
| 70 | try s.dumpValue(w, arg.type.value()); | |
| 71 | 71 | }, |
| 72 | 72 | .@"break" => { |
| 73 | 73 | const brk: Air.Break = air.instData(.@"break", inst); |
| ... | ... | @@ -113,7 +113,7 @@ const State = struct { |
| 113 | 113 | try w.writeAll("("); |
| 114 | 114 | try s.dumpRef(w, memory.init); |
| 115 | 115 | try w.writeAll(", type: "); |
| 116 | try s.dumpValue(w, memory.ty); | |
| 116 | try s.dumpValue(w, memory.ty.value()); | |
| 117 | 117 | try w.writeAll(")"); |
| 118 | 118 | }, |
| 119 | 119 | .field => { |
| ... | ... | @@ -175,7 +175,7 @@ const State = struct { |
| 175 | 175 | try s.tty.setColor(w, .reset); |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | fn dumpValue(s: *State, w: anytype, val: Value) !void { | |
| 178 | pub fn dumpValue(s: *State, w: anytype, val: Value) !void { | |
| 179 | 179 | const tty = s.tty; |
| 180 | 180 | switch (val.toKey(s.ip)) { |
| 181 | 181 | .simple_type => |t| { |
| ... | ... | @@ -198,14 +198,14 @@ const State = struct { |
| 198 | 198 | try w.writeAll(","); |
| 199 | 199 | try tty.setColor(w, .reset); |
| 200 | 200 | } |
| 201 | try s.dumpValue(w, Value.fromInterned(param)); | |
| 201 | try s.dumpValue(w, param.value()); | |
| 202 | 202 | } |
| 203 | 203 | try tty.setColor(w, .blue); |
| 204 | 204 | try w.writeAll(") -> "); |
| 205 | 205 | try tty.setColor(w, .reset); |
| 206 | try s.dumpValue(w, Value.fromInterned(ty.return_type)); | |
| 206 | try s.dumpValue(w, ty.return_type.value()); | |
| 207 | 207 | }, |
| 208 | inline .float, .int32 => |data, tag| { | |
| 208 | inline .float, .int32, .comptime_float => |data, tag| { | |
| 209 | 209 | try tty.setColor(w, .bright_black); |
| 210 | 210 | try w.writeAll("<"); |
| 211 | 211 | try tty.setColor(w, .blue); |
| ... | ... | @@ -235,7 +235,7 @@ const State = struct { |
| 235 | 235 | .func => |func| { |
| 236 | 236 | try tty.setColor(w, .bright_black); |
| 237 | 237 | try w.writeAll("<"); |
| 238 | try s.dumpValue(w, Value.fromInterned(func.ty)); | |
| 238 | try s.dumpValue(w, func.ty); | |
| 239 | 239 | try tty.setColor(w, .bright_black); |
| 240 | 240 | try w.writeAll(", "); |
| 241 | 241 | const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip); |
| ... | ... | @@ -310,8 +310,7 @@ const assert = std.debug.assert; |
| 310 | 310 | const Air = @import("Air.zig"); |
| 311 | 311 | const Inst = Air.Inst; |
| 312 | 312 | |
| 313 | const Value = @import("Value.zig"); | |
| 314 | ||
| 315 | 313 | const InternPool = @import("InternPool.zig"); |
| 314 | const Value = InternPool.Value; | |
| 316 | 315 | |
| 317 | 316 | const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter; |
src/compiler/Air.zig+22-21| ... | ... | @@ -77,7 +77,7 @@ pub const Inst = struct { |
| 77 | 77 | |
| 78 | 78 | pub fn decode(ref: Ref) Decoded { |
| 79 | 79 | return switch (ref.kind) { |
| 80 | .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) }, | |
| 80 | .value => .{ .value = @enumFromInt(ref.data) }, | |
| 81 | 81 | .inst => .{ .inst = @enumFromInt(ref.data) }, |
| 82 | 82 | }; |
| 83 | 83 | } |
| ... | ... | @@ -97,7 +97,7 @@ pub const Inst = struct { |
| 97 | 97 | |
| 98 | 98 | pub fn isVoid(ref: Ref) bool { |
| 99 | 99 | return ref.kind == .value and |
| 100 | ref.decode().value.toIntern() == .void_value; | |
| 100 | ref.decode().value == .void; | |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | 103 | pub fn eql(ref: Ref, other: Ref) bool { |
| ... | ... | @@ -203,7 +203,7 @@ pub const Call = struct { |
| 203 | 203 | }; |
| 204 | 204 | |
| 205 | 205 | pub const Arg = struct { |
| 206 | type: Value, | |
| 206 | type: Type, | |
| 207 | 207 | arg_number: u32, |
| 208 | 208 | }; |
| 209 | 209 | |
| ... | ... | @@ -214,13 +214,13 @@ pub const If = struct { |
| 214 | 214 | pub const Data = struct { |
| 215 | 215 | yes: Inst.Ref, |
| 216 | 216 | no: Inst.Ref, |
| 217 | union_ty: Value, | |
| 217 | union_ty: Type, | |
| 218 | 218 | }; |
| 219 | 219 | }; |
| 220 | 220 | |
| 221 | 221 | pub const Memory = struct { |
| 222 | 222 | init: Inst.Ref, |
| 223 | ty: Value, | |
| 223 | ty: Type, | |
| 224 | 224 | }; |
| 225 | 225 | |
| 226 | 226 | pub 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 |
| 249 | 249 | return air.instData(tag, index).extra_index.get(air); |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index { | |
| 252 | pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Type { | |
| 253 | 253 | return switch (ref.decode()) { |
| 254 | .value => |v| v.type(ip).toIntern(), | |
| 254 | .value => |v| v.typeOf(ip), | |
| 255 | 255 | .inst => |i| air.typeOfIndex(i, ip), |
| 256 | 256 | }; |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPool.Index { | |
| 259 | pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPool.Type { | |
| 260 | 260 | return switch (air.instTag(index)) { |
| 261 | .js_global_ref => .opaque_type, | |
| 261 | .js_global_ref => .@"opaque", | |
| 262 | 262 | |
| 263 | 263 | .block => { |
| 264 | 264 | const items = air.instData(.block, index).slice(air); |
| ... | ... | @@ -267,28 +267,28 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo |
| 267 | 267 | |
| 268 | 268 | inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip), |
| 269 | 269 | |
| 270 | .lt, .gt, .eq, .bool_not => .bool_type, | |
| 270 | .lt, .gt, .eq, .bool_not => .bool, | |
| 271 | 271 | |
| 272 | .arg => air.instData(.arg, index).type.toIntern(), | |
| 272 | .arg => air.instData(.arg, index).type, | |
| 273 | 273 | |
| 274 | 274 | .negate => air.typeOf(air.instData(.negate, index).operand, ip), |
| 275 | 275 | |
| 276 | .@"break" => .noreturn_type, | |
| 276 | .@"break" => .noreturn, | |
| 277 | 277 | |
| 278 | 278 | // todo: |
| 279 | .field => .opaque_type, | |
| 279 | .field => .@"opaque", | |
| 280 | 280 | |
| 281 | 281 | .call => { |
| 282 | 282 | 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; | |
| 285 | 285 | }, |
| 286 | 286 | |
| 287 | .memory => air.instData(.memory, index).ty.toIntern(), | |
| 287 | .memory => air.instData(.memory, index).ty, | |
| 288 | 288 | |
| 289 | .store => air.instData(.memory, air.instData(.store, index).memory).ty.toIntern(), | |
| 289 | .store => air.instData(.memory, air.instData(.store, index).memory).ty, | |
| 290 | 290 | |
| 291 | .@"if" => air.instData(.@"if", index).data.get(air).union_ty.toIntern(), | |
| 291 | .@"if" => air.instData(.@"if", index).data.get(air).union_ty, | |
| 292 | 292 | }; |
| 293 | 293 | } |
| 294 | 294 | |
| ... | ... | @@ -310,10 +310,11 @@ const std = @import("std"); |
| 310 | 310 | const mem = std.mem; |
| 311 | 311 | const assert = std.debug.assert; |
| 312 | 312 | |
| 313 | const Teg = @import("Teg.zig"); | |
| 314 | 313 | const InternPool = @import("InternPool.zig"); |
| 314 | const Value = InternPool.Value; | |
| 315 | const Type = InternPool.Type; | |
| 316 | ||
| 317 | const Teg = @import("Teg.zig"); | |
| 315 | 318 | const dod = @import("dod.zig"); |
| 316 | 319 | |
| 317 | 320 | const Unused = enum(u32) { zero = 0 }; |
| 318 | ||
| 319 | const Value = @import("Value.zig"); |
src/compiler/Comp.zig+1-2| ... | ... | @@ -214,7 +214,7 @@ pub const File = struct { |
| 214 | 214 | |
| 215 | 215 | pub const Decl = struct { |
| 216 | 216 | name: InternPool.NullTerminatedString, |
| 217 | val: Value, | |
| 217 | val: InternPool.Value, | |
| 218 | 218 | /// The direct parent namespace of the Decl. In the case of the Decl |
| 219 | 219 | /// corresponding to a file, this is the namespace of the struct, since |
| 220 | 220 | /// there is no parent. |
| ... | ... | @@ -432,7 +432,6 @@ const Ast = @import("Ast.zig"); |
| 432 | 432 | const Air = @import("Air.zig"); |
| 433 | 433 | const Teg = @import("Teg.zig"); |
| 434 | 434 | const Cache = @import("Cache.zig"); |
| 435 | const Value = @import("Value.zig"); | |
| 436 | 435 | const Sema = @import("Sema.zig"); |
| 437 | 436 | const Transform = @import("Transform.zig"); |
| 438 | 437 | const JsNodeGen = @import("JsNodeGen.zig"); |
src/compiler/InternPool.zig+156-41| ... | ... | @@ -78,21 +78,25 @@ pub const Tag = enum(u8) { |
| 78 | 78 | /// A function type |
| 79 | 79 | /// data is an extra index into a `FuncType` |
| 80 | 80 | type_function, |
| 81 | /// A type that can be represented with only an enum tag. | |
| 82 | /// data is SimpleType enum value. | |
| 83 | simple_type, | |
| 81 | 84 | |
| 82 | 85 | // --- before this are types --- |
| 83 | 86 | // --- past this are values --- |
| 84 | 87 | |
| 85 | /// A type that can be represented with only an enum tag. | |
| 86 | /// data is SimpleType enum value. | |
| 87 | simple_type, | |
| 88 | 88 | /// A value that can be represented with only an enum tag. |
| 89 | 89 | /// data is SimpleValue enum value. |
| 90 | 90 | 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, | |
| 91 | 95 | /// An int32 value |
| 92 | 96 | /// data is i32 |
| 93 | 97 | int32, |
| 94 | 98 | /// A float value |
| 95 | /// data is an extra index into an `Float` | |
| 99 | /// data is an extra index into a `Float` | |
| 96 | 100 | float, |
| 97 | 101 | /// A function |
| 98 | 102 | /// data is an extra index into a `FuncDecl` |
| ... | ... | @@ -122,7 +126,7 @@ pub const Tag = enum(u8) { |
| 122 | 126 | .simple_value, |
| 123 | 127 | .int32, |
| 124 | 128 | => unreachable, // does not have a payload |
| 125 | .float => Float, | |
| 129 | .float, .comptime_float => Float, | |
| 126 | 130 | .func => FuncDecl, |
| 127 | 131 | .type_function => TypeFunction, |
| 128 | 132 | .string => String.WithLen, |
| ... | ... | @@ -133,7 +137,7 @@ pub const Tag = enum(u8) { |
| 133 | 137 | pub const FuncDecl = struct { |
| 134 | 138 | analysis: FuncAnalysis, |
| 135 | 139 | owner_decl: DeclIndex, |
| 136 | ty: Index, | |
| 140 | ty: Value, | |
| 137 | 141 | teg_body_inst: TrackedInst.Index, |
| 138 | 142 | }; |
| 139 | 143 | |
| ... | ... | @@ -141,26 +145,26 @@ pub const Tag = enum(u8) { |
| 141 | 145 | /// 0. param_type: Index for each params_len |
| 142 | 146 | pub const TypeFunction = struct { |
| 143 | 147 | params_len: u32, |
| 144 | return_type: Index, | |
| 148 | return_type: Type, | |
| 145 | 149 | }; |
| 146 | 150 | }; |
| 147 | 151 | |
| 148 | pub 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; | |
| 152 | pub 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; | |
| 153 | 157 | |
| 154 | 158 | opaque_type, |
| 155 | 159 | bool_type, |
| 156 | 160 | int32_type, |
| 157 | 161 | float_type, |
| 158 | 162 | string_type, |
| 159 | void_type, | |
| 160 | 163 | noreturn_type, |
| 161 | 164 | type_type, |
| 165 | numeric_literal_type, | |
| 162 | 166 | |
| 163 | void_value, | |
| 167 | void, | |
| 164 | 168 | bool_true, |
| 165 | 169 | bool_false, |
| 166 | 170 | int32_zero, |
| ... | ... | @@ -178,36 +182,140 @@ pub const Index = enum(u32) { |
| 178 | 182 | start: u32, |
| 179 | 183 | len: u32, |
| 180 | 184 | |
| 181 | pub fn get(slice: Slice, ip: *const InternPool) []Index { | |
| 185 | pub fn get(slice: Slice, ip: *const InternPool) []Value { | |
| 182 | 186 | return @ptrCast(ip.extra.list.items[slice.start..][0..slice.len]); |
| 183 | 187 | } |
| 184 | 188 | }; |
| 185 | 189 | |
| 186 | 190 | /// Used for a map of `Index` values to the index within a list of `Index` values. |
| 187 | 191 | const Adapter = struct { |
| 188 | indexes: []const Index, | |
| 192 | indexes: []const Value, | |
| 189 | 193 | |
| 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 { | |
| 191 | 195 | _ = b_void; |
| 192 | 196 | return a == ctx.indexes[b_map_index]; |
| 193 | 197 | } |
| 194 | 198 | |
| 195 | pub fn hash(ctx: @This(), a: Index) u32 { | |
| 199 | pub fn hash(ctx: @This(), a: Value) u32 { | |
| 196 | 200 | _ = ctx; |
| 197 | 201 | return std.hash.uint32(@intFromEnum(a)); |
| 198 | 202 | } |
| 199 | 203 | }; |
| 200 | 204 | |
| 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 | }; | |
| 203 | 286 | } |
| 287 | }; | |
| 288 | ||
| 289 | pub 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 | _, | |
| 204 | 300 | |
| 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)); | |
| 207 | 315 | } |
| 208 | 316 | |
| 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); | |
| 211 | 319 | } |
| 212 | 320 | }; |
| 213 | 321 | |
| ... | ... | @@ -217,9 +325,9 @@ pub const static_keys = [_]Key{ |
| 217 | 325 | SimpleType.int32.key(), |
| 218 | 326 | SimpleType.float.key(), |
| 219 | 327 | SimpleType.string.key(), |
| 220 | SimpleType.void.key(), | |
| 221 | 328 | SimpleType.noreturn.key(), |
| 222 | 329 | SimpleType.type.key(), |
| 330 | SimpleType.numeric_literal.key(), | |
| 223 | 331 | |
| 224 | 332 | SimpleValue.void.key(), |
| 225 | 333 | SimpleValue.true.key(), |
| ... | ... | @@ -461,11 +569,11 @@ pub const NamespaceIndex = enum(u32) { |
| 461 | 569 | /// * comptime-known value (where we store the value) |
| 462 | 570 | /// * decl val (so that we can analyze the value lazily) |
| 463 | 571 | /// * decl ref (so that we can analyze the reference lazily) |
| 464 | pub const CaptureValue = packed struct(u32) { | |
| 572 | pub const Capture = packed struct(u32) { | |
| 465 | 573 | tag: enum(u2) { @"comptime", runtime, decl_val, decl_ref }, |
| 466 | 574 | idx: u30, |
| 467 | 575 | |
| 468 | pub fn wrap(val: Unwrapped) CaptureValue { | |
| 576 | pub fn wrap(val: Unwrapped) Capture { | |
| 469 | 577 | return switch (val) { |
| 470 | 578 | .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@intFromEnum(i)) }, |
| 471 | 579 | .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@intFromEnum(i)) }, |
| ... | ... | @@ -473,7 +581,7 @@ pub const CaptureValue = packed struct(u32) { |
| 473 | 581 | .decl_ref => |i| .{ .tag = .decl_ref, .idx = @intCast(@intFromEnum(i)) }, |
| 474 | 582 | }; |
| 475 | 583 | } |
| 476 | pub fn unwrap(val: CaptureValue) Unwrapped { | |
| 584 | pub fn unwrap(val: Capture) Unwrapped { | |
| 477 | 585 | return switch (val.tag) { |
| 478 | 586 | .@"comptime" => .{ .@"comptime" = @enumFromInt(val.idx) }, |
| 479 | 587 | .runtime => .{ .runtime = @enumFromInt(val.idx) }, |
| ... | ... | @@ -484,9 +592,9 @@ pub const CaptureValue = packed struct(u32) { |
| 484 | 592 | |
| 485 | 593 | pub const Unwrapped = union(enum) { |
| 486 | 594 | /// Index refers to the value. |
| 487 | @"comptime": Index, | |
| 595 | @"comptime": Value, | |
| 488 | 596 | /// Index refers to the type. |
| 489 | runtime: Index, | |
| 597 | runtime: Value, | |
| 490 | 598 | decl_val: DeclIndex, |
| 491 | 599 | decl_ref: DeclIndex, |
| 492 | 600 | }; |
| ... | ... | @@ -494,7 +602,7 @@ pub const CaptureValue = packed struct(u32) { |
| 494 | 602 | pub const Slice = struct { |
| 495 | 603 | start: u32, |
| 496 | 604 | len: u32, |
| 497 | pub fn get(slice: Slice, ip: *const InternPool) []CaptureValue { | |
| 605 | pub fn get(slice: Slice, ip: *const InternPool) []Capture { | |
| 498 | 606 | return @ptrCast(ip.extra.items[slice.start..][0..slice.len]); |
| 499 | 607 | } |
| 500 | 608 | }; |
| ... | ... | @@ -516,9 +624,9 @@ const SimpleType = enum { |
| 516 | 624 | float, |
| 517 | 625 | string, |
| 518 | 626 | bool, |
| 519 | void, | |
| 520 | 627 | noreturn, |
| 521 | 628 | type, |
| 629 | numeric_literal, | |
| 522 | 630 | |
| 523 | 631 | pub fn key(self: SimpleType) Key { |
| 524 | 632 | return .{ .simple_type = self }; |
| ... | ... | @@ -526,6 +634,7 @@ const SimpleType = enum { |
| 526 | 634 | }; |
| 527 | 635 | |
| 528 | 636 | pub const Key = union(enum) { |
| 637 | comptime_float: f64, | |
| 529 | 638 | int32: i32, |
| 530 | 639 | float: f64, |
| 531 | 640 | string: []const u8, |
| ... | ... | @@ -549,8 +658,8 @@ pub const Key = union(enum) { |
| 549 | 658 | }; |
| 550 | 659 | |
| 551 | 660 | pub const TypeFunction = struct { |
| 552 | param_types: []Index, | |
| 553 | return_type: Index, | |
| 661 | param_types: []Type, | |
| 662 | return_type: Type, | |
| 554 | 663 | |
| 555 | 664 | pub fn hash(self: TypeFunction, hasher: *Hash) void { |
| 556 | 665 | const t = tracer.trace(@src()); |
| ... | ... | @@ -563,13 +672,13 @@ pub const Key = union(enum) { |
| 563 | 672 | } |
| 564 | 673 | |
| 565 | 674 | 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 | |
| 567 | 676 | a.return_type == b.return_type; |
| 568 | 677 | } |
| 569 | 678 | }; |
| 570 | 679 | |
| 571 | 680 | pub const Func = struct { |
| 572 | ty: Index, | |
| 681 | ty: Value, | |
| 573 | 682 | owner_decl: DeclIndex, |
| 574 | 683 | teg_body_inst: TrackedInst.Index, |
| 575 | 684 | |
| ... | ... | @@ -600,7 +709,7 @@ pub const Key = union(enum) { |
| 600 | 709 | => |x| Hash.hash(seed, asBytes(&x)), |
| 601 | 710 | .string, |
| 602 | 711 | => |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))), | |
| 604 | 713 | .type_function => |x| { |
| 605 | 714 | var hasher = Hash.init(seed); |
| 606 | 715 | x.hash(&hasher); |
| ... | ... | @@ -631,6 +740,7 @@ pub const Key = union(enum) { |
| 631 | 740 | .string, |
| 632 | 741 | => |x| std.mem.eql(u8, x, b.string), |
| 633 | 742 | .float => |x| Float.assertPure(x) == Float.assertPure(b.float), |
| 743 | .comptime_float => |x| Float.assertPure(x) == Float.assertPure(b.comptime_float), | |
| 634 | 744 | .type_function => |x| x.eql(b.type_function), |
| 635 | 745 | .func => |a_info| { |
| 636 | 746 | const b_info = b.func; |
| ... | ... | @@ -747,7 +857,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { |
| 747 | 857 | ip.* = undefined; |
| 748 | 858 | } |
| 749 | 859 | |
| 750 | pub fn indexToKey(ip: *const InternPool, index: Index) Key { | |
| 860 | pub fn indexToKey(ip: *const InternPool, index: Value) Key { | |
| 751 | 861 | assert(index != .none); |
| 752 | 862 | const item: Item = ip.items.get(@intFromEnum(index)); |
| 753 | 863 | const data = item.data; |
| ... | ... | @@ -762,6 +872,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { |
| 762 | 872 | .simple_value => .{ .simple_value = data.decode(.simple_value, item.tag) }, |
| 763 | 873 | .int32 => .{ .int32 = data.decode(.int32, item.tag) }, |
| 764 | 874 | .float => .{ .float = data.decode(.float, item.tag).get(ip).decode() }, |
| 875 | .comptime_float => .{ .comptime_float = data.decode(.comptime_float, item.tag).get(ip).decode() }, | |
| 765 | 876 | .string => .{ .string = data.decode(.string, item.tag).get(ip).toSlice(ip) }, |
| 766 | 877 | .func => { |
| 767 | 878 | const func = data.decode(.func, item.tag).get(ip); |
| ... | ... | @@ -774,7 +885,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { |
| 774 | 885 | }; |
| 775 | 886 | } |
| 776 | 887 | |
| 777 | pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index { | |
| 888 | pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { | |
| 778 | 889 | const t = tracer.traceFmt(@src(), "{s}", .{@tagName(key)}); |
| 779 | 890 | defer t.end(); |
| 780 | 891 | |
| ... | ... | @@ -806,6 +917,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index { |
| 806 | 917 | .float, |
| 807 | 918 | try ip.extra.append(gpa, Float, Float.encode(f)), |
| 808 | 919 | ), |
| 920 | .comptime_float => |f| Item.encode( | |
| 921 | .comptime_float, | |
| 922 | try ip.extra.append(gpa, Float, Float.encode(f)), | |
| 923 | ), | |
| 809 | 924 | .func => |f| brk: { |
| 810 | 925 | const extra = try ip.extra.append(gpa, Tag.FuncDecl, .{ |
| 811 | 926 | .analysis = .{ |
| ... | ... | @@ -829,7 +944,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Index { |
| 829 | 944 | }, |
| 830 | 945 | }; |
| 831 | 946 | ip.items.appendAssumeCapacity(item); |
| 832 | const index: Index = @enumFromInt(ip.items.len - 1); | |
| 947 | const index: Value = @enumFromInt(ip.items.len - 1); | |
| 833 | 948 | if (std.debug.runtime_safety) { |
| 834 | 949 | assert(ip.indexToKey(index).eql(key, ip)); |
| 835 | 950 | } |
| ... | ... | @@ -841,7 +956,7 @@ fn extraTypeFunction( |
| 841 | 956 | extra_index: ExtraIndex(Tag.TypeFunction), |
| 842 | 957 | ) Key.TypeFunction { |
| 843 | 958 | 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); | |
| 845 | 960 | const result: Key.TypeFunction = .{ |
| 846 | 961 | .return_type = type_function.return_type, |
| 847 | 962 | .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 |
| 221 | 221 | // TODO: escapes |
| 222 | 222 | .string => |val| try writer.print("\"{s}\"", .{val}), |
| 223 | 223 | |
| 224 | .comptime_float => unreachable, // was not lowered to float | |
| 225 | ||
| 224 | 226 | else => unreachable, // this value is not a plain_value |
| 225 | 227 | } |
| 226 | 228 | }, |
src/compiler/JsNodeGen.zig+29-25| ... | ... | @@ -198,23 +198,23 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { |
| 198 | 198 | if ((tag == .add or tag == .sub) and |
| 199 | 199 | inner_data.rhs.kind == .value) |
| 200 | 200 | { |
| 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 | // } | |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | 220 | // convert into a compound assignment |
| ... | ... | @@ -442,15 +442,20 @@ fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index { |
| 442 | 442 | const t = tracer.trace(@src()); |
| 443 | 443 | defer t.end(); |
| 444 | 444 | |
| 445 | const intern_tag = c.ip.items.items(.tag)[@intFromEnum(v.toIntern())]; | |
| 445 | const intern_tag = v.valueTag(c.ip); | |
| 446 | 446 | switch (intern_tag) { |
| 447 | 447 | .removed => unreachable, // UAF |
| 448 | 448 | |
| 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 | |
| 450 | 452 | |
| 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 }), | |
| 454 | 459 | |
| 455 | 460 | // funcs always point to decls |
| 456 | 461 | .func => { |
| ... | ... | @@ -493,7 +498,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br |
| 493 | 498 | var requires_label = false; |
| 494 | 499 | var items = switch (inst.decode()) { |
| 495 | 500 | .value => |v| items: { |
| 496 | if (v.ip_index == .void_value) | |
| 501 | if (v == .void) | |
| 497 | 502 | return null; |
| 498 | 503 | if (temporary) |temp| { |
| 499 | 504 | const node = try c.valueToExpression(v); |
| ... | ... | @@ -678,6 +683,7 @@ const assert = std.debug.assert; |
| 678 | 683 | const Allocator = std.mem.Allocator; |
| 679 | 684 | |
| 680 | 685 | const InternPool = @import("InternPool.zig"); |
| 686 | const Value = InternPool.Value; | |
| 681 | 687 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 682 | 688 | |
| 683 | 689 | const Comp = @import("Comp.zig"); |
| ... | ... | @@ -690,7 +696,5 @@ const LocalIndex = JsTree.LocalIndex; |
| 690 | 696 | |
| 691 | 697 | const Air = @import("Air.zig"); |
| 692 | 698 | |
| 693 | const Value = @import("Value.zig"); | |
| 694 | ||
| 695 | 699 | const dod = @import("dod.zig"); |
| 696 | 700 | const tracer = @import("tracer"); |
src/compiler/JsTree.zig+2-2| ... | ... | @@ -244,6 +244,8 @@ const std = @import("std"); |
| 244 | 244 | const assert = std.debug.assert; |
| 245 | 245 | |
| 246 | 246 | const InternPool = @import("InternPool.zig"); |
| 247 | const Value = InternPool.Value; | |
| 248 | ||
| 247 | 249 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 248 | 250 | const String = InternPool.String; |
| 249 | 251 | |
| ... | ... | @@ -252,6 +254,4 @@ const Decl = Comp.Decl; |
| 252 | 254 | |
| 253 | 255 | const dod = @import("dod.zig"); |
| 254 | 256 | |
| 255 | const Value = @import("Value.zig"); | |
| 256 | ||
| 257 | 257 | const Unused = @import("Ast.zig").Unused; |
src/compiler/Parse.zig+4-1| ... | ... | @@ -265,7 +265,10 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex { |
| 265 | 265 | const params = try p.parseParamList(); |
| 266 | 266 | |
| 267 | 267 | 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) | |
| 269 | 272 | else |
| 270 | 273 | null; |
| 271 | 274 |
src/compiler/Report.zig+108-56| ... | ... | @@ -24,62 +24,6 @@ pub const Item = struct { |
| 24 | 24 | pub const Type = enum { @"error", warn }; |
| 25 | 25 | }; |
| 26 | 26 | |
| 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. | |
| 30 | pub 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 | ||
| 70 | pub const DebugStoredTrace = if (builtin.mode == .Debug) | |
| 71 | StoredTrace | |
| 72 | else | |
| 73 | struct { | |
| 74 | pub fn capture() @This() { | |
| 75 | return .{}; | |
| 76 | } | |
| 77 | ||
| 78 | pub fn from(_: ?*std.builtin.StackTrace) @This() { | |
| 79 | return .{}; | |
| 80 | } | |
| 81 | }; | |
| 82 | ||
| 83 | 27 | pub const Err = union(enum) { |
| 84 | 28 | pub const FieldEnum = std.meta.FieldEnum(Err); |
| 85 | 29 | |
| ... | ... | @@ -189,6 +133,15 @@ pub const Err = union(enum) { |
| 189 | 133 | ); |
| 190 | 134 | } |
| 191 | 135 | }, |
| 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 | }, | |
| 192 | 145 | }; |
| 193 | 146 | |
| 194 | 147 | pub const Warn = union(enum) { |
| ... | ... | @@ -221,8 +174,74 @@ pub const Warn = union(enum) { |
| 221 | 174 | try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id}); |
| 222 | 175 | } |
| 223 | 176 | }, |
| 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 | }, | |
| 224 | 187 | }; |
| 225 | 188 | |
| 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. | |
| 192 | pub 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 | ||
| 232 | pub const DebugStoredTrace = if (builtin.mode == .Debug) | |
| 233 | StoredTrace | |
| 234 | else | |
| 235 | struct { | |
| 236 | pub fn capture() @This() { | |
| 237 | return .{}; | |
| 238 | } | |
| 239 | ||
| 240 | pub fn from(_: ?*std.builtin.StackTrace) @This() { | |
| 241 | return .{}; | |
| 242 | } | |
| 243 | }; | |
| 244 | ||
| 226 | 245 | pub const UnusedKind = enum { |
| 227 | 246 | param, |
| 228 | 247 | constant, |
| ... | ... | @@ -509,6 +528,7 @@ pub const Builder = struct { |
| 509 | 528 | pub fn deinit(b: *Builder) void { |
| 510 | 529 | b.errors.deinit(b.gpa); |
| 511 | 530 | b.warnings.deinit(b.gpa); |
| 531 | b.string_bytes.deinit(b.gpa); | |
| 512 | 532 | } |
| 513 | 533 | |
| 514 | 534 | pub fn finalize(b: *Builder) !Report { |
| ... | ... | @@ -592,6 +612,38 @@ pub const MsgContext = struct { |
| 592 | 612 | } |
| 593 | 613 | }; |
| 594 | 614 | |
| 615 | pub 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 | ||
| 595 | 647 | const Report = @This(); |
| 596 | 648 | const std = @import("std"); |
| 597 | 649 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
src/compiler/Sema.zig+224-121| ... | ... | @@ -16,9 +16,10 @@ inst_map: InstMap = undefined, |
| 16 | 16 | /// scratch memory. can be used in any place you might temporarily allocate an arraylist |
| 17 | 17 | /// this is most notably used to store block contents before the block is finalized |
| 18 | 18 | scratch: std.ArrayListUnmanaged(u32) = .{}, |
| 19 | params: []InternPool.Index = undefined, | |
| 19 | params: []Type = undefined, | |
| 20 | 20 | |
| 21 | 21 | // outputs |
| 22 | report: Report.Builder = undefined, | |
| 22 | 23 | air_instructions: std.MultiArrayList(Air.Inst) = .{}, |
| 23 | 24 | air_extra: dod.ExtraStore(.air, .@"var") = .{}, |
| 24 | 25 | |
| ... | ... | @@ -27,6 +28,8 @@ pub const Error = error{ |
| 27 | 28 | AnalysisFail, |
| 28 | 29 | /// This analysis is waiting on other dependencies to finish |
| 29 | 30 | Interrupt, |
| 31 | ||
| 32 | OutOfMemory, | |
| 30 | 33 | }; |
| 31 | 34 | |
| 32 | 35 | /// 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 { |
| 74 | 77 | const t = tracer.trace(@src()); |
| 75 | 78 | defer t.end(); |
| 76 | 79 | |
| 80 | s.report = .{ | |
| 81 | .gpa = s.gpa, | |
| 82 | .current_file = s.ip.namespacePtr(s.namespace_index).file_scope, | |
| 83 | }; | |
| 84 | ||
| 77 | 85 | try s.comp.current_compile_decls.append(s.gpa, s.decl_index); |
| 78 | 86 | |
| 79 | 87 | s.inst_map = try InstMap.init( |
| ... | ... | @@ -87,6 +95,10 @@ pub fn deinit(s: *Sema) void { |
| 87 | 95 | const t = tracer.trace(@src()); |
| 88 | 96 | defer t.end(); |
| 89 | 97 | |
| 98 | const report = s.report.finalize() catch @panic("OOM"); | |
| 99 | report.dumpToStdErr(s.comp) catch @panic("Error"); | |
| 100 | report.deinit(s.gpa); | |
| 101 | ||
| 90 | 102 | defer s.* = undefined; |
| 91 | 103 | s.inst_map.deinit(s.arena); |
| 92 | 104 | s.scratch.deinit(s.arena); |
| ... | ... | @@ -121,14 +133,14 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value { |
| 121 | 133 | const decl, const fn_decl = s.code.fullFuncDecl(s.inst_map.start); |
| 122 | 134 | assert(decl.kind == .function); |
| 123 | 135 | |
| 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); | |
| 125 | 137 | defer s.gpa.free(s.params); |
| 126 | 138 | |
| 127 | 139 | 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).?; | |
| 129 | 141 | } |
| 130 | 142 | |
| 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).?; | |
| 132 | 144 | |
| 133 | 145 | const root_block = fn_decl.body.toIndex() orelse |
| 134 | 146 | @panic("TODO: please change this to Index. Ref is not allowed here anymore"); |
| ... | ... | @@ -143,17 +155,16 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value { |
| 143 | 155 | const func_prototype = try s.ip.get(s.gpa, .{ |
| 144 | 156 | .type_function = .{ |
| 145 | 157 | .param_types = s.params, |
| 146 | .return_type = return_type.toIntern(), | |
| 158 | .return_type = return_type, | |
| 147 | 159 | }, |
| 148 | 160 | }); |
| 149 | const func_value = try s.ip.get(s.gpa, .{ | |
| 161 | const value = try s.ip.get(s.gpa, .{ | |
| 150 | 162 | .func = .{ |
| 151 | 163 | .ty = func_prototype, |
| 152 | 164 | .owner_decl = s.decl_index, |
| 153 | 165 | .teg_body_inst = s.decl.teg_decl_index.unwrap().?, |
| 154 | 166 | }, |
| 155 | 167 | }); |
| 156 | const value = Value.fromInterned(func_value); | |
| 157 | 168 | s.decl.val = value; |
| 158 | 169 | return value; |
| 159 | 170 | } |
| ... | ... | @@ -221,11 +232,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 221 | 232 | }, |
| 222 | 233 | .number => { |
| 223 | 234 | const number: dod.Float = s.code.instPayload(.number, in); |
| 224 | return s.makeValueAir(.{ .float = number.decode() }); | |
| 235 | return s.makeValueAir(.{ .comptime_float = number.decode() }); | |
| 225 | 236 | }, |
| 226 | 237 | .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); | |
| 229 | 241 | return s.addInstRef(.js_global_ref, operand.asString(s.ip)); |
| 230 | 242 | }, |
| 231 | 243 | .field => { |
| ... | ... | @@ -233,12 +245,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 233 | 245 | const base = try s.resolveRefRuntime(field.lhs); |
| 234 | 246 | const rhs = try s.resolveRefComptime(field.rhs); |
| 235 | 247 | |
| 236 | try s.validateType(rhs.type(s.ip), .string_type); | |
| 248 | // try s.validateType(rhs.type(s.ip), .string_type); | |
| 237 | 249 | const string = rhs.asString(s.ip); |
| 238 | 250 | |
| 239 | 251 | const base_type = s.typeOfRef(base); |
| 240 | 252 | switch (base_type) { |
| 241 | .opaque_type => { | |
| 253 | .@"opaque" => { | |
| 242 | 254 | return s.addInstRef(.field, .{ |
| 243 | 255 | .base = base, |
| 244 | 256 | .field = try string.tryToNullTerminatedString(s.ip, s.gpa), |
| ... | ... | @@ -259,12 +271,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 259 | 271 | return error.AnalysisFail; |
| 260 | 272 | }; |
| 261 | 273 | const resolved = try s.resolveDecl(decl_index); |
| 262 | return resolved.toIntern().toAir(); | |
| 274 | return resolved.toAir(); | |
| 263 | 275 | }, |
| 264 | 276 | .param => { |
| 265 | 277 | const param: Teg.Inst.DataNode = s.code.instData(.param, in); |
| 266 | 278 | return s.addInstRef(.arg, .{ |
| 267 | .type = Value.fromInterned(s.params[param.data]), | |
| 279 | .type = s.params[param.data], | |
| 268 | 280 | .arg_number = param.data, |
| 269 | 281 | }); |
| 270 | 282 | }, |
| ... | ... | @@ -325,83 +337,86 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 325 | 337 | }); |
| 326 | 338 | }, |
| 327 | 339 | .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 | // }); | |
| 380 | 394 | }, |
| 381 | 395 | .ensure_void => { |
| 382 | 396 | 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(); | |
| 390 | 405 | }, |
| 391 | 406 | .discard => { |
| 392 | 407 | 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) { | |
| 395 | 410 | std.debug.print("oh no, we have a useless _ = void;\n", .{}); |
| 396 | 411 | } |
| 397 | return InternPool.Index.void_value.toAir(); | |
| 412 | return InternPool.Value.void.toAir(); | |
| 398 | 413 | }, |
| 399 | 414 | .@"if" => { |
| 400 | 415 | const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code); |
| 401 | 416 | |
| 402 | 417 | const cond = try s.resolveRefRuntime(data.cond); |
| 403 | 418 | |
| 404 | try s.validateType(Value.fromInterned(s.typeOfRef(cond)), .bool_type); | |
| 419 | // try s.validateType(s.typeOfRef(cond), .bool_type); | |
| 405 | 420 | |
| 406 | 421 | // If known at comptime, only analyze the taken path |
| 407 | 422 | if (cond.kind == .value) { |
| ... | ... | @@ -417,7 +432,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 417 | 432 | .data = try s.air_extra.append(s.gpa, Air.If.Data, .{ |
| 418 | 433 | .yes = yes_block, |
| 419 | 434 | .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)), | |
| 421 | 436 | }), |
| 422 | 437 | }); |
| 423 | 438 | }, |
| ... | ... | @@ -434,11 +449,24 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 434 | 449 | .memory => { |
| 435 | 450 | const data = s.code.instData(.memory, in); |
| 436 | 451 | |
| 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 | } | |
| 438 | 466 | |
| 439 | 467 | return try s.addInstRef(.memory, .{ |
| 440 | 468 | .init = initial_value, |
| 441 | .ty = s.typeOfRef(initial_value).toValue(), | |
| 469 | .ty = s.typeOfRef(initial_value), | |
| 442 | 470 | }); |
| 443 | 471 | }, |
| 444 | 472 | .store => { |
| ... | ... | @@ -448,16 +476,22 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 448 | 476 | const location = s.mapTeg(data.memory.toRef()).decode().inst; |
| 449 | 477 | const operand = try s.resolveRefRuntime(data.operand); |
| 450 | 478 | |
| 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 | // ); | |
| 455 | 483 | |
| 456 | 484 | return try s.addInstRef(.store, .{ |
| 457 | 485 | .memory = location, |
| 458 | 486 | .operand = operand, |
| 459 | 487 | }); |
| 460 | 488 | }, |
| 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 | }, | |
| 461 | 495 | else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}), |
| 462 | 496 | } |
| 463 | 497 | } |
| ... | ... | @@ -498,7 +532,7 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref { |
| 498 | 532 | |
| 499 | 533 | fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.Inst.Ref { |
| 500 | 534 | const index = ref.toIndex() orelse { |
| 501 | return ref.toInterned().?.toAir(); | |
| 535 | return ref.toValue().?.toAir(); | |
| 502 | 536 | }; |
| 503 | 537 | |
| 504 | 538 | if (s.inst_map.get(index)) |air| { |
| ... | ... | @@ -519,20 +553,21 @@ fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) anyerror!Air.Inst.Ref { |
| 519 | 553 | } |
| 520 | 554 | |
| 521 | 555 | fn 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, | |
| 524 | 559 | }; |
| 525 | 560 | |
| 526 | if (s.inst_map.get(in)) |air| { | |
| 561 | if (s.inst_map.get(index)) |air| { | |
| 527 | 562 | switch (air.decode()) { |
| 528 | 563 | .value => |value| return value, |
| 529 | 564 | else => {}, |
| 530 | 565 | } |
| 531 | 566 | } |
| 532 | 567 | |
| 533 | switch (s.code.instTag(in)) { | |
| 568 | switch (s.code.instTag(index)) { | |
| 534 | 569 | .string => { |
| 535 | const string: Teg.SlicedString = s.code.instPayload(.string, in); | |
| 570 | const string: Teg.SlicedString = s.code.instPayload(.string, index); | |
| 536 | 571 | return s.makeValue(.{ .string = string.slice(s.code.string_bytes) }); |
| 537 | 572 | }, |
| 538 | 573 | |
| ... | ... | @@ -540,6 +575,70 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value { |
| 540 | 575 | } |
| 541 | 576 | } |
| 542 | 577 | |
| 578 | /// Coerce `value` runtime ref to `coerce_type`, and emit an error at `teg_ref`'s location | |
| 579 | fn 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 | ||
| 629 | fn 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 | ||
| 543 | 642 | fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { |
| 544 | 643 | const decl = s.ip.declPtr(decl_index); |
| 545 | 644 | switch (decl.analysis) { |
| ... | ... | @@ -579,27 +678,22 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { |
| 579 | 678 | } |
| 580 | 679 | } |
| 581 | 680 | |
| 582 | fn 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) { | |
| 681 | fn unionType(s: *Sema, a: Type, b: Type) Type { | |
| 682 | if (a == b) { | |
| 589 | 683 | return a; |
| 590 | 684 | } |
| 591 | 685 | |
| 592 | if (a.ip_index == .noreturn_type) | |
| 686 | if (a == .noreturn) | |
| 593 | 687 | return b; |
| 594 | 688 | |
| 595 | if (b.ip_index == .noreturn_type) | |
| 689 | if (b == .noreturn) | |
| 596 | 690 | return a; |
| 597 | 691 | |
| 598 | 692 | std.debug.panic( |
| 599 | 693 | "TODO: when two non-equal types implicitly merge (if/match), this should generate a union type.\na: {}\nb: {}", |
| 600 | 694 | .{ |
| 601 | a.fmt(s.ip), | |
| 602 | b.fmt(s.ip), | |
| 695 | a.dump(s.ip), | |
| 696 | b.dump(s.ip), | |
| 603 | 697 | }, |
| 604 | 698 | ); |
| 605 | 699 | // 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 { |
| 650 | 744 | else => comptime unreachable, |
| 651 | 745 | }; |
| 652 | 746 | return @as( |
| 653 | InternPool.Index, | |
| 747 | InternPool.Value, | |
| 654 | 748 | if (result) .bool_true else .bool_false, |
| 655 | 749 | ).toAir(); |
| 656 | 750 | }, |
| ... | ... | @@ -660,7 +754,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref { |
| 660 | 754 | const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq"); |
| 661 | 755 | const result = l == r; |
| 662 | 756 | return @as( |
| 663 | InternPool.Index, | |
| 757 | InternPool.Value, | |
| 664 | 758 | if (result) .bool_true else .bool_false, |
| 665 | 759 | ).toAir(); |
| 666 | 760 | }, |
| ... | ... | @@ -709,15 +803,15 @@ fn setInstData( |
| 709 | 803 | } |
| 710 | 804 | |
| 711 | 805 | fn 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); | |
| 713 | 807 | } |
| 714 | 808 | |
| 715 | 809 | fn 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(); | |
| 717 | 811 | } |
| 718 | 812 | |
| 719 | 813 | fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { |
| 720 | if (inst.toInterned()) |interned| { | |
| 814 | if (inst.toValue()) |interned| { | |
| 721 | 815 | return interned.toAir(); |
| 722 | 816 | } |
| 723 | 817 | const index = inst.toIndex().?; |
| ... | ... | @@ -729,13 +823,6 @@ fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { |
| 729 | 823 | }; |
| 730 | 824 | } |
| 731 | 825 | |
| 732 | fn 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 | ||
| 739 | 826 | fn scratchToIndexList(s: *Sema, scratch_top: usize) !Air.IndexList { |
| 740 | 827 | const items = s.scratch.items[scratch_top..]; |
| 741 | 828 | const extra_top = s.air_extra.list.items.len; |
| ... | ... | @@ -753,7 +840,7 @@ fn tempAir(s: *const Sema) Air { |
| 753 | 840 | }; |
| 754 | 841 | } |
| 755 | 842 | |
| 756 | fn typeOfRef(s: *Sema, ref: Air.Inst.Ref) InternPool.Index { | |
| 843 | fn typeOfRef(s: *Sema, ref: Air.Inst.Ref) InternPool.Type { | |
| 757 | 844 | return s.tempAir().typeOf(ref, s.ip); |
| 758 | 845 | } |
| 759 | 846 | |
| ... | ... | @@ -769,10 +856,28 @@ fn lookupDecl(s: *Sema, name: NullTerminatedString) ?InternPool.DeclIndex { |
| 769 | 856 | } |
| 770 | 857 | } |
| 771 | 858 | |
| 859 | fn nodeLoc(s: *const Sema, in: Teg.Inst.Index) Report.LazyNode { | |
| 860 | return s.code.instSrcLoc(in, s.decl_index); | |
| 861 | } | |
| 862 | ||
| 863 | pub 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 | ||
| 867 | pub 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 | ||
| 871 | pub 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 | ||
| 772 | 876 | const Sema = @This(); |
| 773 | 877 | |
| 774 | 878 | const std = @import("std"); |
| 775 | 879 | const mem = std.mem; |
| 880 | const math = std.math; | |
| 776 | 881 | const Allocator = std.mem.Allocator; |
| 777 | 882 | const assert = std.debug.assert; |
| 778 | 883 | const log = std.log.scoped(.sema); |
| ... | ... | @@ -791,14 +896,12 @@ const Decl = Comp.Decl; |
| 791 | 896 | const Namespace = Comp.Namespace; |
| 792 | 897 | |
| 793 | 898 | const Air = @import("Air.zig"); |
| 794 | ||
| 795 | 899 | const Teg = @import("Teg.zig"); |
| 900 | const Report = @import("Report.zig"); | |
| 901 | const dod = @import("dod.zig"); | |
| 902 | const tracer = @import("tracer"); | |
| 796 | 903 | |
| 797 | const Value = @import("Value.zig"); | |
| 798 | 904 | const InternPool = @import("InternPool.zig"); |
| 905 | const Value = InternPool.Value; | |
| 906 | const Type = InternPool.Type; | |
| 799 | 907 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 800 | const dod = @import("dod.zig"); | |
| 801 | ||
| 802 | const Report = @import("Report.zig"); | |
| 803 | ||
| 804 | const tracer = @import("tracer"); |
src/compiler/Teg.Dump.zig+8| ... | ... | @@ -22,6 +22,14 @@ const State = struct { |
| 22 | 22 | |
| 23 | 23 | const tag = s.teg.instTag(inst); |
| 24 | 24 | |
| 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 | ||
| 25 | 33 | try tty.setColor(writer, .cyan); |
| 26 | 34 | try writer.print("{s}", .{@tagName(tag)}); |
| 27 | 35 | try tty.setColor(writer, .reset); |
src/compiler/Teg.zig+42-14| ... | ... | @@ -209,11 +209,11 @@ pub const Inst = struct { |
| 209 | 209 | int32_type, |
| 210 | 210 | float_type, |
| 211 | 211 | string_type, |
| 212 | void_type, | |
| 213 | 212 | noreturn_type, |
| 214 | 213 | type_type, |
| 214 | numeric_literal_type, | |
| 215 | 215 | |
| 216 | void_value, | |
| 216 | void, | |
| 217 | 217 | bool_true, |
| 218 | 218 | bool_false, |
| 219 | 219 | int32_zero, |
| ... | ... | @@ -227,9 +227,22 @@ pub const Inst = struct { |
| 227 | 227 | |
| 228 | 228 | _, |
| 229 | 229 | |
| 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); | |
| 233 | 246 | if (ref_int >= Index.static_len) { |
| 234 | 247 | return @enumFromInt(ref_int - Index.static_len); |
| 235 | 248 | } else { |
| ... | ... | @@ -242,7 +255,7 @@ pub const Inst = struct { |
| 242 | 255 | return toIndex(inst); |
| 243 | 256 | } |
| 244 | 257 | |
| 245 | pub fn toInterned(inst: Ref) ?InternPool.Index { | |
| 258 | pub fn toValue(inst: Ref) ?InternPool.Value { | |
| 246 | 259 | const ref_int = @intFromEnum(inst); |
| 247 | 260 | |
| 248 | 261 | // todo: is this > or >= |
| ... | ... | @@ -383,18 +396,30 @@ pub fn declData(teg: *const Teg, index: Inst.Index) Inst.Decl { |
| 383 | 396 | return teg.instData(.declaration, index).extra_index.get(teg); |
| 384 | 397 | } |
| 385 | 398 | |
| 399 | pub 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 | ||
| 386 | 411 | pub fn moduleDeclarations(teg: *const Teg, module_index: Inst.Index) []const Inst.Index { |
| 387 | 412 | const module = teg.instData(.module, module_index); |
| 388 | 413 | const module_data: IndexList = module.extra_index.get(teg); |
| 389 | 414 | 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 | } | |
| 398 | 423 | return slice; |
| 399 | 424 | } |
| 400 | 425 | |
| ... | ... | @@ -441,4 +466,7 @@ const InternPool = @import("InternPool.zig"); |
| 441 | 466 | |
| 442 | 467 | const Air = @import("Air.zig"); |
| 443 | 468 | |
| 469 | const Comp = @import("Comp.zig"); | |
| 470 | ||
| 444 | 471 | const Report = @import("Report.zig"); |
| 472 | const LazyNode = Report.LazyNode; |
src/compiler/Tokenizer.zig+1-1| ... | ... | @@ -192,7 +192,7 @@ pub const Token = struct { |
| 192 | 192 | .pipe_pipe => "||", |
| 193 | 193 | .percent => "%", |
| 194 | 194 | .plus => "+", |
| 195 | .minus_lt => "<", | |
| 195 | .minus_lt => "->", | |
| 196 | 196 | .semi => ";", |
| 197 | 197 | .slash => "/", |
| 198 | 198 | .star => "*", |
src/compiler/Transform.zig+18-18| ... | ... | @@ -202,7 +202,7 @@ const BlockContext = struct { |
| 202 | 202 | const Liveness = enum { alive, dead }; |
| 203 | 203 | |
| 204 | 204 | const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{ |
| 205 | .{ "void", .void_value }, | |
| 205 | .{ "void", .void }, | |
| 206 | 206 | .{ "true", .bool_true }, |
| 207 | 207 | .{ "false", .bool_false }, |
| 208 | 208 | .{ "int", .int32_type }, |
| ... | ... | @@ -289,7 +289,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 289 | 289 | |
| 290 | 290 | trail.writeStruct(Inst.DeclFn, .{ |
| 291 | 291 | .params = .{ .start = 0, .len = 0 }, |
| 292 | .return_type = Inst.Ref.void_type, | |
| 292 | .return_type = Inst.Ref.void, | |
| 293 | 293 | .body = block.toRef(), |
| 294 | 294 | }); |
| 295 | 295 | |
| ... | ... | @@ -378,7 +378,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 378 | 378 | const return_type = if (fn_decl.return_type) |return_type_node| |
| 379 | 379 | try t.transformExpressionToBlock(decl, return_type_node, false) |
| 380 | 380 | else |
| 381 | Inst.Ref.void_type; | |
| 381 | Inst.Ref.void; | |
| 382 | 382 | |
| 383 | 383 | const body = try t.transformExpressionToBlock(decl, fn_decl.body, true); |
| 384 | 384 | |
| ... | ... | @@ -528,7 +528,7 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc |
| 528 | 528 | .node = param, |
| 529 | 529 | .prev = sym.value_ptr.node, |
| 530 | 530 | }); |
| 531 | return .void_value; | |
| 531 | return .void; | |
| 532 | 532 | } |
| 533 | 533 | |
| 534 | 534 | sym.value_ptr.* = .{ |
| ... | ... | @@ -558,7 +558,7 @@ const Builtin = enum { |
| 558 | 558 | |
| 559 | 559 | if (args.len != 1) { |
| 560 | 560 | // try t.report.err(.invalid_builtin_args, .{ .id = "js" }); |
| 561 | return .void_value; | |
| 561 | return .void; | |
| 562 | 562 | } |
| 563 | 563 | |
| 564 | 564 | const source = try t.transformExpression(decl, args[0]) orelse return null; |
| ... | ... | @@ -600,7 +600,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 600 | 600 | |
| 601 | 601 | const lookup = t.lookupId(id) orelse { |
| 602 | 602 | try t.report.err(.unknown_identifier, .{ .node = node }); |
| 603 | return .void_value; | |
| 603 | return .void; | |
| 604 | 604 | }; |
| 605 | 605 | |
| 606 | 606 | lookup.used = true; |
| ... | ... | @@ -815,7 +815,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 815 | 815 | => |tag| { |
| 816 | 816 | const statements = t.ast.nodeStatements(node); |
| 817 | 817 | |
| 818 | var last: Inst.Ref = .void_value; | |
| 818 | var last: Inst.Ref = .void; | |
| 819 | 819 | for (statements, 1..) |statement, i| { |
| 820 | 820 | const is_last = i == statements.len; |
| 821 | 821 | last = try t.transformBlockStatement( |
| ... | ... | @@ -827,14 +827,14 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 827 | 827 | }; |
| 828 | 828 | } |
| 829 | 829 | |
| 830 | return if (tag == .block_implicit_return) last else .void_value; | |
| 830 | return if (tag == .block_implicit_return) last else .void; | |
| 831 | 831 | }, |
| 832 | 832 | |
| 833 | 833 | .builtin_call, |
| 834 | 834 | => { |
| 835 | 835 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { |
| 836 | 836 | try t.report.err(.invalid_builtin, .{ .node = node }); |
| 837 | return .void_value; | |
| 837 | return .void; | |
| 838 | 838 | }; |
| 839 | 839 | |
| 840 | 840 | const base = t.ast.nodeData(node).list; |
| ... | ... | @@ -939,9 +939,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 939 | 939 | false, |
| 940 | 940 | ) |
| 941 | 941 | else |
| 942 | Inst.Ref.void_value; | |
| 942 | Inst.Ref.void; | |
| 943 | 943 | |
| 944 | if (yes == .void_value and no == .void_value) { | |
| 944 | if (yes == .void and no == .void) { | |
| 945 | 945 | // when both branches do nothing, we can omit the branch, |
| 946 | 946 | // however we still need to type-check the condition operand |
| 947 | 947 | _ = try t.addInstRef(.{ |
| ... | ... | @@ -951,7 +951,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 951 | 951 | .operand = cond, |
| 952 | 952 | }), |
| 953 | 953 | }); |
| 954 | return .void_value; | |
| 954 | return .void; | |
| 955 | 955 | } |
| 956 | 956 | |
| 957 | 957 | return try t.addInstRef(.{ |
| ... | ... | @@ -990,7 +990,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 990 | 990 | try t.transformExpression(decl.withReturn(null), expr) orelse |
| 991 | 991 | return null |
| 992 | 992 | else |
| 993 | .void_value; | |
| 993 | .void; | |
| 994 | 994 | |
| 995 | 995 | _ = try t.addInstRef(.{ |
| 996 | 996 | .tag = .@"break", |
| ... | ... | @@ -1022,7 +1022,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1022 | 1022 | try t.transformExpression(decl.withReturn(null), expr) orelse |
| 1023 | 1023 | return null |
| 1024 | 1024 | else |
| 1025 | .void_value; | |
| 1025 | .void; | |
| 1026 | 1026 | |
| 1027 | 1027 | _ = try t.addInstRef(.{ |
| 1028 | 1028 | .tag = .@"break", |
| ... | ... | @@ -1063,12 +1063,12 @@ fn transformBlockStatement( |
| 1063 | 1063 | if (mem.eql(u8, var_decl.id, "_")) { |
| 1064 | 1064 | // TODO: relax this restriction into just creating a discard node |
| 1065 | 1065 | try t.report.err(.invalid_discard, .{ .node = node }); |
| 1066 | return .void_value; | |
| 1066 | return .void; | |
| 1067 | 1067 | } |
| 1068 | 1068 | |
| 1069 | 1069 | if (BuiltInIds.has(var_decl.id)) { |
| 1070 | 1070 | try t.report.err(.shadow_builtin_variable, .{ .node = node }); |
| 1071 | return .void_value; | |
| 1071 | return .void; | |
| 1072 | 1072 | } |
| 1073 | 1073 | |
| 1074 | 1074 | const type_ref = if (var_decl.type) |data_type| |
| ... | ... | @@ -1132,7 +1132,7 @@ fn transformBlockStatement( |
| 1132 | 1132 | }, |
| 1133 | 1133 | }; |
| 1134 | 1134 | |
| 1135 | return .void_value; | |
| 1135 | return .void; | |
| 1136 | 1136 | }, |
| 1137 | 1137 | |
| 1138 | 1138 | .fn_decl, |
| ... | ... | @@ -1188,7 +1188,7 @@ fn transformBlockStatement( |
| 1188 | 1188 | const result = try t.transformExpression(decl, node) orelse |
| 1189 | 1189 | return null; |
| 1190 | 1190 | |
| 1191 | if (!is_implicit_return_expression and result != .void_value) { | |
| 1191 | if (!is_implicit_return_expression and result != .void) { | |
| 1192 | 1192 | return try t.addInstRef(.{ |
| 1193 | 1193 | .tag = .ensure_void, |
| 1194 | 1194 | .data = EncodedData.encode(.ensure_void, .{ |
src/compiler/Value.zig deleted-96| ... | ... | @@ -1,96 +0,0 @@ |
| 1 | ip_index: InternPool.Index, | |
| 2 | ||
| 3 | pub fn fromInterned(i: InternPool.Index) Value { | |
| 4 | assert(i != .none); | |
| 5 | return .{ .ip_index = i }; | |
| 6 | } | |
| 7 | ||
| 8 | pub fn toIntern(val: Value) InternPool.Index { | |
| 9 | assert(val.ip_index != .none); | |
| 10 | return val.ip_index; | |
| 11 | } | |
| 12 | ||
| 13 | pub fn toKey(val: Value, ip: *const InternPool) InternPool.Key { | |
| 14 | return val.toIntern().toKey(ip); | |
| 15 | } | |
| 16 | ||
| 17 | pub fn asBool(val: Value) i32 { | |
| 18 | return switch (val.toIntern()) { | |
| 19 | .bool_true => true, | |
| 20 | .bool_false => false, | |
| 21 | else => unreachable, | |
| 22 | }; | |
| 23 | } | |
| 24 | ||
| 25 | pub 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 | ||
| 34 | pub 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 | ||
| 50 | pub 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 | |
| 59 | pub 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 | ||
| 72 | const Value = @This(); | |
| 73 | ||
| 74 | const std = @import("std"); | |
| 75 | const assert = std.debug.assert; | |
| 76 | ||
| 77 | pub const InternPool = @import("InternPool.zig"); | |
| 78 | ||
| 79 | pub fn fmt(v: Value, ip: *InternPool) Format { | |
| 80 | return .{ .value = v, .ip = ip }; | |
| 81 | } | |
| 82 | ||
| 83 | pub 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 |
| 240 | 240 | return ip.get(T, trail.index); |
| 241 | 241 | } |
| 242 | 242 | |
| 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 { | |
| 244 | 252 | defer trail.index += len; |
| 245 | 253 | return .{ |
| 246 | 254 | .start = @intCast(trail.index), |
src/compiler/util.zig+22-1| ... | ... | @@ -1,4 +1,3 @@ |
| 1 | /// TODO: move to io utility file | |
| 2 | 1 | pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config { |
| 3 | 2 | if (@hasField(@TypeOf(writer), "unbuffered_writer")) { |
| 4 | 3 | return detectTTYFromWriter(writer.unbuffered_writer); |
| ... | ... | @@ -17,4 +16,26 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config { |
| 17 | 16 | return .no_color; |
| 18 | 17 | } |
| 19 | 18 | |
| 19 | pub 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 | ||
| 20 | 40 | const std = @import("std"); |
| 41 | const assert = std.debug.assert; |