authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-03 11:46:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-03 11:46:02-07:00
log773902e9de5fc2c7db72c2fdab29ee8e5dd2e337
treece9b18f1c43e93b93c100f351886d1d32e1422f3
parent807a8b6f7549732d73239a649886cc64f0303f34

stage2: hook up semantic analysis of struct fields

Now that they are lazy, they need to get analyzed in the correct context, when requested. This commit also hooks up std.builtin type values being resolved properly. This is needed, for example, with the `@export` builtin function, which occurs in start.zig, for `std.builtin.ExportOptions`. The ZIR code uses the special `Ref.export_options` value, and semantic analysis has to map this to the corresponding type from `std.builtin`.

3 files changed, 225 insertions(+), 136 deletions(-)

src/Module.zig+151-3
......@@ -493,9 +493,10 @@ pub const Struct = struct {
493493 fields: std.StringArrayHashMapUnmanaged(Field),
494494 /// Represents the declarations inside this struct.
495495 namespace: Scope.Namespace,
496
497496 /// Offset from `owner_decl`, points to the struct AST node.
498497 node_offset: i32,
498 /// Index of the struct_decl ZIR instruction.
499 zir_index: Zir.Inst.Index,
499500
500501 layout: std.builtin.TypeInfo.ContainerLayout,
501502 status: enum {
......@@ -2406,6 +2407,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
24062407 }
24072408
24082409 assert(file.zir_loaded);
2410 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2411 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
24092412
24102413 const gpa = mod.gpa;
24112414 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -2418,6 +2421,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
24182421 .owner_decl = undefined, // set below
24192422 .fields = .{},
24202423 .node_offset = 0, // it's the struct for the root file
2424 .zir_index = main_struct_inst,
24212425 .layout = .Auto,
24222426 .status = .none,
24232427 .namespace = .{
......@@ -2468,8 +2472,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
24682472 };
24692473 defer block_scope.instructions.deinit(gpa);
24702474
2471 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2472 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
24732475 try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj);
24742476 try new_decl.finalizeNewArena(&new_decl_arena);
24752477
......@@ -4060,3 +4062,149 @@ pub const SwitchProngSrc = union(enum) {
40604062 } else unreachable;
40614063 }
40624064};
4065
4066pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void {
4067 const tracy = trace(@src());
4068 defer tracy.end();
4069
4070 const gpa = mod.gpa;
4071 const zir = struct_obj.owner_decl.namespace.file_scope.zir;
4072 const inst_data = zir.instructions.items(.data)[struct_obj.zir_index].pl_node;
4073 const src = inst_data.src();
4074 const extra = zir.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
4075 const fields_len = extra.data.fields_len;
4076 const decls_len = extra.data.decls_len;
4077
4078 // Skip over decls.
4079 var extra_index = extra.end;
4080 {
4081 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
4082 var bit_bag_index: usize = extra_index;
4083 extra_index += bit_bags_count;
4084 var cur_bit_bag: u32 = undefined;
4085 var decl_i: u32 = 0;
4086 while (decl_i < decls_len) : (decl_i += 1) {
4087 if (decl_i % 8 == 0) {
4088 cur_bit_bag = zir.extra[bit_bag_index];
4089 bit_bag_index += 1;
4090 }
4091 const flags = @truncate(u4, cur_bit_bag);
4092 cur_bit_bag >>= 4;
4093
4094 extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
4095 extra_index += @truncate(u1, flags >> 2);
4096 extra_index += @truncate(u1, flags >> 3);
4097 }
4098 }
4099
4100 const body = zir.extra[extra_index..][0..extra.data.body_len];
4101 if (fields_len == 0) {
4102 assert(body.len == 0);
4103 return;
4104 }
4105 extra_index += body.len;
4106
4107 var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa);
4108 defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state;
4109
4110 try struct_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
4111
4112 // We create a block for the field type instructions because they
4113 // may need to reference Decls from inside the struct namespace.
4114 // Within the field type, default value, and alignment expressions, the "owner decl"
4115 // should be the struct itself. Thus we need a new Sema.
4116 var sema: Sema = .{
4117 .mod = mod,
4118 .gpa = gpa,
4119 .arena = &decl_arena.allocator,
4120 .code = zir,
4121 .inst_map = try gpa.alloc(*ir.Inst, zir.instructions.len),
4122 .owner_decl = struct_obj.owner_decl,
4123 .namespace = &struct_obj.namespace,
4124 .owner_func = null,
4125 .func = null,
4126 .param_inst_list = &.{},
4127 };
4128 defer gpa.free(sema.inst_map);
4129
4130 var block: Scope.Block = .{
4131 .parent = null,
4132 .sema = &sema,
4133 .src_decl = struct_obj.owner_decl,
4134 .instructions = .{},
4135 .inlining = null,
4136 .is_comptime = true,
4137 };
4138 defer assert(block.instructions.items.len == 0); // should all be comptime instructions
4139
4140 _ = try sema.analyzeBody(&block, body);
4141
4142 const bits_per_field = 4;
4143 const fields_per_u32 = 32 / bits_per_field;
4144 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
4145 var bit_bag_index: usize = extra_index;
4146 extra_index += bit_bags_count;
4147 var cur_bit_bag: u32 = undefined;
4148 var field_i: u32 = 0;
4149 while (field_i < fields_len) : (field_i += 1) {
4150 if (field_i % fields_per_u32 == 0) {
4151 cur_bit_bag = zir.extra[bit_bag_index];
4152 bit_bag_index += 1;
4153 }
4154 const has_align = @truncate(u1, cur_bit_bag) != 0;
4155 cur_bit_bag >>= 1;
4156 const has_default = @truncate(u1, cur_bit_bag) != 0;
4157 cur_bit_bag >>= 1;
4158 const is_comptime = @truncate(u1, cur_bit_bag) != 0;
4159 cur_bit_bag >>= 1;
4160 const unused = @truncate(u1, cur_bit_bag) != 0;
4161 cur_bit_bag >>= 1;
4162
4163 _ = unused;
4164
4165 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
4166 extra_index += 1;
4167 const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4168 extra_index += 1;
4169
4170 // This string needs to outlive the ZIR code.
4171 const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
4172 if (field_type_ref == .none) {
4173 return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
4174 }
4175 const field_ty: Type = if (field_type_ref == .none)
4176 Type.initTag(.noreturn)
4177 else
4178 // TODO: if we need to report an error here, use a source location
4179 // that points to this type expression rather than the struct.
4180 // But only resolve the source location if we need to emit a compile error.
4181 try sema.resolveType(&block, src, field_type_ref);
4182
4183 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
4184 assert(!gop.found_existing);
4185 gop.entry.value = .{
4186 .ty = field_ty,
4187 .abi_align = Value.initTag(.abi_align_default),
4188 .default_val = Value.initTag(.unreachable_value),
4189 .is_comptime = is_comptime,
4190 .offset = undefined,
4191 };
4192
4193 if (has_align) {
4194 const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4195 extra_index += 1;
4196 // TODO: if we need to report an error here, use a source location
4197 // that points to this alignment expression rather than the struct.
4198 // But only resolve the source location if we need to emit a compile error.
4199 gop.entry.value.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
4200 }
4201 if (has_default) {
4202 const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4203 extra_index += 1;
4204 // TODO: if we need to report an error here, use a source location
4205 // that points to this default value expression rather than the struct.
4206 // But only resolve the source location if we need to emit a compile error.
4207 gop.entry.value.default_val = (try sema.resolveInstConst(&block, src, default_ref)).val;
4208 }
4209 }
4210}
src/Sema.zig+72-131
......@@ -572,8 +572,12 @@ fn resolveConstString(
572572 return val.toAllocatedBytes(sema.arena);
573573}
574574
575fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
575pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
576576 const air_inst = try sema.resolveInst(zir_ref);
577 return sema.resolveAirAsType(block, src, air_inst);
578}
579
580fn resolveAirAsType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, air_inst: *ir.Inst) !Type {
577581 const wanted_type = Type.initTag(.@"type");
578582 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
579583 const val = try sema.resolveConstValue(block, src, coerced_inst);
......@@ -675,135 +679,6 @@ pub fn analyzeStructDecl(
675679 _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra.end, decls_len, new_decl);
676680}
677681
678pub fn analyzeStructFields(
679 sema: *Sema,
680 block: *Scope.Block,
681 new_decl_arena: *std.heap.ArenaAllocator,
682) InnerError!void {
683 const tracy = trace(@src());
684 defer tracy.end();
685
686 const mod = sema.mod;
687 const gpa = sema.gpa;
688 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
689 const src = inst_data.src();
690 const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
691 const fields_len = extra.data.fields_len;
692 const decls_len = extra.data.decls_len;
693
694 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
695 if (fields_len == 0) {
696 assert(body.len == 0);
697 return;
698 }
699 extra_index += body.len;
700
701 try struct_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
702
703 {
704 // We create a block for the field type instructions because they
705 // may need to reference Decls from inside the struct namespace.
706 // Within the field type, default value, and alignment expressions, the "owner decl"
707 // should be the struct itself. Thus we need a new Sema.
708 var struct_sema: Sema = .{
709 .mod = mod,
710 .gpa = gpa,
711 .arena = &new_decl_arena.allocator,
712 .code = sema.code,
713 .inst_map = sema.inst_map,
714 .owner_decl = new_decl,
715 .namespace = &struct_obj.namespace,
716 .owner_func = null,
717 .func = null,
718 .param_inst_list = &.{},
719 .branch_quota = sema.branch_quota,
720 .branch_count = sema.branch_count,
721 };
722
723 var struct_block: Scope.Block = .{
724 .parent = null,
725 .sema = &struct_sema,
726 .src_decl = new_decl,
727 .instructions = .{},
728 .inlining = null,
729 .is_comptime = true,
730 };
731 defer assert(struct_block.instructions.items.len == 0); // should all be comptime instructions
732
733 _ = try struct_sema.analyzeBody(&struct_block, body);
734
735 sema.branch_count = struct_sema.branch_count;
736 sema.branch_quota = struct_sema.branch_quota;
737 }
738 const bits_per_field = 4;
739 const fields_per_u32 = 32 / bits_per_field;
740 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
741 var bit_bag_index: usize = extra_index;
742 extra_index += bit_bags_count;
743 var cur_bit_bag: u32 = undefined;
744 var field_i: u32 = 0;
745 while (field_i < fields_len) : (field_i += 1) {
746 if (field_i % fields_per_u32 == 0) {
747 cur_bit_bag = sema.code.extra[bit_bag_index];
748 bit_bag_index += 1;
749 }
750 const has_align = @truncate(u1, cur_bit_bag) != 0;
751 cur_bit_bag >>= 1;
752 const has_default = @truncate(u1, cur_bit_bag) != 0;
753 cur_bit_bag >>= 1;
754 const is_comptime = @truncate(u1, cur_bit_bag) != 0;
755 cur_bit_bag >>= 1;
756 const unused = @truncate(u1, cur_bit_bag) != 0;
757 cur_bit_bag >>= 1;
758
759 _ = unused;
760
761 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
762 extra_index += 1;
763 const field_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
764 extra_index += 1;
765
766 // This string needs to outlive the ZIR code.
767 const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
768 if (field_type_ref == .none) {
769 return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
770 }
771 const field_ty: Type = if (field_type_ref == .none)
772 Type.initTag(.noreturn)
773 else
774 // TODO: if we need to report an error here, use a source location
775 // that points to this type expression rather than the struct.
776 // But only resolve the source location if we need to emit a compile error.
777 try sema.resolveType(block, src, field_type_ref);
778
779 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
780 assert(!gop.found_existing);
781 gop.entry.value = .{
782 .ty = field_ty,
783 .abi_align = Value.initTag(.abi_align_default),
784 .default_val = Value.initTag(.unreachable_value),
785 .is_comptime = is_comptime,
786 };
787
788 if (has_align) {
789 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
790 extra_index += 1;
791 // TODO: if we need to report an error here, use a source location
792 // that points to this alignment expression rather than the struct.
793 // But only resolve the source location if we need to emit a compile error.
794 gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val;
795 }
796 if (has_default) {
797 const default_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
798 extra_index += 1;
799 // TODO: if we need to report an error here, use a source location
800 // that points to this default value expression rather than the struct.
801 // But only resolve the source location if we need to emit a compile error.
802 gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val;
803 }
804 }
805}
806
807682fn zirStructDecl(
808683 sema: *Sema,
809684 block: *Scope.Block,
......@@ -826,6 +701,7 @@ fn zirStructDecl(
826701 .owner_decl = sema.owner_decl,
827702 .fields = .{},
828703 .node_offset = inst_data.src_node,
704 .zir_index = inst,
829705 .layout = layout,
830706 .status = .none,
831707 .namespace = .{
......@@ -5207,8 +5083,23 @@ fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
52075083
52085084fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
52095085 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5086 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
52105087 const src = inst_data.src();
5211 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldType", .{});
5088 const field_name = sema.code.nullTerminatedString(extra.name_start);
5089 const unresolved_struct_type = try sema.resolveType(block, src, extra.container_type);
5090 if (unresolved_struct_type.zigTypeTag() != .Struct) {
5091 return sema.mod.fail(&block.base, src, "expected struct; found '{}'", .{
5092 unresolved_struct_type,
5093 });
5094 }
5095 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
5096 const struct_obj = struct_ty.castTag(.@"struct").?.data;
5097 const field = struct_obj.fields.get(field_name) orelse {
5098 return sema.mod.fail(&block.base, src, "no field named '{s}' in struct '{}'", .{
5099 field_name, struct_ty,
5100 });
5101 };
5102 return sema.mod.constType(sema.arena, src, field.ty);
52125103}
52135104
52145105fn zirErrorReturnTrace(
......@@ -6800,3 +6691,53 @@ fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructi
68006691
68016692 return chosen.ty;
68026693}
6694
6695fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) InnerError!Type {
6696 switch (ty.tag()) {
6697 .@"struct" => {
6698 const struct_obj = ty.castTag(.@"struct").?.data;
6699 switch (struct_obj.status) {
6700 .none => {},
6701 .have_field_types, .have_layout => return ty,
6702 }
6703 try sema.mod.analyzeStructFields(struct_obj);
6704 return ty;
6705 },
6706 .extern_options => {
6707 const extern_options_ty = try sema.getBuiltinType(block, src, "ExternOptions");
6708 return sema.resolveTypeFields(block, src, extern_options_ty);
6709 },
6710 .export_options => {
6711 const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");
6712 return sema.resolveTypeFields(block, src, export_options_ty);
6713 },
6714 else => return ty,
6715 }
6716}
6717
6718fn getBuiltinType(
6719 sema: *Sema,
6720 block: *Scope.Block,
6721 src: LazySrcLoc,
6722 name: []const u8,
6723) InnerError!Type {
6724 const mod = sema.mod;
6725 const std_pkg = mod.root_pkg.table.get("std").?;
6726 const std_file = (mod.importPkg(mod.root_pkg, std_pkg) catch unreachable).file;
6727 const opt_builtin_inst = try sema.analyzeNamespaceLookup(
6728 block,
6729 src,
6730 std_file.namespace,
6731 "builtin",
6732 );
6733 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);
6734 const builtin_ty = try sema.resolveAirAsType(block, src, builtin_inst);
6735 const opt_ty_inst = try sema.analyzeNamespaceLookup(
6736 block,
6737 src,
6738 builtin_ty.getNamespace().?,
6739 name,
6740 );
6741 const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src);
6742 return sema.resolveAirAsType(block, src, ty_inst);
6743}
src/Zir.zig+2-2
......@@ -670,10 +670,10 @@ pub const Inst = struct {
670670 /// A struct literal with a specified type, with no fields.
671671 /// Uses the `un_node` field.
672672 struct_init_empty,
673 /// Given a struct, union, enum, or opaque and a field name as a string index,
673 /// Given a struct, union, or enum, and a field name as a string index,
674674 /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`.
675675 field_type,
676 /// Given a struct, union, enum, or opaque and a field name as a Ref,
676 /// Given a struct, union, or enum, and a field name as a Ref,
677677 /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
678678 field_type_ref,
679679 /// Finalizes a typed struct initialization, performs validation, and returns the