diff options
| -rw-r--r-- | src/Compilation.zig | 34 | ||||
| -rw-r--r-- | src/Module.zig | 135 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 134 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 16 | ||||
| -rw-r--r-- | src/type.zig | 22 | ||||
| -rw-r--r-- | src/value.zig | 73 |
6 files changed, 332 insertions, 82 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index a34d0cd198..00cdba85a4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1709,7 +1709,9 @@ pub fn update(self: *Compilation) !void { // in the start code, but when using the stage1 backend that won't happen, // so in order to run AstGen on the root source file we put it into the // import_table here. - if (use_stage1) { + // Likewise, in the case of `zig test`, the test runner is the root source file, + // and so there is nothing to import the main file. + if (use_stage1 or self.bin_file.options.is_test) { _ = try module.importPkg(module.main_pkg); } @@ -1725,6 +1727,9 @@ pub fn update(self: *Compilation) !void { if (!use_stage1) { try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); + if (self.bin_file.options.is_test) { + try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); + } } } @@ -2053,24 +2058,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor assert(decl.has_tv); assert(decl.ty.hasCodeGenBits()); - self.bin_file.updateDecl(module, decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .codegen_failure; - continue; - }, - else => { - try module.failed_decls.ensureUnusedCapacity(gpa, 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( - gpa, - decl.srcLoc(), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - continue; - }, - }; + try module.linkerUpdateDecl(decl); }, }, .codegen_func => |func| switch (func.owner_decl.analysis) { @@ -2396,6 +2384,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, }; + + if (self.bin_file.options.is_test and self.totalErrorCount() == 0) { + // The `test_functions` decl has been intentionally postponed until now, + // at which point we must populate it with the list of test functions that + // have been discovered and not filtered out. + const mod = self.bin_file.options.module.?; + try mod.populateTestFunctions(); + } } const AstGenSrc = union(enum) { diff --git a/src/Module.zig b/src/Module.zig index 99f314c5cb..4ddce33655 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -112,6 +112,8 @@ compile_log_text: ArrayListUnmanaged(u8) = .{}, emit_h: ?*GlobalEmitH, +test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. pub const GlobalEmitH = struct { /// Where to put the output. @@ -282,6 +284,7 @@ pub const Decl = struct { pub fn destroy(decl: *Decl, module: *Module) void { const gpa = module.gpa; log.debug("destroy {*} ({s})", .{ decl, decl.name }); + _ = module.test_functions.swapRemove(decl); if (decl.deletion_flag) { assert(module.deletion_set.swapRemove(decl)); } @@ -3319,6 +3322,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // the test name filter. if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) break :blk false; + try mod.test_functions.put(gpa, new_decl, {}); break :blk true; }, else => blk: { @@ -3326,6 +3330,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) break :blk false; // TODO check the name against --test-filter + try mod.test_functions.put(gpa, new_decl, {}); break :blk true; }, }; @@ -3766,16 +3771,37 @@ pub fn createAnonymousDeclNamed( typed_value: TypedValue, name: [:0]u8, ) !*Decl { + return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name); +} + +pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { + return mod.createAnonymousDeclFromDecl(scope.ownerDecl().?, typed_value); +} + +pub fn createAnonymousDeclFromDecl(mod: *Module, owner_decl: *Decl, tv: TypedValue) !*Decl { + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ + owner_decl.name, name_index, + }); + return mod.createAnonymousDeclFromDeclNamed(owner_decl, tv, name); +} + +/// Takes ownership of `name` even if it returns an error. +pub fn createAnonymousDeclFromDeclNamed( + mod: *Module, + owner_decl: *Decl, + typed_value: TypedValue, + name: [:0]u8, +) !*Decl { errdefer mod.gpa.free(name); - const scope_decl = scope.ownerDecl().?; - const namespace = scope_decl.namespace; + const namespace = owner_decl.namespace; try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); - const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); + const new_decl = try mod.allocateNewDecl(namespace, owner_decl.src_node); new_decl.name = name; - new_decl.src_line = scope_decl.src_line; + new_decl.src_line = owner_decl.src_line; new_decl.ty = typed_value.ty; new_decl.val = typed_value.val; new_decl.has_tv = true; @@ -3796,15 +3822,6 @@ pub fn createAnonymousDeclNamed( return new_decl; } -pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { - const scope_decl = scope.ownerDecl().?; - const name_index = mod.getNextAnonNameIndex(); - const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ - scope_decl.name, name_index, - }); - return mod.createAnonymousDeclNamed(scope, typed_value, name); -} - pub fn getNextAnonNameIndex(mod: *Module) usize { return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); } @@ -4801,3 +4818,95 @@ pub fn processExports(mod: *Module) !void { }; } } + +pub fn populateTestFunctions(mod: *Module) !void { + const gpa = mod.gpa; + const builtin_pkg = mod.main_pkg.table.get("builtin").?; + const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file; + const builtin_namespace = builtin_file.root_decl.?.namespace; + const decl = builtin_namespace.decls.get("test_functions").?; + var buf: Type.Payload.ElemType = undefined; + const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType(); + + const array_decl = d: { + // Add mod.test_functions to an array decl then make the test_functions + // decl reference it as a slice. + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); + errdefer new_decl_arena.deinit(); + const arena = &new_decl_arena.allocator; + + const test_fn_vals = try arena.alloc(Value, mod.test_functions.count()); + const array_decl = try mod.createAnonymousDeclFromDecl(decl, .{ + .ty = try Type.Tag.array.create(arena, .{ + .len = test_fn_vals.len, + .elem_type = try tmp_test_fn_ty.copy(arena), + }), + .val = try Value.Tag.array.create(arena, test_fn_vals), + }); + for (mod.test_functions.keys()) |test_decl, i| { + const test_name_slice = mem.sliceTo(test_decl.name, 0); + const test_name_decl = n: { + var name_decl_arena = std.heap.ArenaAllocator.init(gpa); + errdefer name_decl_arena.deinit(); + const bytes = try name_decl_arena.allocator.dupe(u8, test_name_slice); + const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, .{ + .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len), + .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes), + }); + try test_name_decl.finalizeNewArena(&name_decl_arena); + break :n test_name_decl; + }; + try mod.linkerUpdateDecl(test_name_decl); + + const field_vals = try arena.create([3]Value); + field_vals.* = .{ + try Value.Tag.slice.create(arena, .{ + .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl), + .len = try Value.Tag.int_u64.create(arena, test_name_slice.len), + }), // name + try Value.Tag.decl_ref.create(arena, test_decl), // func + Value.initTag(.null_value), // async_frame_size + }; + test_fn_vals[i] = try Value.Tag.@"struct".create(arena, field_vals); + } + + try array_decl.finalizeNewArena(&new_decl_arena); + break :d array_decl; + }; + try mod.linkerUpdateDecl(array_decl); + + { + var arena_instance = decl.value_arena.?.promote(gpa); + defer decl.value_arena.?.* = arena_instance.state; + const arena = &arena_instance.allocator; + + decl.ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena)); + decl.val = try Value.Tag.slice.create(arena, .{ + .ptr = try Value.Tag.decl_ref.create(arena, array_decl), + .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()), + }); + } + try mod.linkerUpdateDecl(decl); +} + +pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void { + mod.comp.bin_file.updateDecl(mod, decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .codegen_failure; + return; + }, + else => { + const gpa = mod.gpa; + try mod.failed_decls.ensureUnusedCapacity(gpa, 1); + mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + gpa, + decl.srcLoc(), + "unable to codegen: {s}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + return; + }, + }; +} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1b3c36fc69..2468b1dbde 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -500,7 +500,18 @@ pub const DeclGen = struct { } else if (decl.val.castTag(.extern_fn)) |extern_fn| { _ = try self.resolveLlvmFunction(extern_fn.data); } else { - _ = try self.resolveGlobalDecl(decl); + const global = try self.resolveGlobalDecl(decl); + assert(decl.has_tv); + const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { + const variable = payload.data; + break :init_val variable.init; + } else init_val: { + global.setGlobalConstant(.True); + break :init_val decl.val; + }; + + const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }); + llvm.setInitializer(global, llvm_init); } } @@ -548,25 +559,11 @@ pub const DeclGen = struct { } fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { - if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val; - - assert(decl.has_tv); - + const llvm_module = self.object.llvm_module; + if (llvm_module.getNamedGlobal(decl.name)) |val| return val; // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. const llvm_type = try self.llvmType(decl.ty); - const global = self.llvmModule().addGlobal(llvm_type, decl.name); - const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { - const variable = payload.data; - break :init_val variable.init; - } else init_val: { - global.setGlobalConstant(.True); - break :init_val decl.val; - }; - - const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null); - llvm.setInitializer(global, llvm_init); - - return global; + return llvm_module.addGlobal(llvm_type, decl.name); } fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type { @@ -596,7 +593,8 @@ pub const DeclGen = struct { }, .Array => { const elem_type = try self.llvmType(t.elemType()); - return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); + const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); + return elem_type.arrayType(@intCast(c_uint, total_len)); }, .Optional => { if (!t.isPtrLikeOptional()) { @@ -674,8 +672,7 @@ pub const DeclGen = struct { } } - // TODO: figure out a way to remove the FuncGen argument - fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value { + fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { const llvm_type = try self.llvmType(tv.ty); if (tv.val.isUndef()) @@ -711,20 +708,36 @@ pub const DeclGen = struct { usize_type.constNull(), }; - // TODO: consider using buildInBoundsGEP2 for opaque pointers - return fg.?.builder.buildInBoundsGEP(val, &indices, 2, ""); + return val.constInBoundsGEP(&indices, indices.len); }, .ref_val => { - const elem_value = tv.val.castTag(.ref_val).?.data; - const elem_type = tv.ty.castPointer().?.data; - const alloca = fg.?.buildAlloca(try self.llvmType(elem_type)); - _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca); - return alloca; + //const elem_value = tv.val.castTag(.ref_val).?.data; + //const elem_type = tv.ty.castPointer().?.data; + //const alloca = fg.?.buildAlloca(try self.llvmType(elem_type)); + //_ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca); + //return alloca; + // TODO eliminate the ref_val Value Tag + return self.todo("implement const of pointer tag ref_val", .{}); }, .variable => { const variable = tv.val.castTag(.variable).?.data; return self.resolveGlobalDecl(variable.owner_decl); }, + .slice => { + const slice = tv.val.castTag(.slice).?.data; + var buf: Type.Payload.ElemType = undefined; + const fields: [2]*const llvm.Value = .{ + try self.genTypedValue(.{ + .ty = tv.ty.slicePtrFieldType(&buf), + .val = slice.ptr, + }), + try self.genTypedValue(.{ + .ty = Type.initTag(.usize), + .val = slice.len, + }), + }; + return self.context.constStruct(&fields, fields.len, .False); + }, else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), }, .Array => { @@ -734,10 +747,28 @@ pub const DeclGen = struct { return self.todo("handle other sentinel values", .{}); } else false; - return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); - } else { - return self.todo("handle more array values", .{}); + return self.context.constString( + payload.data.ptr, + @intCast(c_uint, payload.data.len), + llvm.Bool.fromBool(!zero_sentinel), + ); + } + if (tv.val.castTag(.array)) |payload| { + const gpa = self.gpa; + const elem_ty = tv.ty.elemType(); + const elem_vals = payload.data; + const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); + defer gpa.free(llvm_elems); + for (elem_vals) |elem_val, i| { + llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); + } + const llvm_elem_ty = try self.llvmType(elem_ty); + return llvm_elem_ty.constArray( + llvm_elems.ptr, + @intCast(c_uint, llvm_elems.len), + ); } + return self.todo("handle more array values", .{}); }, .Optional => { if (!tv.ty.isPtrLikeOptional()) { @@ -750,26 +781,25 @@ pub const DeclGen = struct { llvm_child_type.constNull(), self.context.intType(1).constNull(), }; - return self.context.constStruct(&optional_values, 2, .False); + return self.context.constStruct(&optional_values, optional_values.len, .False); } else { var optional_values: [2]*const llvm.Value = .{ - try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg), + try self.genTypedValue(.{ .ty = child_type, .val = tv.val }), self.context.intType(1).constAllOnes(), }; - return self.context.constStruct(&optional_values, 2, .False); + return self.context.constStruct(&optional_values, optional_values.len, .False); } } else { return self.todo("implement const of optional pointer", .{}); } }, .Fn => { - const fn_decl = if (tv.val.castTag(.extern_fn)) |extern_fn| - extern_fn.data - else if (tv.val.castTag(.function)) |func_payload| - func_payload.data.owner_decl - else - unreachable; - + const fn_decl = switch (tv.val.tag()) { + .extern_fn => tv.val.castTag(.extern_fn).?.data, + .function => tv.val.castTag(.function).?.data.owner_decl, + .decl_ref => tv.val.castTag(.decl_ref).?.data, + else => unreachable, + }; return self.resolveLlvmFunction(fn_decl); }, .ErrorSet => { @@ -793,11 +823,29 @@ pub const DeclGen = struct { if (!payload_type.hasCodeGenBits()) { // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = sub_val }, fg); + return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); } return self.todo("implement error union const of type '{}'", .{tv.ty}); }, + .Struct => { + const fields_len = tv.ty.structFieldCount(); + const field_vals = tv.val.castTag(.@"struct").?.data; + const gpa = self.gpa; + const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len); + defer gpa.free(llvm_fields); + for (llvm_fields) |*llvm_field, i| { + llvm_field.* = try self.genTypedValue(.{ + .ty = tv.ty.structFieldType(i), + .val = field_vals[i], + }); + } + return self.context.constStruct( + llvm_fields.ptr, + @intCast(c_uint, llvm_fields.len), + .False, + ); + }, else => return self.todo("implement const of type '{}'", .{tv.ty}), } } @@ -869,7 +917,7 @@ pub const FuncGen = struct { fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value { if (self.air.value(inst)) |val| { - return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self); + return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }); } const inst_index = Air.refToIndex(inst).?; if (self.func_inst_table.get(inst_index)) |value| return value; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 9ef6d7e1ac..d63a75d8bd 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -49,7 +49,12 @@ pub const Context = opaque { extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value; pub const constStruct = LLVMConstStructInContext; - extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value; + extern fn LLVMConstStructInContext( + C: *const Context, + ConstantVals: [*]const *const Value, + Count: c_uint, + Packed: Bool, + ) *const Value; pub const createBasicBlock = LLVMCreateBasicBlockInContext; extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; @@ -100,6 +105,13 @@ pub const Value = opaque { pub const setAliasee = LLVMAliasSetAliasee; extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void; + + pub const constInBoundsGEP = LLVMConstInBoundsGEP; + extern fn LLVMConstInBoundsGEP( + ConstantVal: *const Value, + ConstantIndices: [*]const *const Value, + NumIndices: c_uint, + ) *const Value; }; pub const Type = opaque { @@ -113,7 +125,7 @@ pub const Type = opaque { extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; pub const constArray = LLVMConstArray; - extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value; + extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; pub const getUndef = LLVMGetUndef; extern fn LLVMGetUndef(Ty: *const Type) *const Value; diff --git a/src/type.zig b/src/type.zig index 731c69b806..4dd1a15fdd 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1526,6 +1526,8 @@ pub const Type = extern union { .var_args_param => unreachable, .@"struct" => { + const s = self.castTag(.@"struct").?.data; + assert(s.status == .have_layout); @panic("TODO abiSize struct"); }, .enum_simple, .enum_full, .enum_nonexhaustive => { @@ -2768,6 +2770,26 @@ pub const Type = extern union { } } + pub fn structFieldCount(ty: Type) usize { + switch (ty.tag()) { + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + return struct_obj.fields.count(); + }, + else => unreachable, + } + } + + pub fn structFieldType(ty: Type, index: usize) Type { + switch (ty.tag()) { + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + return struct_obj.fields.values()[index].ty; + }, + else => unreachable, + } + } + pub fn declSrcLoc(ty: Type) Module.SrcLoc { switch (ty.tag()) { .enum_full, .enum_nonexhaustive => { diff --git a/src/value.zig b/src/value.zig index abb2ea7b1e..fc34473921 100644 --- a/src/value.zig +++ b/src/value.zig @@ -112,6 +112,10 @@ pub const Value = extern union { /// This value is repeated some number of times. The amount of times to repeat /// is stored externally. repeated, + /// Each element stored as a `Value`. + array, + /// Pointer and length as sub `Value` objects. + slice, float_16, float_32, float_64, @@ -217,6 +221,9 @@ pub const Value = extern union { .enum_literal, => Payload.Bytes, + .array => Payload.Array, + .slice => Payload.Slice, + .enum_field_index => Payload.U32, .ty => Payload.Ty, @@ -442,6 +449,28 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .array => { + const payload = self.castTag(.array).?; + const new_payload = try allocator.create(Payload.Array); + new_payload.* = .{ + .base = payload.base, + .data = try allocator.alloc(Value, payload.data.len), + }; + std.mem.copy(Value, new_payload.data, payload.data); + return Value{ .ptr_otherwise = &new_payload.base }; + }, + .slice => { + const payload = self.castTag(.slice).?; + const new_payload = try allocator.create(Payload.Slice); + new_payload.* = .{ + .base = payload.base, + .data = .{ + .ptr = try payload.data.ptr.copy(allocator), + .len = try payload.data.len.copy(allocator), + }, + }; + return Value{ .ptr_otherwise = &new_payload.base }; + }, .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), @@ -605,6 +634,8 @@ pub const Value = extern union { try out_stream.writeAll("(repeated) "); val = val.castTag(.repeated).?.data; }, + .array => return out_stream.writeAll("(array)"), + .slice => return out_stream.writeAll("(slice)"), .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), @@ -729,6 +760,8 @@ pub const Value = extern union { .field_ptr, .bytes, .repeated, + .array, + .slice, .float_16, .float_32, .float_64, @@ -1075,6 +1108,8 @@ pub const Value = extern union { return orderAgainstZero(lhs).compare(op); } + /// TODO we can't compare value equality without also knowing the type to treat + /// the values as pub fn eql(a: Value, b: Value) bool { const a_tag = a.tag(); const b_tag = b.tag(); @@ -1109,6 +1144,8 @@ pub const Value = extern union { return @truncate(u32, self.hash()); } + /// TODO we can't hash without also knowing the type of the value. + /// we have to hash as if there were a canonical value memory layout. pub fn hash(self: Value) u64 { var hasher = std.hash.Wyhash.init(0); @@ -1203,6 +1240,15 @@ pub const Value = extern union { const payload = self.castTag(.bytes).?; hasher.update(payload.data); }, + .repeated => { + @panic("TODO Value.hash for repeated"); + }, + .array => { + @panic("TODO Value.hash for array"); + }, + .slice => { + @panic("TODO Value.hash for slice"); + }, .int_u64 => { const payload = self.castTag(.int_u64).?; std.hash.autoHash(&hasher, payload.data); @@ -1211,10 +1257,6 @@ pub const Value = extern union { const payload = self.castTag(.int_i64).?; std.hash.autoHash(&hasher, payload.data); }, - .repeated => { - const payload = self.castTag(.repeated).?; - std.hash.autoHash(&hasher, payload.data.hash()); - }, .ref_val => { const payload = self.castTag(.ref_val).?; std.hash.autoHash(&hasher, payload.data.hash()); @@ -1340,6 +1382,8 @@ pub const Value = extern union { return switch (val.tag()) { .empty_array => 0, .bytes => val.castTag(.bytes).?.data.len, + .array => val.castTag(.array).?.data.len, + .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), .ref_val => sliceLen(val.castTag(.ref_val).?.data), .decl_ref => { const decl = val.castTag(.decl_ref).?.data; @@ -1364,6 +1408,9 @@ pub const Value = extern union { // No matter the index; all the elements are the same! .repeated => return self.castTag(.repeated).?.data, + .array => return self.castTag(.array).?.data[index], + .slice => return self.castTag(.slice).?.data.ptr.elemValue(allocator, index), + else => unreachable, } } @@ -1450,7 +1497,8 @@ pub const Value = extern union { } /// Valid for all types. Asserts the value is not undefined. - pub fn isType(self: Value) bool { + /// TODO this function is a code smell and should be deleted + fn isType(self: Value) bool { return switch (self.tag()) { .ty, .int_type, @@ -1528,6 +1576,8 @@ pub const Value = extern union { .field_ptr, .bytes, .repeated, + .array, + .slice, .float_16, .float_32, .float_64, @@ -1638,6 +1688,19 @@ pub const Value = extern union { data: []const u8, }; + pub const Array = struct { + base: Payload, + data: []Value, + }; + + pub const Slice = struct { + base: Payload, + data: struct { + ptr: Value, + len: Value, + }, + }; + pub const Ty = struct { base: Payload, data: Type, |
