From 0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Aug 2021 15:23:55 -0700 Subject: stage2: field type expressions support referencing locals The big change in this commit is making `semaDecl` resolve the fields if the Decl ends up being a struct or union. It needs to do this while the `Sema` is still in scope, because it will have the resolved AIR instructions that the field type expressions possibly reference. We do this after the decl is populated and set to `complete` so that a `Decl` may reference itself. Everything else is fixes and improvements to make the test suite pass again after making this change. * New AIR instruction: `ptr_elem_ptr` - Implemented for LLVM backend * New Type tag: `type_info` which represents `std.builtin.TypeInfo`. It is used by AstGen for the operand type of `@Type`. * ZIR instruction `set_float_mode` uses `coerced_ty` to avoid superfluous `as` instruction on operand. * ZIR instruction `Type` uses `coerced_ty` to properly handle result location type of operand. * Fix two instances of `enum_nonexhaustive` Value Tag not handled properly - it should generally be handled the same as `enum_full`. * Fix struct and union field resolution not copying Type and Value objects into its Decl arena. * Fix enum tag value resolution discarding the ZIR=>AIR instruction map for the child Sema, when they still needed to be accessed. * Fix `zirResolveInferredAlloc` use-after-free in the AIR instructions data array. * Fix `elemPtrArray` not respecting const/mutable attribute of pointer in the result type. * Fix LLVM backend crashing when `updateDeclExports` is called before `updateDecl`/`updateFunc` (which is, according to the API, perfectly legal for the frontend to do). * Fix LLVM backend handling element pointer of pointer-to-array. It needed another index in the GEP otherwise LLVM saw the wrong type. * Fix LLVM test cases not returning 0 from main, causing test failures. Fixes a regression introduced in 6a5094872f10acc629543cc7f10533b438d0283a. * Implement comptime shift-right. * Implement `@Type` for integers and `@TypeInfo` for integers. * Implement union initialization syntax. * Implement `zirFieldType` for unions. * Implement `elemPtrArray` for a runtime-known operand. * Make `zirLog2IntType` support RHS of shift being `comptime_int`. In this case it returns `comptime_int`. The motivating test case for this commit was originally: ```zig test "example" { var l: List(10) = undefined; l.array[1] = 1; } fn List(comptime L: usize) type { var T = u8; return struct { array: [L]T, }; } ``` However I changed it to: ```zig test "example" { var l: List = undefined; l.array[1] = 1; } const List = blk: { const T = [10]u8; break :blk struct { array: T, }; }; ``` Which ended up being a similar, smaller problem. The former test case will require a similar solution in the implementation of comptime function calls - checking if the result of the function call is a struct or union, and using the child `Sema` before it is destroyed to resolve the fields. --- src/Module.zig | 320 +++------------------------------------------------------ 1 file changed, 15 insertions(+), 305 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 319363e9b8..d55931bec8 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -554,8 +554,8 @@ pub const Decl = struct { assert(struct_obj.owner_decl == decl); return &struct_obj.namespace; }, - .enum_full => { - const enum_obj = ty.castTag(.enum_full).?.data; + .enum_full, .enum_nonexhaustive => { + const enum_obj = ty.cast(Type.Payload.EnumFull).?.data; assert(enum_obj.owner_decl == decl); return &enum_obj.namespace; }, @@ -660,6 +660,7 @@ pub const Struct = struct { /// is necessary to determine whether it has bits at runtime. known_has_bits: bool, + /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl. pub const Field = struct { /// Uses `noreturn` to indicate `anytype`. /// undefined until `status` is `have_field_types` or `have_layout`. @@ -3091,6 +3092,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (linksection_ref == .none) break :blk Value.initTag(.null_value); break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; }; + // Note this resolves the type of the Decl, not the value; if this Decl + // is a struct, for example, this resolves `type` (which needs no resolution), + // not the struct itself. try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); // We need the memory for the Type to go into the arena for the Decl @@ -3193,6 +3197,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (type_changed and mod.emit_h != null) { try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); } + } else if (decl_tv.ty.zigTypeTag() == .Type) { + // In case this Decl is a struct or union, we need to resolve the fields + // while we still have the `Sema` in scope, so that the field type expressions + // can use the resolved AIR instructions that they possibly reference. + // We do this after the decl is populated and set to `complete` so that a `Decl` + // may reference itself. + var buffer: Value.ToTypeBuffer = undefined; + const ty = decl.val.toType(&buffer); + try sema.resolveDeclFields(&block_scope, src, ty); } if (decl.is_exported) { @@ -4450,309 +4463,6 @@ pub const PeerTypeCandidateSrc = union(enum) { } }; -pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = mod.gpa; - const zir = struct_obj.owner_decl.namespace.file_scope.zir; - const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; - assert(extended.opcode == .struct_decl); - const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); - var extra_index: usize = extended.operand; - - const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset }; - extra_index += @boolToInt(small.has_src_node); - - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - // Skip over decls. - var decls_it = zir.declIteratorInner(extra_index, decls_len); - while (decls_it.next()) |_| {} - extra_index = decls_it.extra_index; - - const body = zir.extra[extra_index..][0..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, - .owner_decl = struct_obj.owner_decl, - .namespace = &struct_obj.namespace, - .owner_func = null, - .func = null, - .fn_ret_ty = Type.initTag(.void), - }; - defer sema.deinit(); - - 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 - - if (body.len != 0) { - _ = 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.value_ptr.* = .{ - .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.value_ptr.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.value_ptr.default_val = (try sema.resolveInstConst(&block, src, default_ref)).val; - } - } -} - -pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = mod.gpa; - const zir = union_obj.owner_decl.namespace.file_scope.zir; - const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; - assert(extended.opcode == .union_decl); - const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); - var extra_index: usize = extended.operand; - - const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; - extra_index += @boolToInt(small.has_src_node); - - if (small.has_tag_type) { - extra_index += 1; - } - - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - // Skip over decls. - var decls_it = zir.declIteratorInner(extra_index, decls_len); - while (decls_it.next()) |_| {} - extra_index = decls_it.extra_index; - - const body = zir.extra[extra_index..][0..body_len]; - if (fields_len == 0) { - assert(body.len == 0); - return; - } - extra_index += body.len; - - var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa); - defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; - - try union_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, - .owner_decl = union_obj.owner_decl, - .namespace = &union_obj.namespace, - .owner_func = null, - .func = null, - .fn_ret_ty = Type.initTag(.void), - }; - defer sema.deinit(); - - var block: Scope.Block = .{ - .parent = null, - .sema = &sema, - .src_decl = union_obj.owner_decl, - .instructions = .{}, - .inlining = null, - .is_comptime = true, - }; - defer assert(block.instructions.items.len == 0); // should all be comptime instructions - - if (body.len != 0) { - _ = 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_type = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - const has_align = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - const has_tag = @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: Zir.Inst.Ref = if (has_type) blk: { - const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); - extra_index += 1; - break :blk field_type_ref; - } else .none; - - const align_ref: Zir.Inst.Ref = if (has_align) blk: { - const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); - extra_index += 1; - break :blk align_ref; - } else .none; - - if (has_tag) { - extra_index += 1; - } - - // This string needs to outlive the ZIR code. - const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); - const field_ty: Type = if (field_type_ref == .none) - Type.initTag(.void) - else - // TODO: if we need to report an error here, use a source location - // that points to this type expression rather than the union. - // But only resolve the source location if we need to emit a compile error. - try sema.resolveType(&block, src, field_type_ref); - - const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); - gop.value_ptr.* = .{ - .ty = field_ty, - .abi_align = Value.initTag(.abi_align_default), - }; - - if (align_ref != .none) { - // 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.value_ptr.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val; - } - } - - // TODO resolve the union tag_type_ref -} - /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { -- cgit v1.2.3