authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-30 20:49:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-02 13:16:17-07:00
log0ad2a99675d331c686847a7b2a84feddfcce6573
treeba1b08642950fd481e95bb0cd198ada0176850f5
parentdbd0a2c35d590752dfa586a816f2870e4bcb3200

stage2: CacheMode.whole: trigger loading zig source files

Previously the code asserted source files were already loaded, but this is not the case when cached ZIR is loaded. Now it will trigger .zig source code to be loaded for the purposes of hashing the source for `CacheMode.whole`. This additionally refactors stat_size, stat_inode, and stat_mtime fields into using the `Cache.File.Stat` struct.

3 files changed, 121 insertions(+), 73 deletions(-)

src/Compilation.zig+2-2
...@@ -421,7 +421,7 @@ pub const AllErrors = struct {...@@ -421,7 +421,7 @@ pub const AllErrors = struct {
421 const module_note = module_err_msg.notes[i];421 const module_note = module_err_msg.notes[i];
422 const source = try module_note.src_loc.file_scope.getSource(module.gpa);422 const source = try module_note.src_loc.file_scope.getSource(module.gpa);
423 const byte_offset = try module_note.src_loc.byteOffset(module.gpa);423 const byte_offset = try module_note.src_loc.byteOffset(module.gpa);
424 const loc = std.zig.findLineColumn(source, byte_offset);424 const loc = std.zig.findLineColumn(source.bytes, byte_offset);
425 const file_path = try module_note.src_loc.file_scope.fullPath(allocator);425 const file_path = try module_note.src_loc.file_scope.fullPath(allocator);
426 note.* = .{426 note.* = .{
427 .src = .{427 .src = .{
...@@ -444,7 +444,7 @@ pub const AllErrors = struct {...@@ -444,7 +444,7 @@ pub const AllErrors = struct {
444 }444 }
445 const source = try module_err_msg.src_loc.file_scope.getSource(module.gpa);445 const source = try module_err_msg.src_loc.file_scope.getSource(module.gpa);
446 const byte_offset = try module_err_msg.src_loc.byteOffset(module.gpa);446 const byte_offset = try module_err_msg.src_loc.byteOffset(module.gpa);
447 const loc = std.zig.findLineColumn(source, byte_offset);447 const loc = std.zig.findLineColumn(source.bytes, byte_offset);
448 const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);448 const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);
449 try errors.append(.{449 try errors.append(.{
450 .src = .{450 .src = .{
src/Module.zig+101-55
...@@ -1463,11 +1463,7 @@ pub const File = struct {...@@ -1463,11 +1463,7 @@ pub const File = struct {
1463 /// Whether this is populated depends on `source_loaded`.1463 /// Whether this is populated depends on `source_loaded`.
1464 source: [:0]const u8,1464 source: [:0]const u8,
1465 /// Whether this is populated depends on `status`.1465 /// Whether this is populated depends on `status`.
1466 stat_size: u64,1466 stat: Cache.File.Stat,
1467 /// Whether this is populated depends on `status`.
1468 stat_inode: std.fs.File.INode,
1469 /// Whether this is populated depends on `status`.
1470 stat_mtime: i128,
1471 /// Whether this is populated or not depends on `tree_loaded`.1467 /// Whether this is populated or not depends on `tree_loaded`.
1472 tree: Ast,1468 tree: Ast,
1473 /// Whether this is populated or not depends on `zir_loaded`.1469 /// Whether this is populated or not depends on `zir_loaded`.
...@@ -1535,8 +1531,16 @@ pub const File = struct {...@@ -1535,8 +1531,16 @@ pub const File = struct {
1535 file.* = undefined;1531 file.* = undefined;
1536 }1532 }
15371533
1538 pub fn getSource(file: *File, gpa: Allocator) ![:0]const u8 {1534 pub const Source = struct {
1539 if (file.source_loaded) return file.source;1535 bytes: [:0]const u8,
1536 stat: Cache.File.Stat,
1537 };
1538
1539 pub fn getSource(file: *File, gpa: Allocator) !Source {
1540 if (file.source_loaded) return Source{
1541 .bytes = file.source,
1542 .stat = file.stat,
1543 };
15401544
1541 const root_dir_path = file.pkg.root_src_directory.path orelse ".";1545 const root_dir_path = file.pkg.root_src_directory.path orelse ".";
1542 log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{1546 log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{
...@@ -1565,14 +1569,21 @@ pub const File = struct {...@@ -1565,14 +1569,21 @@ pub const File = struct {
15651569
1566 file.source = source;1570 file.source = source;
1567 file.source_loaded = true;1571 file.source_loaded = true;
1568 return source;1572 return Source{
1573 .bytes = source,
1574 .stat = .{
1575 .size = stat.size,
1576 .inode = stat.inode,
1577 .mtime = stat.mtime,
1578 },
1579 };
1569 }1580 }
15701581
1571 pub fn getTree(file: *File, gpa: Allocator) !*const Ast {1582 pub fn getTree(file: *File, gpa: Allocator) !*const Ast {
1572 if (file.tree_loaded) return &file.tree;1583 if (file.tree_loaded) return &file.tree;
15731584
1574 const source = try file.getSource(gpa);1585 const source = try file.getSource(gpa);
1575 file.tree = try std.zig.parse(gpa, source);1586 file.tree = try std.zig.parse(gpa, source.bytes);
1576 file.tree_loaded = true;1587 file.tree_loaded = true;
1577 return &file.tree;1588 return &file.tree;
1578 }1589 }
...@@ -1631,9 +1642,7 @@ pub const EmbedFile = struct {...@@ -1631,9 +1642,7 @@ pub const EmbedFile = struct {
1631 /// Memory is stored in gpa, owned by EmbedFile.1642 /// Memory is stored in gpa, owned by EmbedFile.
1632 sub_file_path: []const u8,1643 sub_file_path: []const u8,
1633 bytes: [:0]const u8,1644 bytes: [:0]const u8,
1634 stat_size: u64,1645 stat: Cache.File.Stat,
1635 stat_inode: std.fs.File.INode,
1636 stat_mtime: i128,
1637 /// Package that this file is a part of, managed externally.1646 /// Package that this file is a part of, managed externally.
1638 pkg: *Package,1647 pkg: *Package,
1639 /// The Decl that was created from the `@embedFile` to own this resource.1648 /// The Decl that was created from the `@embedFile` to own this resource.
...@@ -2704,9 +2713,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -2704,9 +2713,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
2704 keep_zir = true;2713 keep_zir = true;
2705 file.zir = zir;2714 file.zir = zir;
2706 file.zir_loaded = true;2715 file.zir_loaded = true;
2707 file.stat_size = header.stat_size;2716 file.stat = .{
2708 file.stat_inode = header.stat_inode;2717 .size = header.stat_size,
2709 file.stat_mtime = header.stat_mtime;2718 .inode = header.stat_inode,
2719 .mtime = header.stat_mtime,
2720 };
2710 file.status = .success_zir;2721 file.status = .success_zir;
2711 log.debug("AstGen cached success: {s}", .{file.sub_file_path});2722 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
27122723
...@@ -2724,9 +2735,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -2724,9 +2735,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
2724 },2735 },
2725 .parse_failure, .astgen_failure, .success_zir => {2736 .parse_failure, .astgen_failure, .success_zir => {
2726 const unchanged_metadata =2737 const unchanged_metadata =
2727 stat.size == file.stat_size and2738 stat.size == file.stat.size and
2728 stat.mtime == file.stat_mtime and2739 stat.mtime == file.stat.mtime and
2729 stat.inode == file.stat_inode;2740 stat.inode == file.stat.inode;
27302741
2731 if (unchanged_metadata) {2742 if (unchanged_metadata) {
2732 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});2743 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
...@@ -2787,9 +2798,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -2787,9 +2798,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
2787 if (amt != stat.size)2798 if (amt != stat.size)
2788 return error.UnexpectedEndOfFile;2799 return error.UnexpectedEndOfFile;
27892800
2790 file.stat_size = stat.size;2801 file.stat = .{
2791 file.stat_inode = stat.inode;2802 .size = stat.size,
2792 file.stat_mtime = stat.mtime;2803 .inode = stat.inode,
2804 .mtime = stat.mtime,
2805 };
2793 file.source = source;2806 file.source = source;
2794 file.source_loaded = true;2807 file.source_loaded = true;
27952808
...@@ -3069,9 +3082,11 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3069,9 +3082,11 @@ pub fn populateBuiltinFile(mod: *Module) !void {
30693082
3070 try writeBuiltinFile(file, builtin_pkg);3083 try writeBuiltinFile(file, builtin_pkg);
3071 } else {3084 } else {
3072 file.stat_size = stat.size;3085 file.stat = .{
3073 file.stat_inode = stat.inode;3086 .size = stat.size,
3074 file.stat_mtime = stat.mtime;3087 .inode = stat.inode,
3088 .mtime = stat.mtime,
3089 };
3075 }3090 }
3076 } else |err| switch (err) {3091 } else |err| switch (err) {
3077 error.BadPathName => unreachable, // it's always "builtin.zig"3092 error.BadPathName => unreachable, // it's always "builtin.zig"
...@@ -3099,9 +3114,11 @@ pub fn writeBuiltinFile(file: *File, builtin_pkg: *Package) !void {...@@ -3099,9 +3114,11 @@ pub fn writeBuiltinFile(file: *File, builtin_pkg: *Package) !void {
3099 try af.file.writeAll(file.source);3114 try af.file.writeAll(file.source);
3100 try af.finish();3115 try af.finish();
31013116
3102 file.stat_size = file.source.len;3117 file.stat = .{
3103 file.stat_inode = 0; // dummy value3118 .size = file.source.len,
3104 file.stat_mtime = 0; // dummy value3119 .inode = 0, // dummy value
3120 .mtime = 0, // dummy value
3121 };
3105}3122}
31063123
3107pub fn mapOldZirToNew(3124pub fn mapOldZirToNew(
...@@ -3382,16 +3399,16 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {...@@ -3382,16 +3399,16 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
3382 }3399 }
33833400
3384 if (mod.comp.whole_cache_manifest) |man| {3401 if (mod.comp.whole_cache_manifest) |man| {
3385 assert(file.source_loaded);3402 const source = file.getSource(gpa) catch |err| {
3403 try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)});
3404 return error.AnalysisFail;
3405 };
3386 const resolved_path = try file.pkg.root_src_directory.join(gpa, &.{3406 const resolved_path = try file.pkg.root_src_directory.join(gpa, &.{
3387 file.sub_file_path,3407 file.sub_file_path,
3388 });3408 });
3389 errdefer gpa.free(resolved_path);3409 errdefer gpa.free(resolved_path);
3390 try man.addFilePostContents(resolved_path, file.source, .{3410
3391 .size = file.stat_size,3411 try man.addFilePostContents(resolved_path, source.bytes, source.stat);
3392 .inode = file.stat_inode,
3393 .mtime = file.stat_mtime,
3394 });
3395 }3412 }
3396 } else {3413 } else {
3397 new_decl.analysis = .file_failure;3414 new_decl.analysis = .file_failure;
...@@ -3723,9 +3740,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {...@@ -3723,9 +3740,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {
3723 .source_loaded = false,3740 .source_loaded = false,
3724 .tree_loaded = false,3741 .tree_loaded = false,
3725 .zir_loaded = false,3742 .zir_loaded = false,
3726 .stat_size = undefined,3743 .stat = undefined,
3727 .stat_inode = undefined,
3728 .stat_mtime = undefined,
3729 .tree = undefined,3744 .tree = undefined,
3730 .zir = undefined,3745 .zir = undefined,
3731 .status = .never_loaded,3746 .status = .never_loaded,
...@@ -3793,9 +3808,7 @@ pub fn importFile(...@@ -3793,9 +3808,7 @@ pub fn importFile(
3793 .source_loaded = false,3808 .source_loaded = false,
3794 .tree_loaded = false,3809 .tree_loaded = false,
3795 .zir_loaded = false,3810 .zir_loaded = false,
3796 .stat_size = undefined,3811 .stat = undefined,
3797 .stat_inode = undefined,
3798 .stat_mtime = undefined,
3799 .tree = undefined,3812 .tree = undefined,
3800 .zir = undefined,3813 .zir = undefined,
3801 .status = .never_loaded,3814 .status = .never_loaded,
...@@ -3840,8 +3853,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -3840,8 +3853,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
3840 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});3853 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});
3841 defer file.close();3854 defer file.close();
38423855
3843 const stat = try file.stat();3856 const actual_stat = try file.stat();
3844 const size_usize = try std.math.cast(usize, stat.size);3857 const stat: Cache.File.Stat = .{
3858 .size = actual_stat.size,
3859 .inode = actual_stat.inode,
3860 .mtime = actual_stat.mtime,
3861 };
3862 const size_usize = try std.math.cast(usize, actual_stat.size);
3845 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);3863 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
3846 errdefer gpa.free(bytes);3864 errdefer gpa.free(bytes);
38473865
...@@ -3852,11 +3870,7 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -3852,11 +3870,7 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
3852 if (mod.comp.whole_cache_manifest) |man| {3870 if (mod.comp.whole_cache_manifest) |man| {
3853 const copied_resolved_path = try gpa.dupe(u8, resolved_path);3871 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
3854 errdefer gpa.free(copied_resolved_path);3872 errdefer gpa.free(copied_resolved_path);
3855 try man.addFilePostContents(copied_resolved_path, bytes, .{3873 try man.addFilePostContents(copied_resolved_path, bytes, stat);
3856 .size = stat.size,
3857 .inode = stat.inode,
3858 .mtime = stat.mtime,
3859 });
3860 }3874 }
38613875
3862 keep_resolved_path = true; // It's now owned by embed_table.3876 keep_resolved_path = true; // It's now owned by embed_table.
...@@ -3864,9 +3878,7 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -3864,9 +3878,7 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
3864 new_file.* = .{3878 new_file.* = .{
3865 .sub_file_path = sub_file_path,3879 .sub_file_path = sub_file_path,
3866 .bytes = bytes,3880 .bytes = bytes,
3867 .stat_size = stat.size,3881 .stat = stat,
3868 .stat_inode = stat.inode,
3869 .stat_mtime = stat.mtime,
3870 .pkg = cur_file.pkg,3882 .pkg = cur_file.pkg,
3871 .owner_decl = undefined, // Set by Sema immediately after this function returns.3883 .owner_decl = undefined, // Set by Sema immediately after this function returns.
3872 };3884 };
...@@ -3880,9 +3892,9 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {...@@ -3880,9 +3892,9 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {
3880 const stat = try file.stat();3892 const stat = try file.stat();
38813893
3882 const unchanged_metadata =3894 const unchanged_metadata =
3883 stat.size == embed_file.stat_size and3895 stat.size == embed_file.stat.size and
3884 stat.mtime == embed_file.stat_mtime and3896 stat.mtime == embed_file.stat.mtime and
3885 stat.inode == embed_file.stat_inode;3897 stat.inode == embed_file.stat.inode;
38863898
3887 if (unchanged_metadata) return;3899 if (unchanged_metadata) return;
38883900
...@@ -3891,9 +3903,11 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {...@@ -3891,9 +3903,11 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {
3891 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);3903 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
3892 gpa.free(embed_file.bytes);3904 gpa.free(embed_file.bytes);
3893 embed_file.bytes = bytes;3905 embed_file.bytes = bytes;
3894 embed_file.stat_size = stat.size;3906 embed_file.stat = .{
3895 embed_file.stat_mtime = stat.mtime;3907 .size = stat.size,
3896 embed_file.stat_inode = stat.inode;3908 .mtime = stat.mtime,
3909 .inode = stat.inode,
3910 };
38973911
3898 mod.comp.mutex.lock();3912 mod.comp.mutex.lock();
3899 defer mod.comp.mutex.unlock();3913 defer mod.comp.mutex.unlock();
...@@ -5024,3 +5038,35 @@ pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void {...@@ -5024,3 +5038,35 @@ pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void {
5024 },5038 },
5025 };5039 };
5026}5040}
5041
5042fn reportRetryableFileError(
5043 mod: *Module,
5044 file: *File,
5045 comptime format: []const u8,
5046 args: anytype,
5047) error{OutOfMemory}!void {
5048 file.status = .retryable_failure;
5049
5050 const err_msg = try ErrorMsg.create(
5051 mod.gpa,
5052 .{
5053 .file_scope = file,
5054 .parent_decl_node = 0,
5055 .lazy = .entire_file,
5056 },
5057 format,
5058 args,
5059 );
5060 errdefer err_msg.destroy(mod.gpa);
5061
5062 mod.comp.mutex.lock();
5063 defer mod.comp.mutex.unlock();
5064
5065 const gop = try mod.failed_files.getOrPut(mod.gpa, file);
5066 if (gop.found_existing) {
5067 if (gop.value_ptr.*) |old_err_msg| {
5068 old_err_msg.destroy(mod.gpa);
5069 }
5070 }
5071 gop.value_ptr.* = err_msg;
5072}
src/main.zig+18-16
...@@ -3664,9 +3664,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -3664,9 +3664,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
3664 .zir_loaded = false,3664 .zir_loaded = false,
3665 .sub_file_path = "<stdin>",3665 .sub_file_path = "<stdin>",
3666 .source = source_code,3666 .source = source_code,
3667 .stat_size = undefined,3667 .stat = undefined,
3668 .stat_inode = undefined,
3669 .stat_mtime = undefined,
3670 .tree = tree,3668 .tree = tree,
3671 .tree_loaded = true,3669 .tree_loaded = true,
3672 .zir = undefined,3670 .zir = undefined,
...@@ -3860,9 +3858,11 @@ fn fmtPathFile(...@@ -3860,9 +3858,11 @@ fn fmtPathFile(
3860 .zir_loaded = false,3858 .zir_loaded = false,
3861 .sub_file_path = file_path,3859 .sub_file_path = file_path,
3862 .source = source_code,3860 .source = source_code,
3863 .stat_size = stat.size,3861 .stat = .{
3864 .stat_inode = stat.inode,3862 .size = stat.size,
3865 .stat_mtime = stat.mtime,3863 .inode = stat.inode,
3864 .mtime = stat.mtime,
3865 },
3866 .tree = tree,3866 .tree = tree,
3867 .tree_loaded = true,3867 .tree_loaded = true,
3868 .zir = undefined,3868 .zir = undefined,
...@@ -4458,9 +4458,7 @@ pub fn cmdAstCheck(...@@ -4458,9 +4458,7 @@ pub fn cmdAstCheck(
4458 .zir_loaded = false,4458 .zir_loaded = false,
4459 .sub_file_path = undefined,4459 .sub_file_path = undefined,
4460 .source = undefined,4460 .source = undefined,
4461 .stat_size = undefined,4461 .stat = undefined,
4462 .stat_inode = undefined,
4463 .stat_mtime = undefined,
4464 .tree = undefined,4462 .tree = undefined,
4465 .zir = undefined,4463 .zir = undefined,
4466 .pkg = undefined,4464 .pkg = undefined,
...@@ -4485,9 +4483,11 @@ pub fn cmdAstCheck(...@@ -4485,9 +4483,11 @@ pub fn cmdAstCheck(
4485 file.sub_file_path = file_name;4483 file.sub_file_path = file_name;
4486 file.source = source;4484 file.source = source;
4487 file.source_loaded = true;4485 file.source_loaded = true;
4488 file.stat_size = stat.size;4486 file.stat = .{
4489 file.stat_inode = stat.inode;4487 .size = stat.size,
4490 file.stat_mtime = stat.mtime;4488 .inode = stat.inode,
4489 .mtime = stat.mtime,
4490 };
4491 } else {4491 } else {
4492 const stdin = io.getStdIn();4492 const stdin = io.getStdIn();
4493 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {4493 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {
...@@ -4496,7 +4496,7 @@ pub fn cmdAstCheck(...@@ -4496,7 +4496,7 @@ pub fn cmdAstCheck(
4496 file.sub_file_path = "<stdin>";4496 file.sub_file_path = "<stdin>";
4497 file.source = source;4497 file.source = source;
4498 file.source_loaded = true;4498 file.source_loaded = true;
4499 file.stat_size = source.len;4499 file.stat.size = source.len;
4500 }4500 }
45014501
4502 file.pkg = try Package.create(gpa, null, file.sub_file_path);4502 file.pkg = try Package.create(gpa, null, file.sub_file_path);
...@@ -4609,9 +4609,11 @@ pub fn cmdChangelist(...@@ -4609,9 +4609,11 @@ pub fn cmdChangelist(
4609 .zir_loaded = false,4609 .zir_loaded = false,
4610 .sub_file_path = old_source_file,4610 .sub_file_path = old_source_file,
4611 .source = undefined,4611 .source = undefined,
4612 .stat_size = stat.size,4612 .stat = .{
4613 .stat_inode = stat.inode,4613 .size = stat.size,
4614 .stat_mtime = stat.mtime,4614 .inode = stat.inode,
4615 .mtime = stat.mtime,
4616 },
4615 .tree = undefined,4617 .tree = undefined,
4616 .zir = undefined,4618 .zir = undefined,
4617 .pkg = undefined,4619 .pkg = undefined,