diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 154 | ||||
| -rw-r--r-- | src/Sema.zig | 203 | ||||
| -rw-r--r-- | src/Zir.zig | 4 |
3 files changed, 225 insertions, 136 deletions
diff --git a/src/Module.zig b/src/Module.zig index 57bb2435fb..f780dfb9f6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -493,9 +493,10 @@ pub const Struct = struct { fields: std.StringArrayHashMapUnmanaged(Field), /// Represents the declarations inside this struct. namespace: Scope.Namespace, - /// Offset from `owner_decl`, points to the struct AST node. node_offset: i32, + /// Index of the struct_decl ZIR instruction. + zir_index: Zir.Inst.Index, layout: std.builtin.TypeInfo.ContainerLayout, status: enum { @@ -2406,6 +2407,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { } assert(file.zir_loaded); + const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] - + @intCast(u32, Zir.Inst.Ref.typed_value_map.len); const gpa = mod.gpa; var new_decl_arena = std.heap.ArenaAllocator.init(gpa); @@ -2418,6 +2421,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { .owner_decl = undefined, // set below .fields = .{}, .node_offset = 0, // it's the struct for the root file + .zir_index = main_struct_inst, .layout = .Auto, .status = .none, .namespace = .{ @@ -2468,8 +2472,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { }; defer block_scope.instructions.deinit(gpa); - const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] - - @intCast(u32, Zir.Inst.Ref.typed_value_map.len); try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj); try new_decl.finalizeNewArena(&new_decl_arena); @@ -4060,3 +4062,149 @@ pub const SwitchProngSrc = union(enum) { } else unreachable; } }; + +pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = mod.gpa; + const zir = struct_obj.owner_decl.namespace.file_scope.zir; + const inst_data = zir.instructions.items(.data)[struct_obj.zir_index].pl_node; + const src = inst_data.src(); + const extra = zir.extraData(Zir.Inst.StructDecl, inst_data.payload_index); + const fields_len = extra.data.fields_len; + const decls_len = extra.data.decls_len; + + // Skip over decls. + var extra_index = extra.end; + { + const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; + var bit_bag_index: usize = extra_index; + extra_index += bit_bags_count; + var cur_bit_bag: u32 = undefined; + var decl_i: u32 = 0; + while (decl_i < decls_len) : (decl_i += 1) { + if (decl_i % 8 == 0) { + cur_bit_bag = zir.extra[bit_bag_index]; + bit_bag_index += 1; + } + const flags = @truncate(u4, cur_bit_bag); + cur_bit_bag >>= 4; + + extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1) + extra_index += @truncate(u1, flags >> 2); + extra_index += @truncate(u1, flags >> 3); + } + } + + const body = zir.extra[extra_index..][0..extra.data.body_len]; + if (fields_len == 0) { + assert(body.len == 0); + return; + } + extra_index += body.len; + + var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa); + defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state; + + try struct_obj.fields.ensureCapacity(&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 sema: Sema = .{ + .mod = mod, + .gpa = gpa, + .arena = &decl_arena.allocator, + .code = zir, + .inst_map = try gpa.alloc(*ir.Inst, zir.instructions.len), + .owner_decl = struct_obj.owner_decl, + .namespace = &struct_obj.namespace, + .owner_func = null, + .func = null, + .param_inst_list = &.{}, + }; + defer gpa.free(sema.inst_map); + + var block: Scope.Block = .{ + .parent = null, + .sema = &sema, + .src_decl = struct_obj.owner_decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer assert(block.instructions.items.len == 0); // should all be comptime instructions + + _ = try sema.analyzeBody(&block, body); + + const bits_per_field = 4; + const fields_per_u32 = 32 / bits_per_field; + const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; + var bit_bag_index: usize = extra_index; + extra_index += bit_bags_count; + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % fields_per_u32 == 0) { + cur_bit_bag = zir.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 is_comptime = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const unused = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + _ = unused; + + const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); + extra_index += 1; + const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + + // This string needs to outlive the ZIR code. + const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); + if (field_type_ref == .none) { + return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{}); + } + const field_ty: Type = if (field_type_ref == .none) + Type.initTag(.noreturn) + else + // 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. + 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), + .is_comptime = is_comptime, + .offset = undefined, + }; + + if (has_align) { + const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_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, zir.extra[extra_index]); + extra_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; + } + } +} diff --git a/src/Sema.zig b/src/Sema.zig index 178de4f9bf..6a8a820c98 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -572,8 +572,12 @@ fn resolveConstString( return val.toAllocatedBytes(sema.arena); } -fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { +pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { const air_inst = try sema.resolveInst(zir_ref); + return sema.resolveAirAsType(block, src, air_inst); +} + +fn resolveAirAsType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, air_inst: *ir.Inst) !Type { const wanted_type = Type.initTag(.@"type"); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst); @@ -675,135 +679,6 @@ pub fn analyzeStructDecl( _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra.end, decls_len, new_decl); } -pub fn analyzeStructFields( - sema: *Sema, - block: *Scope.Block, - new_decl_arena: *std.heap.ArenaAllocator, -) InnerError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const mod = sema.mod; - const gpa = sema.gpa; - 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 fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; - - const body = sema.code.extra[extra_index..][0..extra.data.body_len]; - if (fields_len == 0) { - assert(body.len == 0); - return; - } - extra_index += body.len; - - 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 = mod, - .gpa = 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 bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - var bit_bag_index: usize = extra_index; - extra_index += bit_bags_count; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 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 is_comptime = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - const unused = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - - _ = unused; - - const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]); - extra_index += 1; - const field_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_index += 1; - - // This string needs to outlive the ZIR code. - const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir); - if (field_type_ref == .none) { - return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{}); - } - const field_ty: Type = if (field_type_ref == .none) - Type.initTag(.noreturn) - else - // 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. - 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), - .is_comptime = is_comptime, - }; - - if (has_align) { - const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); - extra_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[extra_index]); - extra_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; - } - } -} - fn zirStructDecl( sema: *Sema, block: *Scope.Block, @@ -826,6 +701,7 @@ fn zirStructDecl( .owner_decl = sema.owner_decl, .fields = .{}, .node_offset = inst_data.src_node, + .zir_index = inst, .layout = layout, .status = .none, .namespace = .{ @@ -5207,8 +5083,23 @@ fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldType", .{}); + const field_name = sema.code.nullTerminatedString(extra.name_start); + const unresolved_struct_type = try sema.resolveType(block, src, extra.container_type); + if (unresolved_struct_type.zigTypeTag() != .Struct) { + return sema.mod.fail(&block.base, src, "expected struct; found '{}'", .{ + unresolved_struct_type, + }); + } + const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); + const struct_obj = struct_ty.castTag(.@"struct").?.data; + const field = struct_obj.fields.get(field_name) orelse { + return sema.mod.fail(&block.base, src, "no field named '{s}' in struct '{}'", .{ + field_name, struct_ty, + }); + }; + return sema.mod.constType(sema.arena, src, field.ty); } fn zirErrorReturnTrace( @@ -6800,3 +6691,53 @@ fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructi return chosen.ty; } + +fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) InnerError!Type { + switch (ty.tag()) { + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + switch (struct_obj.status) { + .none => {}, + .have_field_types, .have_layout => return ty, + } + try sema.mod.analyzeStructFields(struct_obj); + return ty; + }, + .extern_options => { + const extern_options_ty = try sema.getBuiltinType(block, src, "ExternOptions"); + return sema.resolveTypeFields(block, src, extern_options_ty); + }, + .export_options => { + const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions"); + return sema.resolveTypeFields(block, src, export_options_ty); + }, + else => return ty, + } +} + +fn getBuiltinType( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + name: []const u8, +) InnerError!Type { + const mod = sema.mod; + const std_pkg = mod.root_pkg.table.get("std").?; + const std_file = (mod.importPkg(mod.root_pkg, std_pkg) catch unreachable).file; + const opt_builtin_inst = try sema.analyzeNamespaceLookup( + block, + src, + std_file.namespace, + "builtin", + ); + const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src); + const builtin_ty = try sema.resolveAirAsType(block, src, builtin_inst); + const opt_ty_inst = try sema.analyzeNamespaceLookup( + block, + src, + builtin_ty.getNamespace().?, + name, + ); + const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src); + return sema.resolveAirAsType(block, src, ty_inst); +} diff --git a/src/Zir.zig b/src/Zir.zig index ec4c97f0f7..8b67618cf1 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -670,10 +670,10 @@ pub const Inst = struct { /// A struct literal with a specified type, with no fields. /// Uses the `un_node` field. struct_init_empty, - /// Given a struct, union, enum, or opaque and a field name as a string index, + /// Given a struct, union, or enum, and a field name as a string index, /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`. field_type, - /// Given a struct, union, enum, or opaque and a field name as a Ref, + /// Given a struct, union, or enum, and a field name as a Ref, /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`. field_type_ref, /// Finalizes a typed struct initialization, performs validation, and returns the |
