diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 64 | ||||
| -rw-r--r-- | src/Compilation.zig | 44 | ||||
| -rw-r--r-- | src/Module.zig | 140 | ||||
| -rw-r--r-- | src/Sema.zig | 252 | ||||
| -rw-r--r-- | src/codegen/spirv/spec.zig | 2 | ||||
| -rw-r--r-- | src/link.zig | 2 | ||||
| -rw-r--r-- | src/stage1/codegen.cpp | 42 | ||||
| -rw-r--r-- | src/value.zig | 27 | ||||
| -rw-r--r-- | src/zir.zig | 46 |
9 files changed, 468 insertions, 151 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 827f545c1b..c3754db766 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1254,6 +1254,8 @@ fn blockExprStmts( .coerce_result_ptr, .decl_ref, .decl_val, + .decl_ref_named, + .decl_val_named, .load, .div, .elem_ptr, @@ -1817,7 +1819,7 @@ pub fn structDeclInner( tag: zir.Inst.Tag, ) InnerError!zir.Inst.Ref { if (container_decl.ast.members.len == 0) { - return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0 }); + return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); } const astgen = gz.astgen; @@ -1826,12 +1828,21 @@ pub fn structDeclInner( const tree = gz.tree(); const node_tags = tree.nodes.items(.tag); + // The struct_decl instruction introduces a scope in which the decls of the struct + // are in scope, so that field types, alignments, and default value expressions + // can refer to decls within the struct itself. + var block_scope: GenZir = .{ + .parent = scope, + .astgen = astgen, + .force_comptime = true, + }; + defer block_scope.instructions.deinit(gpa); + + // We don't know which members are fields until we iterate, so cannot do + // an accurate ensureCapacity yet. var fields_data = ArrayListUnmanaged(u32){}; defer fields_data.deinit(gpa); - // field_name and field_type are both mandatory - try fields_data.ensureCapacity(gpa, container_decl.ast.members.len * 2); - // We only need this if there are greater than 16 fields. var bit_bag = ArrayListUnmanaged(u32){}; defer bit_bag.deinit(gpa); @@ -1857,7 +1868,7 @@ pub fn structDeclInner( const field_name = try gz.identAsString(member.ast.name_token); fields_data.appendAssumeCapacity(field_name); - const field_type = try typeExpr(gz, scope, member.ast.type_expr); + const field_type = try typeExpr(&block_scope, &block_scope.base, member.ast.type_expr); fields_data.appendAssumeCapacity(@enumToInt(field_type)); const have_align = member.ast.align_expr != 0; @@ -1867,31 +1878,40 @@ pub fn structDeclInner( (@as(u32, @boolToInt(have_value)) << 31); if (have_align) { - const align_inst = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, member.ast.align_expr); + const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr); fields_data.appendAssumeCapacity(@enumToInt(align_inst)); } if (have_value) { - const default_inst = try comptimeExpr(gz, scope, .{ .ty = field_type }, member.ast.value_expr); + const default_inst = try expr(&block_scope, &block_scope.base, .{ .ty = field_type }, member.ast.value_expr); fields_data.appendAssumeCapacity(@enumToInt(default_inst)); } field_index += 1; } if (field_index == 0) { - return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0 }); + return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); } const empty_slot_count = 16 - (field_index % 16); cur_bit_bag >>= @intCast(u5, empty_slot_count * 2); - const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{ - .fields_len = @intCast(u32, container_decl.ast.members.len), - }); + const decl_inst = try gz.addBlock(tag, node); + try gz.instructions.append(gpa, decl_inst); + _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); + try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - bit_bag.items.len + 1 + fields_data.items.len); + @typeInfo(zir.Inst.StructDecl).Struct.fields.len + + bit_bag.items.len + 1 + fields_data.items.len + + block_scope.instructions.items.len); + const zir_datas = astgen.instructions.items(.data); + zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.StructDecl{ + .body_len = @intCast(u32, block_scope.instructions.items.len), + .fields_len = @intCast(u32, field_index), + }); + astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items); astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty. astgen.extra.appendAssumeCapacity(cur_bit_bag); astgen.extra.appendSliceAssumeCapacity(fields_data.items); - return result; + return astgen.indexToRef(decl_inst); } fn containerDecl( @@ -3722,16 +3742,16 @@ fn identifier( }; } - const decl = mod.lookupIdentifier(scope, ident_name) orelse { - // TODO insert a "dependency on the non-existence of a decl" here to make this - // compile error go away when the decl is introduced. This data should be in a global - // sparse map since it is only relevant when a compile error occurs. - return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); - }; - const decl_index = try mod.declareDeclDependency(astgen.decl, decl); + // We can't look up Decls until Sema because the same ZIR code is supposed to be + // used for multiple generic instantiations, and this may refer to a different Decl + // depending on the scope, determined by the generic instantiation. + const str_index = try gz.identAsString(ident_token); switch (rl) { - .ref, .none_or_ref => return gz.addDecl(.decl_ref, decl_index, ident), - else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), + .ref, .none_or_ref => return gz.addStrTok(.decl_ref_named, str_index, ident_token), + else => { + const result = try gz.addStrTok(.decl_val_named, str_index, ident_token); + return rvalue(gz, scope, rl, result, ident); + }, } } diff --git a/src/Compilation.zig b/src/Compilation.zig index eaf9b7f5b4..cef24204d1 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -531,7 +531,7 @@ pub const InitOptions = struct { /// is externally modified - essentially anything other than zig-cache - then /// this flag would be set to disable this machinery to avoid false positives. disable_lld_caching: bool = false, - object_format: ?std.builtin.ObjectFormat = null, + object_format: ?std.Target.ObjectFormat = null, optimize_mode: std.builtin.Mode = .Debug, keep_source_files_loaded: bool = false, clang_argv: []const []const u8 = &[0][]const u8{}, @@ -1041,6 +1041,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { try std_pkg.add(gpa, "builtin", builtin_pkg); try std_pkg.add(gpa, "root", root_pkg); + try std_pkg.add(gpa, "std", std_pkg); + + try builtin_pkg.add(gpa, "std", std_pkg); + try builtin_pkg.add(gpa, "builtin", builtin_pkg); } // TODO when we implement serialization and deserialization of incremental @@ -2993,7 +2997,8 @@ fn wantBuildLibCFromSource(comp: Compilation) bool { .Exe => true, }; return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and - comp.bin_file.options.libc_installation == null; + comp.bin_file.options.libc_installation == null and + comp.bin_file.options.object_format != .c; } fn wantBuildGLibCFromSource(comp: Compilation) bool { @@ -3017,6 +3022,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { }; return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and comp.bin_file.options.libc_installation == null and + comp.bin_file.options.object_format != .c and target_util.libcNeedsLibUnwind(comp.getTarget()); } @@ -3068,26 +3074,21 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc @setEvalBranchQuota(4000); try buffer.writer().print( - \\usingnamespace @import("std").builtin; - \\/// Deprecated - \\pub const arch = Target.current.cpu.arch; - \\/// Deprecated - \\pub const endian = Target.current.cpu.arch.endian(); - \\ + \\const std = @import("std"); \\/// Zig version. When writing code that supports multiple versions of Zig, prefer \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. - \\pub const zig_version = try @import("std").SemanticVersion.parse("{s}"); + \\pub const zig_version = try std.SemanticVersion.parse("{s}"); \\pub const zig_is_stage2 = {}; \\ - \\pub const output_mode = OutputMode.{}; - \\pub const link_mode = LinkMode.{}; + \\pub const output_mode = std.builtin.OutputMode.{}; + \\pub const link_mode = std.builtin.LinkMode.{}; \\pub const is_test = {}; \\pub const single_threaded = {}; - \\pub const abi = Abi.{}; - \\pub const cpu: Cpu = Cpu{{ + \\pub const abi = std.Target.Abi.{}; + \\pub const cpu: std.Target.Cpu = .{{ \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ .model = &std.Target.{}.cpu.{}, + \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{ \\ , .{ build_options.version, @@ -3115,7 +3116,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc try buffer.writer().print( \\ }}), \\}}; - \\pub const os = Os{{ + \\pub const os = std.Target.Os{{ \\ .tag = .{}, \\ .version_range = .{{ , @@ -3202,8 +3203,13 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_link_libc); try buffer.writer().print( - \\pub const object_format = ObjectFormat.{}; - \\pub const mode = Mode.{}; + \\pub const target = std.Target{{ + \\ .cpu = cpu, + \\ .os = os, + \\ .abi = abi, + \\}}; + \\pub const object_format = std.Target.ObjectFormat.{}; + \\pub const mode = std.builtin.Mode.{}; \\pub const link_libc = {}; \\pub const link_libcpp = {}; \\pub const have_error_return_tracing = {}; @@ -3211,7 +3217,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc \\pub const position_independent_code = {}; \\pub const position_independent_executable = {}; \\pub const strip_debug_info = {}; - \\pub const code_model = CodeModel.{}; + \\pub const code_model = std.builtin.CodeModel.{}; \\ , .{ std.zig.fmtId(@tagName(comp.bin_file.options.object_format)), diff --git a/src/Module.zig b/src/Module.zig index 20cc6b3c0d..807b30c4d7 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -657,6 +657,7 @@ pub const Scope = struct { /// Direct children of the namespace. Used during an update to detect /// which decls have been added/removed from source. decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + usingnamespace_set: std.AutoHashMapUnmanaged(*Namespace, bool) = .{}, pub fn deinit(ns: *Namespace, gpa: *Allocator) void { ns.decls.deinit(gpa); @@ -2540,6 +2541,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { .code = code, .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -2560,7 +2562,73 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { decl.generation = mod.generation; return true; }, - .@"usingnamespace" => @panic("TODO usingnamespace decl"), + .@"usingnamespace" => { + decl.analysis = .in_progress; + + const type_expr = node_datas[decl_node].lhs; + const is_pub = blk: { + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + const main_token = main_tokens[decl_node]; + break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub); + }; + + // A usingnamespace decl does not store any value so we can + // deinit this arena after analysis is done. + var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); + defer analysis_arena.deinit(); + + var code: zir.Code = blk: { + var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator); + defer astgen.deinit(); + + var gen_scope: Scope.GenZir = .{ + .force_comptime = true, + .parent = &decl.namespace.base, + .astgen = &astgen, + }; + defer gen_scope.instructions.deinit(mod.gpa); + + const ns_type = try AstGen.typeExpr(&gen_scope, &gen_scope.base, type_expr); + _ = try gen_scope.addBreak(.break_inline, 0, ns_type); + + const code = try gen_scope.finish(); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + code.dump(mod.gpa, "usingnamespace_type", &gen_scope.base, 0) catch {}; + } + break :blk code; + }; + defer code.deinit(mod.gpa); + + var sema: Sema = .{ + .mod = mod, + .gpa = mod.gpa, + .arena = &analysis_arena.allocator, + .code = code, + .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), + .owner_decl = decl, + .namespace = decl.namespace, + .func = null, + .owner_func = null, + .param_inst_list = &.{}, + }; + var block_scope: Scope.Block = .{ + .parent = null, + .sema = &sema, + .src_decl = decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer block_scope.instructions.deinit(mod.gpa); + + const ty = try sema.rootAsType(&block_scope); + try decl.namespace.usingnamespace_set.put(mod.gpa, ty.getNamespace().?, is_pub); + + decl.analysis = .complete; + decl.generation = mod.generation; + return true; + }, else => unreachable, } } @@ -2765,6 +2833,7 @@ fn astgenAndSemaFn( .code = fn_type_code, .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3064,6 +3133,7 @@ fn astgenAndSemaVarDecl( .code = code, .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3125,6 +3195,7 @@ fn astgenAndSemaVarDecl( .code = code, .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3387,6 +3458,7 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* .code = code, .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = top_decl, + .namespace = top_decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3411,7 +3483,7 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* struct_decl.contents_hash = top_decl.contents_hash; new_file.namespace = struct_ty.getNamespace().?; new_file.namespace.parent = null; - new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash; + //new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash; // Transfer the dependencies to `owner_decl`. assert(top_decl.dependants.count() == 0); @@ -3422,24 +3494,31 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* _ = try mod.declareDeclDependency(struct_decl, dep); } - try mod.analyzeFile(new_file); return new_file; } pub fn analyzeFile(mod: *Module, file: *Scope.File) !void { - return mod.analyzeNamespace(file.namespace); + // We call `getAstTree` here so that `analyzeFile` has the error set that includes + // file system operations, but `analyzeNamespace` does not. + const tree = try mod.getAstTree(file.namespace.file_scope); + const decls = tree.rootDecls(); + return mod.analyzeNamespace(file.namespace, decls); } -pub fn analyzeNamespace(mod: *Module, namespace: *Scope.Namespace) !void { +pub fn analyzeNamespace( + mod: *Module, + namespace: *Scope.Namespace, + decls: []const ast.Node.Index, +) InnerError!void { const tracy = trace(@src()); defer tracy.end(); // We may be analyzing it for the first time, or this may be // an incremental update. This code handles both cases. - const tree = try mod.getAstTree(namespace.file_scope); + assert(namespace.file_scope.status == .loaded_success); // Caller must ensure tree loaded. + const tree: *const ast.Tree = &namespace.file_scope.tree; const node_tags = tree.nodes.items(.tag); const node_datas = tree.nodes.items(.data); - const decls = tree.rootDecls(); try mod.comp.work_queue.ensureUnusedCapacity(decls.len); try namespace.decls.ensureCapacity(mod.gpa, decls.len); @@ -3612,7 +3691,20 @@ pub fn analyzeNamespace(mod: *Module, namespace: *Scope.Namespace) !void { } }, .@"usingnamespace" => { - log.err("TODO: analyze usingnamespace decl", .{}); + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index}); + defer mod.gpa.free(name); + + const name_hash = namespace.fullyQualifiedNameHash(name); + const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); + + const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); + namespace.decls.putAssumeCapacity(new_decl, {}); + + mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => continue, + }; }, else => unreachable, }; @@ -3900,6 +3992,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { .code = func.zir, .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = func, .owner_func = func, .param_inst_list = param_inst_list, @@ -4001,6 +4094,10 @@ fn createNewDecl( const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash); errdefer mod.gpa.destroy(new_decl); new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name); + log.debug("insert Decl {s} with hash {}", .{ + new_decl.name, + std.fmt.fmtSliceHexLower(&name_hash), + }); mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); return new_decl; } @@ -4245,7 +4342,7 @@ fn getNextAnonNameIndex(mod: *Module) usize { pub fn lookupIdentifier(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { var namespace = scope.namespace(); while (true) { - if (mod.lookupInNamespace(namespace, ident_name)) |decl| { + if (mod.lookupInNamespace(namespace, ident_name, false)) |decl| { return decl; } namespace = namespace.parent orelse break; @@ -4259,9 +4356,32 @@ pub fn lookupInNamespace( mod: *Module, namespace: *Scope.Namespace, ident_name: []const u8, + only_pub_usingnamespaces: bool, ) ?*Decl { const name_hash = namespace.fullyQualifiedNameHash(ident_name); - return mod.decl_table.get(name_hash); + log.debug("lookup Decl {s} with hash {}", .{ + ident_name, + std.fmt.fmtSliceHexLower(&name_hash), + }); + // TODO handle decl collision with usingnamespace + // TODO the decl doing the looking up needs to create a decl dependency + // on each usingnamespace decl here. + if (mod.decl_table.get(name_hash)) |decl| { + return decl; + } + { + var it = namespace.usingnamespace_set.iterator(); + while (it.next()) |entry| { + const other_ns = entry.key; + const other_is_pub = entry.value; + if (only_pub_usingnamespaces and !other_is_pub) continue; + // TODO handle cycles + if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| { + return decl; + } + } + } + return null; } pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { diff --git a/src/Sema.zig b/src/Sema.zig index e8d3a72c64..a18c3bb20c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17,6 +17,8 @@ inst_map: []*Inst, /// and `src_decl` of `Scope.Block` is the `Decl` of the callee. /// This `Decl` owns the arena memory of this `Sema`. owner_decl: *Decl, +/// How to look up decl names. +namespace: *Scope.Namespace, /// For an inline or comptime function call, this will be the root parent function /// which contains the callsite. Corresponds to `owner_decl`. owner_func: ?*Module.Fn, @@ -169,7 +171,9 @@ pub fn analyzeBody( .cmp_neq => try sema.zirCmp(block, inst, .neq), .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), .decl_ref => try sema.zirDeclRef(block, inst), + .decl_ref_named => try sema.zirDeclRefNamed(block, inst), .decl_val => try sema.zirDeclVal(block, inst), + .decl_val_named => try sema.zirDeclValNamed(block, inst), .load => try sema.zirLoad(block, inst), .div => try sema.zirArithmetic(block, inst), .elem_ptr => try sema.zirElemPtr(block, inst), @@ -535,68 +539,10 @@ fn zirStructDecl( const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(zir.Inst.StructDecl, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const fields_len = extra.data.fields_len; - const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - - var fields_map: std.StringArrayHashMapUnmanaged(Module.Struct.Field) = .{}; - try fields_map.ensureCapacity(&new_decl_arena.allocator, fields_len); - - { - var field_index: usize = extra.end + bit_bags_count; - var bit_bag_index: usize = extra.end; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % 16 == 0) { - cur_bit_bag = sema.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_align = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - const has_default = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - - const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[field_index]); - field_index += 1; - const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); - field_index += 1; - - // This string needs to outlive the ZIR code. - const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir); - // TODO: if we need to report an error here, use a source location - // that points to this type expression rather than the struct. - // But only resolve the source location if we need to emit a compile error. - const field_ty = try sema.resolveType(block, src, field_type_ref); - - const gop = fields_map.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); - gop.entry.value = .{ - .ty = field_ty, - .abi_align = Value.initTag(.abi_align_default), - .default_val = Value.initTag(.unreachable_value), - }; - - if (has_align) { - const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); - field_index += 1; - // TODO: if we need to report an error here, use a source location - // that points to this alignment expression rather than the struct. - // But only resolve the source location if we need to emit a compile error. - gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val; - } - if (has_default) { - const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); - field_index += 1; - // TODO: if we need to report an error here, use a source location - // that points to this default value expression rather than the struct. - // But only resolve the source location if we need to emit a compile error. - gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val; - } - } - } const struct_obj = try new_decl_arena.allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); @@ -607,7 +553,7 @@ fn zirStructDecl( }); struct_obj.* = .{ .owner_decl = sema.owner_decl, - .fields = fields_map, + .fields = .{}, .node_offset = inst_data.src_node, .namespace = .{ .parent = sema.owner_decl.namespace, @@ -616,6 +562,128 @@ fn zirStructDecl( .file_scope = block.getFileScope(), }, }; + + { + const ast = std.zig.ast; + const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); + const tree: *const ast.Tree = &struct_obj.namespace.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + var buf: [2]ast.Node.Index = undefined; + const members: []const ast.Node.Index = switch (node_tags[node]) { + .container_decl, + .container_decl_trailing, + => tree.containerDecl(node).ast.members, + + .container_decl_two, + .container_decl_two_trailing, + => tree.containerDeclTwo(&buf, node).ast.members, + + .container_decl_arg, + .container_decl_arg_trailing, + => tree.containerDeclArg(node).ast.members, + + .root => tree.rootDecls(), + else => unreachable, + }; + try sema.mod.analyzeNamespace(&struct_obj.namespace, members); + } + + if (fields_len == 0) { + assert(body.len == 0); + return sema.analyzeDeclVal(block, src, new_decl); + } + + try struct_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len); + + { + // We create a block for the field type instructions because they + // may need to reference Decls from inside the struct namespace. + // Within the field type, default value, and alignment expressions, the "owner decl" + // should be the struct itself. Thus we need a new Sema. + var struct_sema: Sema = .{ + .mod = sema.mod, + .gpa = sema.mod.gpa, + .arena = &new_decl_arena.allocator, + .code = sema.code, + .inst_map = sema.inst_map, + .owner_decl = new_decl, + .namespace = &struct_obj.namespace, + .owner_func = null, + .func = null, + .param_inst_list = &.{}, + .branch_quota = sema.branch_quota, + .branch_count = sema.branch_count, + }; + + var struct_block: Scope.Block = .{ + .parent = null, + .sema = &struct_sema, + .src_decl = new_decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer assert(struct_block.instructions.items.len == 0); // should all be comptime instructions + + _ = try struct_sema.analyzeBody(&struct_block, body); + + sema.branch_count = struct_sema.branch_count; + sema.branch_quota = struct_sema.branch_quota; + } + const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; + const body_end = extra.end + body.len; + var field_index: usize = body_end + bit_bags_count; + var bit_bag_index: usize = body_end; + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % 16 == 0) { + cur_bit_bag = sema.code.extra[bit_bag_index]; + bit_bag_index += 1; + } + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_default = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[field_index]); + field_index += 1; + const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); + field_index += 1; + + // This string needs to outlive the ZIR code. + const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir); + // TODO: if we need to report an error here, use a source location + // that points to this type expression rather than the struct. + // But only resolve the source location if we need to emit a compile error. + const field_ty = try sema.resolveType(block, src, field_type_ref); + + const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); + assert(!gop.found_existing); + gop.entry.value = .{ + .ty = field_ty, + .abi_align = Value.initTag(.abi_align_default), + .default_val = Value.initTag(.unreachable_value), + }; + + if (has_align) { + const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); + field_index += 1; + // TODO: if we need to report an error here, use a source location + // that points to this alignment expression rather than the struct. + // But only resolve the source location if we need to emit a compile error. + gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val; + } + if (has_default) { + const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); + field_index += 1; + // TODO: if we need to report an error here, use a source location + // that points to this default value expression rather than the struct. + // But only resolve the source location if we need to emit a compile error. + gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val; + } + } + return sema.analyzeDeclVal(block, src, new_decl); } @@ -1447,6 +1515,34 @@ fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError return sema.analyzeDeclVal(block, src, decl); } +fn zirDeclRefNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].str_tok; + const src = inst_data.src(); + const decl_name = inst_data.get(sema.code); + const decl = try sema.lookupIdentifier(block, src, decl_name); + return sema.analyzeDeclRef(block, src, decl); +} + +fn zirDeclValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].str_tok; + const src = inst_data.src(); + const decl_name = inst_data.get(sema.code); + const decl = try sema.lookupIdentifier(block, src, decl_name); + return sema.analyzeDeclVal(block, src, decl); +} + +fn lookupIdentifier(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, name: []const u8) !*Decl { + const mod = sema.mod; + const decl = mod.lookupIdentifier(&sema.namespace.base, name) orelse { + // TODO insert a "dependency on the non-existence of a decl" here to make this + // compile error go away when the decl is introduced. This data should be in a global + // sparse map since it is only relevant when a compile error occurs. + return mod.fail(&block.base, src, "use of undeclared identifier '{s}'", .{name}); + }; + _ = try mod.declareDeclDependency(sema.owner_decl, decl); + return decl; +} + fn zirCallNone( sema: *Sema, block: *Scope.Block, @@ -1587,6 +1683,7 @@ fn analyzeCall( .code = module_fn.zir, .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len), .owner_decl = sema.owner_decl, + .namespace = sema.owner_decl.namespace, .owner_func = sema.owner_func, .func = module_fn, .param_inst_list = casted_args, @@ -3647,7 +3744,7 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError "expected struct, enum, union, or opaque, found '{}'", .{container_type}, ); - if (mod.lookupInNamespace(namespace, decl_name)) |decl| { + if (mod.lookupInNamespace(namespace, decl_name, true)) |decl| { if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) { return mod.constBool(arena, src, true); } @@ -3673,7 +3770,8 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError! return mod.fail(&block.base, src, "unable to find '{s}'", .{operand}); }, else => { - // TODO: make sure this gets retried and not cached + // TODO: these errors are file system errors; make sure an update() will + // retry this and not cache the file system error, which may be transient. return mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); }, }; @@ -4069,8 +4167,21 @@ fn zirCmp( const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - try sema.requireRuntimeBlock(block, src); // TODO try to do it at comptime - const bool_type = Type.initTag(.bool); // TODO handle vectors + + if (casted_lhs.value()) |lhs_val| { + if (casted_rhs.value()) |rhs_val| { + if (lhs_val.isUndef() or rhs_val.isUndef()) { + return sema.mod.constInst(sema.arena, src, .{ + .ty = resolved_type, + .val = Value.initTag(.undef), + }); + } + const result = lhs_val.compare(op, rhs_val); + return sema.mod.constBool(sema.arena, src, result); + } + } + + try sema.requireRuntimeBlock(block, src); const tag: Inst.Tag = switch (op) { .lt => .cmp_lt, .lte => .cmp_lte, @@ -4079,6 +4190,7 @@ fn zirCmp( .gt => .cmp_gt, .neq => .cmp_neq, }; + const bool_type = Type.initTag(.bool); // TODO handle vectors return block.addBinOp(src, bool_type, tag, casted_lhs, casted_rhs); } @@ -4525,7 +4637,7 @@ fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { if (block.is_comptime) { - return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); + return sema.failWithNeededComptime(block, src); } try sema.requireFunctionBlock(block, src); } @@ -4775,7 +4887,7 @@ fn analyzeNamespaceLookup( ) InnerError!?*Inst { const mod = sema.mod; const gpa = sema.gpa; - if (mod.lookupInNamespace(namespace, decl_name)) |decl| { + if (mod.lookupInNamespace(namespace, decl_name, true)) |decl| { if (!decl.is_pub and decl.namespace.file_scope != block.getFileScope()) { const msg = msg: { const msg = try mod.errMsg(&block.base, src, "'{s}' is not marked 'pub'", .{ @@ -5639,6 +5751,14 @@ fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructi continue; } + if (chosen.ty.zigTypeTag() == .Enum and candidate.ty.zigTypeTag() == .EnumLiteral) { + continue; + } + if (chosen.ty.zigTypeTag() == .EnumLiteral and candidate.ty.zigTypeTag() == .Enum) { + chosen = candidate; + continue; + } + // TODO error notes pointing out each type return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty }); } diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig index ceb62f1e5d..a014098811 100644 --- a/src/codegen/spirv/spec.zig +++ b/src/codegen/spirv/spec.zig @@ -21,7 +21,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS // IN THE MATERIALS. -const Version = @import("builtin").Version; +const Version = @import("std").builtin.Version; pub const version = Version{ .major = 1, .minor = 5, .patch = 4 }; pub const magic_number: u32 = 0x07230203; pub const Opcode = extern enum(u16) { diff --git a/src/link.zig b/src/link.zig index c0f9a50b2b..0b8e3a0b8e 100644 --- a/src/link.zig +++ b/src/link.zig @@ -30,7 +30,7 @@ pub const Options = struct { target: std.Target, output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, - object_format: std.builtin.ObjectFormat, + object_format: std.Target.ObjectFormat, optimize_mode: std.builtin.Mode, machine_code_model: std.builtin.CodeModel, root_name: []const u8, diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 6d219c517d..968caaf19b 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8921,10 +8921,10 @@ static const char *bool_to_str(bool b) { static const char *build_mode_to_str(BuildMode build_mode) { switch (build_mode) { - case BuildModeDebug: return "Mode.Debug"; - case BuildModeSafeRelease: return "Mode.ReleaseSafe"; - case BuildModeFastRelease: return "Mode.ReleaseFast"; - case BuildModeSmallRelease: return "Mode.ReleaseSmall"; + case BuildModeDebug: return "Debug"; + case BuildModeSafeRelease: return "ReleaseSafe"; + case BuildModeFastRelease: return "ReleaseFast"; + case BuildModeSmallRelease: return "ReleaseSmall"; } zig_unreachable(); } @@ -8995,7 +8995,9 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { g->have_err_ret_tracing = detect_err_ret_tracing(g); Buf *contents = buf_alloc(); - buf_appendf(contents, "usingnamespace @import(\"std\").builtin;\n\n"); + buf_appendf(contents, + "const std = @import(\"std\");\n" + ); const char *cur_os = nullptr; { @@ -9089,19 +9091,23 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { static_assert(TargetSubsystemEfiRom == 6, ""); static_assert(TargetSubsystemEfiRuntimeDriver == 7, ""); - buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch`\n"); - buf_append_str(contents, "pub const arch = Target.current.cpu.arch;\n"); - buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n"); - buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n"); - buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n"); - buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", ZIG_QUOTE(ZIG_LINK_MODE)); + buf_appendf(contents, "pub const output_mode = std.builtin.OutputMode.Obj;\n"); + buf_appendf(contents, "pub const link_mode = std.builtin.LinkMode.%s;\n", ZIG_QUOTE(ZIG_LINK_MODE)); buf_appendf(contents, "pub const is_test = false;\n"); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); - buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); - buf_appendf(contents, "pub const cpu: Cpu = Target.Cpu.baseline(.%s);\n", cur_arch); - buf_appendf(contents, "pub const os = Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os); - buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); - buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); + buf_appendf(contents, "pub const abi = std.Target.Abi.%s;\n", cur_abi); + buf_appendf(contents, "pub const cpu = std.Target.Cpu.baseline(.%s);\n", cur_arch); + buf_appendf(contents, "pub const os = std.Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os); + buf_appendf(contents, + "pub const target = std.Target{\n" + " .cpu = cpu,\n" + " .os = os,\n" + " .abi = abi,\n" + "};\n" + ); + + buf_appendf(contents, "pub const object_format = std.Target.ObjectFormat.%s;\n", cur_obj_fmt); + buf_appendf(contents, "pub const mode = std.builtin.Mode.%s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->link_libc)); buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->link_libcpp)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); @@ -9109,13 +9115,13 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); buf_appendf(contents, "pub const position_independent_executable = %s;\n", bool_to_str(g->have_pie)); buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); - buf_appendf(contents, "pub const code_model = CodeModel.default;\n"); + buf_appendf(contents, "pub const code_model = std.builtin.CodeModel.default;\n"); buf_appendf(contents, "pub const zig_is_stage2 = false;\n"); { TargetSubsystem detected_subsystem = detect_subsystem(g); if (detected_subsystem != TargetSubsystemAuto) { - buf_appendf(contents, "pub const explicit_subsystem = SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); + buf_appendf(contents, "pub const explicit_subsystem = std.builtin.SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); } } diff --git a/src/value.zig b/src/value.zig index 66a23692c1..a9aec47272 100644 --- a/src/value.zig +++ b/src/value.zig @@ -930,7 +930,11 @@ pub const Value = extern union { /// Asserts the value is comparable. pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { - return order(lhs, rhs).compare(op); + return switch (op) { + .eq => lhs.eql(rhs), + .neq => !lhs.eql(rhs), + else => order(lhs, rhs).compare(op), + }; } /// Asserts the value is comparable. @@ -942,12 +946,19 @@ pub const Value = extern union { const a_tag = a.tag(); const b_tag = b.tag(); if (a_tag == b_tag) { - if (a_tag == .void_value or a_tag == .null_value) { - return true; - } else if (a_tag == .enum_literal) { - const a_name = a.castTag(.enum_literal).?.data; - const b_name = b.castTag(.enum_literal).?.data; - return std.mem.eql(u8, a_name, b_name); + switch (a_tag) { + .void_value, .null_value => return true, + .enum_literal => { + const a_name = a.castTag(.enum_literal).?.data; + const b_name = b.castTag(.enum_literal).?.data; + return std.mem.eql(u8, a_name, b_name); + }, + .enum_field_index => { + const a_field_index = a.castTag(.enum_field_index).?.data; + const b_field_index = b.castTag(.enum_field_index).?.data; + return a_field_index == b_field_index; + }, + else => {}, } } if (a.isType() and b.isType()) { @@ -958,7 +969,7 @@ pub const Value = extern union { const b_type = b.toType(&fib.allocator) catch unreachable; return a_type.eql(b_type); } - return compare(a, .eq, b); + return order(a, b).compare(.eq); } pub fn hash_u32(self: Value) u32 { diff --git a/src/zir.zig b/src/zir.zig index 06cf3bcf07..3d000713f2 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -294,6 +294,12 @@ pub const Inst = struct { /// Equivalent to a decl_ref followed by load. /// Uses the `pl_node` union field. `payload_index` is into `decls`. decl_val, + /// Same as `decl_ref` except instead of indexing into decls, uses + /// a name to identify the Decl. Uses the `str_tok` union field. + decl_ref_named, + /// Same as `decl_val` except instead of indexing into decls, uses + /// a name to identify the Decl. Uses the `str_tok` union field. + decl_val_named, /// Load the value from a pointer. Assumes `x.*` syntax. /// Uses `un_node` field. AST node is the `x.*` syntax. load, @@ -744,6 +750,8 @@ pub const Inst = struct { .dbg_stmt_node, .decl_ref, .decl_val, + .decl_ref_named, + .decl_val_named, .load, .div, .elem_ptr, @@ -1507,17 +1515,19 @@ pub const Inst = struct { }; /// Trailing: - /// 0. has_bits: u32 // for every 16 fields + /// 0. inst: Index // for every body_len + /// 1. has_bits: u32 // for every 16 fields /// - sets of 2 bits: /// 0b0X: whether corresponding field has an align expression /// 0bX0: whether corresponding field has a default expression - /// 1. fields: { // for every fields_len + /// 2. fields: { // for every fields_len /// field_name: u32, /// field_type: Ref, /// align: Ref, // if corresponding bit is set /// default_value: Ref, // if corresponding bit is set /// } pub const StructDecl = struct { + body_len: u32, fields_len: u32, }; @@ -1792,6 +1802,8 @@ const Writer = struct { .error_value, .enum_literal, + .decl_ref_named, + .decl_val_named, => try self.writeStrTok(stream, inst), .fn_type => try self.writeFnType(stream, inst, false), @@ -1872,7 +1884,16 @@ const Writer = struct { inst: Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; - try stream.writeAll("TODO)"); + const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; + const str_const = if (!inst_data.is_mutable) "const, " else ""; + const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; + try self.writeInstRef(stream, inst_data.elem_type); + try stream.print(", {s}{s}{s}{s})", .{ + str_allowzero, + str_const, + str_volatile, + @tagName(inst_data.size), + }); } fn writePtrType( @@ -1991,14 +2012,27 @@ const Writer = struct { fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; const fields_len = extra.data.fields_len; - const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; + + if (fields_len == 0) { + assert(body.len == 0); + try stream.writeAll("{}, {}) "); + try self.writeSrc(stream, inst_data.src()); + return; + } try stream.writeAll("{\n"); self.indent += 2; + try self.writeBody(stream, body); - var field_index: usize = extra.end + bit_bags_count; - var bit_bag_index: usize = extra.end; + try stream.writeByteNTimes(' ', self.indent - 2); + try stream.writeAll("}, {\n"); + + const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; + const body_end = extra.end + body.len; + var field_index: usize = body_end + bit_bags_count; + var bit_bag_index: usize = body_end; var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; while (field_i < fields_len) : (field_i += 1) { |
