diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 91b0adf0e4..0d92157d31 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -237,7 +237,7 @@ pub const TypeInfo = union(enum) { /// This field is an optional type. /// The type of the sentinel is the element type of the pointer, which is /// the value of the `child` field in this struct. However there is no way - /// to refer to that type here, so we use `var`. + /// to refer to that type here, so we use `anytype`. sentinel: anytype, /// This data structure is used by the Zig language code generation and diff --git a/src/Air.zig b/src/Air.zig index 2c5235e4a1..81d220ba59 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -286,6 +286,10 @@ pub const Inst = struct { /// Result type is the element type of the pointer operand. /// Uses the `bin_op` field. ptr_elem_val, + /// Given a pointer value, and element index, return the element pointer at that index. + /// Result type is pointer to the element type of the pointer operand. + /// Uses the `ty_pl` field with payload `Bin`. + ptr_elem_ptr, /// Given a pointer to a pointer, and element index, return the element value of the inner /// pointer at that index. /// Result type is the element type of the inner pointer operand. @@ -410,6 +414,11 @@ pub const StructField = struct { field_index: u32, }; +pub const Bin = struct { + lhs: Inst.Ref, + rhs: Inst.Ref, +}; + /// Trailing: /// 0. `Inst.Ref` for every outputs_len /// 1. `Inst.Ref` for every inputs_len @@ -482,6 +491,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .constant, .struct_field_ptr, .struct_field_val, + .ptr_elem_ptr, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -527,8 +537,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { }, .slice_elem_val, .ptr_elem_val => { - const slice_ty = air.typeOf(datas[inst].bin_op.lhs); - return slice_ty.elemType(); + const ptr_ty = air.typeOf(datas[inst].bin_op.lhs); + return ptr_ty.elemType(); }, .ptr_slice_elem_val, .ptr_ptr_elem_val => { const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs); diff --git a/src/AstGen.zig b/src/AstGen.zig index e3f33ec332..2b2bbd4f22 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7102,38 +7102,38 @@ fn builtinCall( .bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of), .align_of => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of), - .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int), - .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int), - .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error), - .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error), - .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota), - .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int), - .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int), - .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file), - .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name), - .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic), - .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack), - .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold), - .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .ty = .float_mode_type }, params[0], .set_float_mode), - .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety), - .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), - .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), - .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos), - .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp), - .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2), - .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log), - .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2), - .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10), - .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs), - .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor), - .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil), - .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc), - .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round), - .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name), - .Type => return simpleUnOp(gz, scope, rl, node, .none, params[0], .reify), - .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name), - .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type), - .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size), + .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int), + .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int), + .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error), + .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error), + .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota), + .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int), + .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int), + .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file), + .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name), + .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic), + .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack), + .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold), + .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .float_mode_type }, params[0], .set_float_mode), + .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety), + .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), + .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), + .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos), + .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp), + .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2), + .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log), + .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2), + .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10), + .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs), + .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor), + .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil), + .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc), + .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round), + .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name), + .Type => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type }, params[0], .reify), + .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name), + .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type), + .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size), .float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int), .int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float), diff --git a/src/Liveness.zig b/src/Liveness.zig index 240c82bea5..f6d51e58b4 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -330,6 +330,10 @@ fn analyzeInst( const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none }); }, + .ptr_elem_ptr => { + const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); + }, .br => { const br = inst_datas[inst].br; return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); 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 { diff --git a/src/Sema.zig b/src/Sema.zig index 360d936b61..78f0948623 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1032,25 +1032,27 @@ fn zirEnumDecl( // We create a block for the field type instructions because they // may need to reference Decls from inside the enum namespace. // Within the field type, default value, and alignment expressions, the "owner decl" - // should be the enum itself. Thus we need a new Sema. - var enum_sema: Sema = .{ - .mod = mod, - .gpa = gpa, - .arena = &new_decl_arena.allocator, - .code = sema.code, - .inst_map = sema.inst_map, - .owner_decl = new_decl, - .namespace = &enum_obj.namespace, - .owner_func = null, - .func = null, - .fn_ret_ty = Type.initTag(.void), - .branch_quota = sema.branch_quota, - .branch_count = sema.branch_count, - }; + // should be the enum itself. + + const prev_owner_decl = sema.owner_decl; + sema.owner_decl = new_decl; + defer sema.owner_decl = prev_owner_decl; + + const prev_namespace = sema.namespace; + sema.namespace = &enum_obj.namespace; + defer sema.namespace = prev_namespace; + + const prev_owner_func = sema.owner_func; + sema.owner_func = null; + defer sema.owner_func = prev_owner_func; + + const prev_func = sema.func; + sema.func = null; + defer sema.func = prev_func; var enum_block: Scope.Block = .{ .parent = null, - .sema = &enum_sema, + .sema = sema, .src_decl = new_decl, .instructions = .{}, .inlining = null, @@ -1059,11 +1061,8 @@ fn zirEnumDecl( defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions if (body.len != 0) { - _ = try enum_sema.analyzeBody(&enum_block, body); + _ = try sema.analyzeBody(&enum_block, body); } - - sema.branch_count = enum_sema.branch_count; - sema.branch_quota = enum_sema.branch_quota; } var bit_bag_index: usize = body_end; var cur_bit_bag: u32 = undefined; @@ -1466,8 +1465,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde const ptr = sema.resolveInst(inst_data.operand); const ptr_inst = Air.refToIndex(ptr).?; assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); - const air_datas = sema.air_instructions.items(.data); - const value_index = air_datas[ptr_inst].ty_pl.payload; + const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload; const ptr_val = sema.air_values.items[value_index]; const var_is_mut = switch (sema.typeOf(ptr).tag()) { .inferred_alloc_const => false, @@ -1481,7 +1479,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde const final_elem_ty = try decl.ty.copy(sema.arena); const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One); - air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty); + const final_ptr_ty_inst = try sema.addType(final_ptr_ty); + sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst; if (var_is_mut) { sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{ @@ -5329,10 +5328,16 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + const lhs_ty = sema.typeOf(lhs); if (lhs_val.isUndef() or rhs_val.isUndef()) { - return sema.addConstUndef(sema.typeOf(lhs)); + return sema.addConstUndef(lhs_ty); } - return sema.mod.fail(&block.base, src, "TODO implement comptime shr", .{}); + // If rhs is 0, return lhs without doing any calculations. + if (rhs_val.compareWithZero(.eq)) { + return sema.addConstant(lhs_ty, lhs_val); + } + const val = try lhs_val.shr(rhs_val, sema.arena); + return sema.addConstant(lhs_ty, val); } } @@ -6008,6 +6013,28 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr }), ); }, + .Int => { + const info = ty.intInfo(target); + const field_values = try sema.arena.alloc(Value, 2); + // signedness: Signedness, + field_values[0] = try Value.Tag.enum_field_index.create( + sema.arena, + @enumToInt(info.signedness), + ); + // bits: comptime_int, + field_values[1] = try Value.Tag.int_u64.create(sema.arena, info.bits); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create( + sema.arena, + @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int), + ), + .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr), + }), + ); + }, else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{ @tagName(t), }), @@ -6047,20 +6074,24 @@ fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compil } fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { - if (operand.zigTypeTag() != .Int) return sema.mod.fail( - &block.base, - src, - "bit shifting operation expected integer type, found '{}'", - .{operand}, - ); - - var count: u16 = 0; - var s = operand.bitSize(sema.mod.getTarget()) - 1; - while (s != 0) : (s >>= 1) { - count += 1; + switch (operand.zigTypeTag()) { + .ComptimeInt => return Air.Inst.Ref.comptime_int_type, + .Int => { + var count: u16 = 0; + var s = operand.bitSize(sema.mod.getTarget()) - 1; + while (s != 0) : (s >>= 1) { + count += 1; + } + const res = try Module.makeIntType(sema.arena, .unsigned, count); + return sema.addType(res); + }, + else => return sema.mod.fail( + &block.base, + src, + "bit shifting operation expected integer type, found '{}'", + .{operand}, + ), } - const res = try Module.makeIntType(sema.arena, .unsigned, count); - return sema.addType(res); } fn zirTypeofPeer( @@ -6517,99 +6548,134 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: const first_field_type_data = zir_datas[first_item.field_type].pl_node; const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type); - const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); - const struct_obj = struct_ty.castTag(.@"struct").?.data; + const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); - // Maps field index to field_type index of where it was already initialized. - // For making sure all fields are accounted for and no fields are duplicated. - const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); - defer gpa.free(found_fields); - mem.set(Zir.Inst.Index, found_fields, 0); + if (resolved_ty.castTag(.@"struct")) |struct_payload| { + const struct_obj = struct_payload.data; - // The init values to use for the struct instance. - const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); - defer gpa.free(field_inits); + // Maps field index to field_type index of where it was already initialized. + // For making sure all fields are accounted for and no fields are duplicated. + const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); + defer gpa.free(found_fields); + mem.set(Zir.Inst.Index, found_fields, 0); - var field_i: u32 = 0; - var extra_index = extra.end; + // The init values to use for the struct instance. + const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); + defer gpa.free(field_inits); - while (field_i < extra.data.fields_len) : (field_i += 1) { - const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); - extra_index = item.end; + var field_i: u32 = 0; + var extra_index = extra.end; + + while (field_i < extra.data.fields_len) : (field_i += 1) { + const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); + extra_index = item.end; + + const field_type_data = zir_datas[item.data.field_type].pl_node; + const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; + const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; + const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); + const field_index = struct_obj.fields.getIndex(field_name) orelse + return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name); + if (found_fields[field_index] != 0) { + const other_field_type = found_fields[field_index]; + const other_field_type_data = zir_datas[other_field_type].pl_node; + const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node }; + const msg = msg: { + const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{}); + errdefer msg.destroy(gpa); + try mod.errNote(&block.base, other_field_src, msg, "other field here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(&block.base, msg); + } + found_fields[field_index] = item.data.field_type; + field_inits[field_index] = sema.resolveInst(item.data.init); + } + + var root_msg: ?*Module.ErrorMsg = null; + + for (found_fields) |field_type_inst, i| { + if (field_type_inst != 0) continue; + + // Check if the field has a default init. + const field = struct_obj.fields.values()[i]; + if (field.default_val.tag() == .unreachable_value) { + const field_name = struct_obj.fields.keys()[i]; + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try mod.errNote(&block.base, src, msg, template, args); + } else { + root_msg = try mod.errMsg(&block.base, src, template, args); + } + } else { + field_inits[i] = try sema.addConstant(field.ty, field.default_val); + } + } + if (root_msg) |msg| { + const fqn = try struct_obj.getFullyQualifiedName(gpa); + defer gpa.free(fqn); + try mod.errNoteNonLazy( + struct_obj.srcLoc(), + msg, + "struct '{s}' declared here", + .{fqn}, + ); + return mod.failWithOwnedErrorMsg(&block.base, msg); + } + + if (is_ref) { + return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{}); + } + + const is_comptime = for (field_inits) |field_init| { + if (!(try sema.isComptimeKnown(block, src, field_init))) { + break false; + } + } else true; + + if (is_comptime) { + const values = try sema.arena.alloc(Value, field_inits.len); + for (field_inits) |field_init, i| { + values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; + } + return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr)); + } + + return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); + } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| { + const union_obj = union_payload.data; + + if (extra.data.fields_len != 1) { + return sema.mod.fail(&block.base, src, "union initialization expects exactly one field", .{}); + } + + const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); const field_type_data = zir_datas[item.data.field_type].pl_node; const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); - const field_index = struct_obj.fields.getIndex(field_name) orelse - return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name); - if (found_fields[field_index] != 0) { - const other_field_type = found_fields[field_index]; - const other_field_type_data = zir_datas[other_field_type].pl_node; - const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node }; - const msg = msg: { - const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{}); - errdefer msg.destroy(gpa); - try mod.errNote(&block.base, other_field_src, msg, "other field here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(&block.base, msg); + const field_index = union_obj.fields.getIndex(field_name) orelse + return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); + + if (is_ref) { + return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true union", .{}); } - found_fields[field_index] = item.data.field_type; - field_inits[field_index] = sema.resolveInst(item.data.init); - } - var root_msg: ?*Module.ErrorMsg = null; - - for (found_fields) |field_type_inst, i| { - if (field_type_inst != 0) continue; - - // Check if the field has a default init. - const field = struct_obj.fields.values()[i]; - if (field.default_val.tag() == .unreachable_value) { - const field_name = struct_obj.fields.keys()[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try mod.errNote(&block.base, src, msg, template, args); - } else { - root_msg = try mod.errMsg(&block.base, src, template, args); - } - } else { - field_inits[i] = try sema.addConstant(field.ty, field.default_val); + const init_inst = sema.resolveInst(item.data.init); + if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { + return sema.addConstant( + resolved_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.int_u64.create(sema.arena, field_index), + .val = val, + }), + ); } + return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); } - if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(gpa); - defer gpa.free(fqn); - try mod.errNoteNonLazy( - struct_obj.srcLoc(), - msg, - "struct '{s}' declared here", - .{fqn}, - ); - return mod.failWithOwnedErrorMsg(&block.base, msg); - } - - if (is_ref) { - return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{}); - } - - const is_comptime = for (field_inits) |field_init| { - if (!(try sema.isComptimeKnown(block, src, field_init))) { - break false; - } - } else true; - - if (is_comptime) { - const values = try sema.arena.alloc(Value, field_inits.len); - for (field_inits) |field_init, i| { - values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; - } - return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr)); - } - - return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); + unreachable; } fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { @@ -6647,17 +6713,25 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; const src = inst_data.src(); 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 unresolved_ty = try sema.resolveType(block, src, extra.container_type); + const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_ty); + switch (resolved_ty.zigTypeTag()) { + .Struct => { + const struct_obj = resolved_ty.castTag(.@"struct").?.data; + const field = struct_obj.fields.get(field_name) orelse + return sema.failWithBadFieldAccess(block, struct_obj, src, field_name); + return sema.addType(field.ty); + }, + .Union => { + const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const field = union_obj.fields.get(field_name) orelse + return sema.failWithBadUnionFieldAccess(block, union_obj, src, field_name); + return sema.addType(field.ty); + }, + else => return sema.mod.fail(&block.base, src, "expected struct or union; found '{}'", .{ + resolved_ty, + }), } - 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.failWithBadFieldAccess(block, struct_obj, src, field_name); - return sema.addType(field.ty); } fn zirErrorReturnTrace( @@ -6732,7 +6806,54 @@ fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{}); + const type_info_ty = try sema.getBuiltinType(block, src, "TypeInfo"); + const uncasted_operand = sema.resolveInst(inst_data.operand); + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); + const val = try sema.resolveConstValue(block, operand_src, type_info); + const union_val = val.cast(Value.Payload.Union).?.data; + const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo); + const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt()); + switch (@intToEnum(std.builtin.TypeId, tag_index)) { + .Type => return Air.Inst.Ref.type_type, + .Void => return Air.Inst.Ref.void_type, + .Bool => return Air.Inst.Ref.bool_type, + .NoReturn => return Air.Inst.Ref.noreturn_type, + .Int => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + const signedness_val = struct_val[0]; + const bits_val = struct_val[1]; + + const signedness = signedness_val.toEnum(std.builtin.Signedness); + const bits = @intCast(u16, bits_val.toUnsignedInt()); + const ty = switch (signedness) { + .signed => try Type.Tag.int_signed.create(sema.arena, bits), + .unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits), + }; + return sema.addType(ty); + }, + .Float => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Float", .{}), + .Pointer => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Pointer", .{}), + .Array => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Array", .{}), + .Struct => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Struct", .{}), + .ComptimeFloat => return Air.Inst.Ref.comptime_float_type, + .ComptimeInt => return Air.Inst.Ref.comptime_int_type, + .Undefined => return Air.Inst.Ref.undefined_type, + .Null => return Air.Inst.Ref.null_type, + .Optional => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Optional", .{}), + .ErrorUnion => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorUnion", .{}), + .ErrorSet => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorSet", .{}), + .Enum => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Enum", .{}), + .Union => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Union", .{}), + .Fn => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Fn", .{}), + .BoundFn => @panic("TODO delete BoundFn from the language"), + .Opaque => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Opaque", .{}), + .Frame => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Frame", .{}), + .AnyFrame => return Air.Inst.Ref.anyframe_type, + .Vector => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Vector", .{}), + .EnumLiteral => return Air.Inst.Ref.enum_literal_type, + } } fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -8152,24 +8273,35 @@ fn elemPtrArray( elem_index: Air.Inst.Ref, elem_index_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const array_ptr_ty = sema.typeOf(array_ptr); + const pointee_type = array_ptr_ty.elemType().elemType(); + const result_ty = if (array_ptr_ty.ptrIsMutable()) + try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type) + else + try Type.Tag.single_const_pointer.create(sema.arena, pointee_type); + if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { - if (try sema.resolveDefinedValue(block, src, elem_index)) |index_val| { + if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { // Both array pointer and index are compile-time known. const index_u64 = index_val.toUnsignedInt(); // @intCast here because it would have been impossible to construct a value that // required a larger index. const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); - const pointee_type = sema.typeOf(array_ptr).elemType().elemType(); - - return sema.addConstant( - try Type.Tag.single_const_pointer.create(sema.arena, pointee_type), - elem_ptr, - ); + return sema.addConstant(result_ty, elem_ptr); } } - _ = elem_index; - _ = elem_index_src; - return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr for arrays", .{}); + // TODO safety check for array bounds + try sema.requireRuntimeBlock(block, src); + return block.addInst(.{ + .tag = .ptr_elem_ptr, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(result_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = array_ptr, + .rhs = elem_index, + }), + } }, + }); } fn coerce( @@ -9177,22 +9309,62 @@ pub fn resolveTypeLayout( } } -fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type { +/// `sema` and `block` are expected to be the same ones used for the `Decl`. +pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { switch (ty.tag()) { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.owner_decl.namespace != sema.owner_decl.namespace) return; switch (struct_obj.status) { .none => {}, .field_types_wip => { return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty}); }, + .have_field_types, .have_layout, .layout_wip => return, + } + const prev_namespace = sema.namespace; + sema.namespace = &struct_obj.namespace; + defer sema.namespace = prev_namespace; + + struct_obj.status = .field_types_wip; + try sema.analyzeStructFields(block, struct_obj); + struct_obj.status = .have_field_types; + }, + .@"union", .union_tagged => { + const union_obj = ty.cast(Type.Payload.Union).?.data; + if (union_obj.owner_decl.namespace != sema.owner_decl.namespace) return; + switch (union_obj.status) { + .none => {}, + .field_types_wip => { + return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty}); + }, + .have_field_types, .have_layout, .layout_wip => return, + } + const prev_namespace = sema.namespace; + sema.namespace = &union_obj.namespace; + defer sema.namespace = prev_namespace; + + union_obj.status = .field_types_wip; + try sema.analyzeUnionFields(block, union_obj); + union_obj.status = .have_field_types; + }, + else => return, + } +} + +fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type { + switch (ty.tag()) { + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + switch (struct_obj.status) { + .none => unreachable, + .field_types_wip => { + return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty}); + }, .have_field_types, .have_layout, .layout_wip => return ty, } - struct_obj.status = .field_types_wip; - try sema.mod.analyzeStructFields(struct_obj); - struct_obj.status = .have_field_types; - return ty; }, + .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"), .extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"), .export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"), .atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrdering"), @@ -9205,18 +9377,12 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type .@"union", .union_tagged => { const union_obj = ty.cast(Type.Payload.Union).?.data; switch (union_obj.status) { - .none => {}, + .none => unreachable, .field_types_wip => { - return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ - ty, - }); + return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty}); }, .have_field_types, .have_layout, .layout_wip => return ty, } - union_obj.status = .field_types_wip; - try sema.mod.analyzeUnionFields(union_obj); - union_obj.status = .have_field_types; - return ty; }, else => return ty, } @@ -9232,6 +9398,265 @@ fn resolveBuiltinTypeFields( return sema.resolveTypeFields(block, src, resolved_ty); } +fn analyzeStructFields( + sema: *Sema, + block: *Scope.Block, + struct_obj: *Module.Struct, +) CompileError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = sema.gpa; + const zir = sema.code; + 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.ensureTotalCapacity(&decl_arena.allocator, fields_len); + + 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); + 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 = try field_ty.copy(&decl_arena.allocator), + .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. + const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val; + gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator); + } + if (has_default) { + const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + const default_inst = sema.resolveInst(default_ref); + // 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. + const default_val = (try sema.resolveMaybeUndefVal(block, src, default_inst)) orelse + return sema.failWithNeededComptime(block, src); + gop.value_ptr.default_val = try default_val.copy(&decl_arena.allocator); + } + } +} + +fn analyzeUnionFields( + sema: *Sema, + block: *Scope.Block, + union_obj: *Module.Union, +) CompileError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = sema.gpa; + const zir = sema.code; + 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); + + 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 = try field_ty.copy(&decl_arena.allocator), + .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. + const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val; + gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator); + } + } + + // TODO resolve the union tag_type_ref +} + fn getBuiltin( sema: *Sema, block: *Scope.Block, @@ -9344,6 +9769,7 @@ fn typeHasOnePossibleValue( .call_options, .export_options, .extern_options, + .type_info, .@"anyframe", .anyframe_T, .many_const_pointer, @@ -9528,6 +9954,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { .call_options => return .call_options_type, .export_options => return .export_options_type, .extern_options => return .extern_options_type, + .type_info => return .type_info_type, .manyptr_u8 => return .manyptr_u8_type, .manyptr_const_u8 => return .manyptr_const_u8_type, .fn_noreturn_no_args => return .fn_noreturn_no_args_type, diff --git a/src/Zir.zig b/src/Zir.zig index 1aed609de9..2110122580 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -687,14 +687,14 @@ 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, or enum, and a field name as a string index, + /// Given a struct or union, 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, or enum, and a field name as a Ref, + /// Given a struct or union, 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 - /// struct value. + /// Finalizes a typed struct or union initialization, performs validation, and returns the + /// struct or union value. /// Uses the `pl_node` field. Payload is `StructInit`. struct_init, /// Struct initialization syntax, make the result a pointer. @@ -1703,6 +1703,7 @@ pub const Inst = struct { call_options_type, export_options_type, extern_options_type, + type_info_type, manyptr_u8_type, manyptr_const_u8_type, fn_noreturn_no_args_type, @@ -1973,6 +1974,10 @@ pub const Inst = struct { .ty = Type.initTag(.type), .val = Value.initTag(.extern_options_type), }, + .type_info_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_info_type), + }, .undef = .{ .ty = Type.initTag(.@"undefined"), diff --git a/src/codegen.zig b/src/codegen.zig index f4306c5f2b..b2f0e453ea 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -862,6 +862,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .slice_elem_val => try self.airSliceElemVal(inst), .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .constant => unreachable, // excluded from function bodies @@ -1419,6 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } + fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + else => return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); + } + fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d3417d1567..e7994ffd06 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -913,6 +913,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .ptr_elem_val => try airPtrElemVal(o, inst, "["), .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["), + .ptr_elem_ptr => try airPtrElemPtr(o, inst), .slice_elem_val => try airSliceElemVal(o, inst, "["), .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["), @@ -960,6 +961,13 @@ fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { return o.dg.fail("TODO: C backend: airPtrElemVal", .{}); } +fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + return o.dg.fail("TODO: C backend: airPtrElemPtr", .{}); +} + fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { const is_volatile = false; // TODO if (!is_volatile and o.liveness.isUnused(inst)) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 632b275704..745fb036db 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -432,6 +432,8 @@ pub const Object = struct { }, else => |e| return e, }; + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + try self.updateDeclExports(module, decl, decl_exports); } pub fn updateDeclExports( @@ -440,7 +442,9 @@ pub const Object = struct { decl: *const Module.Decl, exports: []const *Module.Export, ) !void { - const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?; + // If the module does not already have the function, we ignore this function call + // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`. + const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return; const is_extern = decl.val.tag() == .extern_fn; if (is_extern or exports.len != 0) { llvm_fn.setLinkage(.External); @@ -1041,6 +1045,7 @@ pub const FuncGen = struct { .slice_elem_val => try self.airSliceElemVal(inst), .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .optional_payload => try self.airOptionalPayload(inst, false), @@ -1296,11 +1301,35 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const indices: [1]*const llvm.Value = .{rhs}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: { + // If this is a single-item pointer to an array, we need another index in the GEP. + const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; + break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } else ptr: { + const indices: [1]*const llvm.Value = .{rhs}; + break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + }; return self.builder.buildLoad(ptr, ""); } + fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const base_ptr = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + if (self.air.typeOf(bin_op.lhs).isSinglePointer()) { + // If this is a single-item pointer to an array, we need another index in the GEP. + const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; + return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } else { + const indices: [1]*const llvm.Value = .{rhs}; + return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } + } + fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const is_volatile = false; // TODO if (!is_volatile and self.liveness.isUnused(inst)) diff --git a/src/print_air.zig b/src/print_air.zig index 738453464b..20badcdc31 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -175,6 +175,7 @@ const Writer = struct { .loop, => try w.writeBlock(s, inst), + .ptr_elem_ptr => try w.writePtrElemPtr(s, inst), .struct_field_ptr => try w.writeStructField(s, inst), .struct_field_val => try w.writeStructField(s, inst), .constant => try w.writeConstant(s, inst), @@ -239,10 +240,19 @@ const Writer = struct { fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; - const extra = w.air.extraData(Air.StructField, ty_pl.payload); + const extra = w.air.extraData(Air.StructField, ty_pl.payload).data; - try w.writeOperand(s, inst, 0, extra.data.struct_operand); - try s.print(", {d}", .{extra.data.field_index}); + try w.writeOperand(s, inst, 0, extra.struct_operand); + try s.print(", {d}", .{extra.field_index}); + } + + fn writePtrElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, extra.rhs); } fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/type.zig b/src/type.zig index 41f392c04a..467e9c931b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -133,6 +133,7 @@ pub const Type = extern union { .@"union", .union_tagged, + .type_info, => return .Union, .var_args_param => unreachable, // can be any type @@ -248,6 +249,30 @@ pub const Type = extern union { }; } + pub fn ptrIsMutable(ty: Type) bool { + return switch (ty.tag()) { + .single_const_pointer_to_comptime_int, + .const_slice_u8, + .single_const_pointer, + .many_const_pointer, + .manyptr_const_u8, + .c_const_pointer, + .const_slice, + => false, + + .single_mut_pointer, + .many_mut_pointer, + .manyptr_u8, + .c_mut_pointer, + .mut_slice, + => true, + + .pointer => ty.castTag(.pointer).?.data.mutable, + + else => unreachable, + }; + } + pub fn ptrInfo(self: Type) Payload.Pointer { switch (self.tag()) { .single_const_pointer_to_comptime_int => return .{ .data = .{ @@ -717,6 +742,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, .@"anyframe", .generic_poison, => unreachable, @@ -928,6 +954,7 @@ pub const Type = extern union { .call_options => return writer.writeAll("std.builtin.CallOptions"), .export_options => return writer.writeAll("std.builtin.ExportOptions"), .extern_options => return writer.writeAll("std.builtin.ExternOptions"), + .type_info => return writer.writeAll("std.builtin.TypeInfo"), .function => { const payload = ty.castTag(.function).?.data; try writer.writeAll("fn("); @@ -1178,6 +1205,7 @@ pub const Type = extern union { .comptime_int, .comptime_float, .enum_literal, + .type_info, => true, .var_args_param => unreachable, @@ -1269,6 +1297,7 @@ pub const Type = extern union { .call_options => return Value.initTag(.call_options_type), .export_options => return Value.initTag(.export_options_type), .extern_options => return Value.initTag(.extern_options_type), + .type_info => return Value.initTag(.type_info_type), .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, else => return Value.Tag.ty.create(allocator, self), @@ -1409,6 +1438,7 @@ pub const Type = extern union { .empty_struct, .empty_struct_literal, .@"opaque", + .type_info, => false, .inferred_alloc_const => unreachable, @@ -1636,6 +1666,7 @@ pub const Type = extern union { .inferred_alloc_mut, .@"opaque", .var_args_param, + .type_info, => unreachable, .generic_poison => unreachable, @@ -1667,6 +1698,7 @@ pub const Type = extern union { .@"opaque" => unreachable, .var_args_param => unreachable, .generic_poison => unreachable, + .type_info => unreachable, .@"struct" => { const s = self.castTag(.@"struct").?.data; @@ -1978,6 +2010,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, => @panic("TODO at some point we gotta resolve builtin types"), }; } @@ -2691,6 +2724,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, .@"anyframe", .anyframe_T, .many_const_pointer, @@ -2778,6 +2812,7 @@ pub const Type = extern union { return switch (self.tag()) { .@"struct" => &self.castTag(.@"struct").?.data.namespace, .enum_full => &self.castTag(.enum_full).?.data.namespace, + .enum_nonexhaustive => &self.castTag(.enum_nonexhaustive).?.data.namespace, .empty_struct => self.castTag(.empty_struct).?.data, .@"opaque" => &self.castTag(.@"opaque").?.data, .@"union" => &self.castTag(.@"union").?.data.namespace, @@ -3022,6 +3057,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, => @panic("TODO resolve std.builtin types"), else => unreachable, } @@ -3058,6 +3094,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, => @panic("TODO resolve std.builtin types"), else => unreachable, } @@ -3167,6 +3204,7 @@ pub const Type = extern union { call_options, export_options, extern_options, + type_info, manyptr_u8, manyptr_const_u8, fn_noreturn_no_args, @@ -3289,6 +3327,7 @@ pub const Type = extern union { .call_options, .export_options, .extern_options, + .type_info, .@"anyframe", => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), diff --git a/src/value.zig b/src/value.zig index 562d7171e8..7b3056bfcf 100644 --- a/src/value.zig +++ b/src/value.zig @@ -68,6 +68,7 @@ pub const Value = extern union { call_options_type, export_options_type, extern_options_type, + type_info_type, manyptr_u8_type, manyptr_const_u8_type, fn_noreturn_no_args_type, @@ -221,6 +222,7 @@ pub const Value = extern union { .call_options_type, .export_options_type, .extern_options_type, + .type_info_type, .generic_poison, => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), @@ -402,6 +404,7 @@ pub const Value = extern union { .call_options_type, .export_options_type, .extern_options_type, + .type_info_type, .generic_poison, => unreachable, @@ -585,6 +588,7 @@ pub const Value = extern union { .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"), .export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"), .extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"), + .type_info_type => return out_stream.writeAll("std.builtin.TypeInfo"), .abi_align_default => return out_stream.writeAll("(default ABI alignment)"), .empty_struct_value => return out_stream.writeAll("struct {}{}"), @@ -743,6 +747,7 @@ pub const Value = extern union { .call_options_type => Type.initTag(.call_options), .export_options_type => Type.initTag(.export_options), .extern_options_type => Type.initTag(.extern_options), + .type_info_type => Type.initTag(.type_info), .int_type => { const payload = self.castTag(.int_type).?.data; @@ -1514,6 +1519,31 @@ pub const Value = extern union { return Tag.int_u64.create(arena, truncated); } + pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { + // TODO is this a performance issue? maybe we should try the operation without + // resorting to BigInt first. + var lhs_space: Value.BigIntSpace = undefined; + const lhs_bigint = lhs.toBigInt(&lhs_space); + const shift = rhs.toUnsignedInt(); + const limbs = try allocator.alloc( + std.math.big.Limb, + lhs_bigint.limbs.len - (shift / (@sizeOf(std.math.big.Limb) * 8)), + ); + var result_bigint = BigIntMutable{ + .limbs = limbs, + .positive = undefined, + .len = undefined, + }; + result_bigint.shiftRight(lhs_bigint, shift); + const result_limbs = result_bigint.limbs[0..result_bigint.len]; + + if (result_bigint.positive) { + return Value.Tag.int_big_positive.create(allocator, result_limbs); + } else { + return Value.Tag.int_big_negative.create(allocator, result_limbs); + } + } + pub fn floatAdd( lhs: Value, rhs: Value, diff --git a/test/behavior.zig b/test/behavior.zig index bb55ec83f1..7836380c13 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,12 +9,13 @@ test { _ = @import("behavior/pointers.zig"); _ = @import("behavior/if.zig"); _ = @import("behavior/cast.zig"); + _ = @import("behavior/array.zig"); if (!builtin.zig_is_stage2) { // Tests that only pass for stage1. _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); - _ = @import("behavior/array.zig"); + _ = @import("behavior/array_stage1.zig"); if (builtin.os.tag != .wasi) { _ = @import("behavior/asm.zig"); _ = @import("behavior/async_fn.zig"); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 84a2cdb36c..232ba87f55 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -3,487 +3,3 @@ const testing = std.testing; const mem = std.mem; const expect = testing.expect; const expectEqual = testing.expectEqual; - -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = @as(u32, 0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - try expect(accumulator == 15); - try expect(getArrayLen(&array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - -test "array with sentinels" { - const S = struct { - fn doTheTest(is_ct: bool) !void { - if (is_ct) { - var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; - // Disabled at runtime because of - // https://github.com/ziglang/zig/issues/4372 - try expectEqual(@as(u8, 0xde), zero_sized[0]); - var reinterpreted = @ptrCast(*[1]u8, &zero_sized); - try expectEqual(@as(u8, 0xde), reinterpreted[0]); - } - var arr: [3:0x55]u8 = undefined; - // Make sure the sentinel pointer is pointing after the last element - if (!is_ct) { - const sentinel_ptr = @ptrToInt(&arr[3]); - const last_elem_ptr = @ptrToInt(&arr[2]); - try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr); - } - // Make sure the sentinel is writeable - arr[3] = 0x55; - } - }; - - try S.doTheTest(false); - comptime try S.doTheTest(true); -} - -test "void arrays" { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - try expect(@sizeOf(@TypeOf(array)) == 0); - try expect(array.len == 4); -} - -test "array literal" { - const hex_mult = [_]u16{ - 4096, - 256, - 16, - 1, - }; - - try expect(hex_mult.len == 4); - try expect(hex_mult[1] == 256); -} - -test "array dot len const expr" { - try expect(comptime x: { - break :x some_array.len == 4; - }); -} - -const ArrayDotLenConstExpr = struct { - y: [some_array.len]u8, -}; -const some_array = [_]u8{ - 0, - 1, - 2, - 3, -}; - -test "nested arrays" { - const array_of_strings = [_][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; - for (array_of_strings) |s, i| { - if (i == 0) try expect(mem.eql(u8, s, "hello")); - if (i == 1) try expect(mem.eql(u8, s, "this")); - if (i == 2) try expect(mem.eql(u8, s, "is")); - if (i == 3) try expect(mem.eql(u8, s, "my")); - if (i == 4) try expect(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - try expect(s_array[0].b == 1); - try expect(s_array[1].b == 2); - try expect(s_array[2].b == 3); -} - -test "array literal with specified size" { - var array = [2]u8{ - 1, - 2, - }; - try expect(array[0] == 1); - try expect(array[1] == 2); -} - -test "array len field" { - var arr = [4]u8{ 0, 0, 0, 0 }; - var ptr = &arr; - try expect(arr.len == 4); - comptime try expect(arr.len == 4); - try expect(ptr.len == 4); - comptime try expect(ptr.len == 4); -} - -test "single-item pointer to array indexing and slicing" { - try testSingleItemPtrArrayIndexSlice(); - comptime try testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() !void { - { - var array: [4]u8 = "aaaa".*; - doSomeMangling(&array); - try expect(mem.eql(u8, "azya", &array)); - } - { - var array = "aaaa".*; - doSomeMangling(&array); - try expect(mem.eql(u8, "azya", &array)); - } -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - -test "implicit cast single-item pointer" { - try testImplicitCastSingleItemPtr(); - comptime try testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() !void { - var byte: u8 = 100; - const slice = @as(*[1]u8, &byte)[0..]; - slice[0] += 1; - try expect(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evalutating function that takes array by value" { - const arr = [_]u8{ 0, 1 }; - _ = comptime testArrayByValAtComptime(arr); - _ = comptime testArrayByValAtComptime(arr); -} - -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - try expect(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} - -test "runtime initialize array elem and then implicit cast to slice" { - var two: i32 = 2; - const x: []const i32 = &[_]i32{two}; - try expect(x[0] == 2); -} - -test "array literal as argument to function" { - const S = struct { - fn entry(two: i32) !void { - try foo(&[_]i32{ - 1, - 2, - 3, - }); - try foo(&[_]i32{ - 1, - two, - 3, - }); - try foo2(true, &[_]i32{ - 1, - 2, - 3, - }); - try foo2(true, &[_]i32{ - 1, - two, - 3, - }); - } - fn foo(x: []const i32) !void { - try expect(x[0] == 1); - try expect(x[1] == 2); - try expect(x[2] == 3); - } - fn foo2(trash: bool, x: []const i32) !void { - try expect(trash); - try expect(x[0] == 1); - try expect(x[1] == 2); - try expect(x[2] == 3); - } - }; - try S.entry(2); - comptime try S.entry(2); -} - -test "double nested array to const slice cast in array literal" { - const S = struct { - fn entry(two: i32) !void { - const cases = [_][]const []const i32{ - &[_][]const i32{&[_]i32{1}}, - &[_][]const i32{&[_]i32{ 2, 3 }}, - &[_][]const i32{ - &[_]i32{4}, - &[_]i32{ 5, 6, 7 }, - }, - }; - try check(&cases); - - const cases2 = [_][]const i32{ - &[_]i32{1}, - &[_]i32{ two, 3 }, - }; - try expect(cases2.len == 2); - try expect(cases2[0].len == 1); - try expect(cases2[0][0] == 1); - try expect(cases2[1].len == 2); - try expect(cases2[1][0] == 2); - try expect(cases2[1][1] == 3); - - const cases3 = [_][]const []const i32{ - &[_][]const i32{&[_]i32{1}}, - &[_][]const i32{&[_]i32{ two, 3 }}, - &[_][]const i32{ - &[_]i32{4}, - &[_]i32{ 5, 6, 7 }, - }, - }; - try check(&cases3); - } - - fn check(cases: []const []const []const i32) !void { - try expect(cases.len == 3); - try expect(cases[0].len == 1); - try expect(cases[0][0].len == 1); - try expect(cases[0][0][0] == 1); - try expect(cases[1].len == 1); - try expect(cases[1][0].len == 2); - try expect(cases[1][0][0] == 2); - try expect(cases[1][0][1] == 3); - try expect(cases[2].len == 2); - try expect(cases[2][0].len == 1); - try expect(cases[2][0][0] == 4); - try expect(cases[2][1].len == 3); - try expect(cases[2][1][0] == 5); - try expect(cases[2][1][1] == 6); - try expect(cases[2][1][2] == 7); - } - }; - try S.entry(2); - comptime try S.entry(2); -} - -test "read/write through global variable array of struct fields initialized via array mult" { - const S = struct { - fn doTheTest() !void { - try expect(storage[0].term == 1); - storage[0] = MyStruct{ .term = 123 }; - try expect(storage[0].term == 123); - } - - pub const MyStruct = struct { - term: usize, - }; - - var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; - }; - try S.doTheTest(); -} - -test "implicit cast zero sized array ptr to slice" { - { - var b = "".*; - const c: []const u8 = &b; - try expect(c.len == 0); - } - { - var b: [0]u8 = "".*; - const c: []const u8 = &b; - try expect(c.len == 0); - } -} - -test "anonymous list literal syntax" { - const S = struct { - fn doTheTest() !void { - var array: [4]u8 = .{ 1, 2, 3, 4 }; - try expect(array[0] == 1); - try expect(array[1] == 2); - try expect(array[2] == 3); - try expect(array[3] == 4); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "anonymous literal in array" { - const S = struct { - const Foo = struct { - a: usize = 2, - b: usize = 4, - }; - fn doTheTest() !void { - var array: [2]Foo = .{ - .{ .a = 3 }, - .{ .b = 3 }, - }; - try expect(array[0].a == 3); - try expect(array[0].b == 4); - try expect(array[1].a == 2); - try expect(array[1].b == 3); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "access the null element of a null terminated array" { - const S = struct { - fn doTheTest() !void { - var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; - try expect(array[4] == 0); - var len: usize = 4; - try expect(array[len] == 0); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type deduction for array subscript expression" { - const S = struct { - fn doTheTest() !void { - var array = [_]u8{ 0x55, 0xAA }; - var v0 = true; - try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]); - var v1 = false; - try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "sentinel element count towards the ABI size calculation" { - const S = struct { - fn doTheTest() !void { - const T = packed struct { - fill_pre: u8 = 0x55, - data: [0:0]u8 = undefined, - fill_post: u8 = 0xAA, - }; - var x = T{}; - var as_slice = mem.asBytes(&x); - try expectEqual(@as(usize, 3), as_slice.len); - try expectEqual(@as(u8, 0x55), as_slice[0]); - try expectEqual(@as(u8, 0xAA), as_slice[2]); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "zero-sized array with recursive type definition" { - const U = struct { - fn foo(comptime T: type, comptime n: usize) type { - return struct { - s: [n]T, - x: usize = n, - }; - } - }; - - const S = struct { - list: U.foo(@This(), 0), - }; - - var t: S = .{ .list = .{ .s = undefined } }; - try expectEqual(@as(usize, 0), t.list.x); -} - -test "type coercion of anon struct literal to array" { - const S = struct { - const U = union { - a: u32, - b: bool, - c: []const u8, - }; - - fn doTheTest() !void { - var x1: u8 = 42; - const t1 = .{ x1, 56, 54 }; - var arr1: [3]u8 = t1; - try expect(arr1[0] == 42); - try expect(arr1[1] == 56); - try expect(arr1[2] == 54); - - var x2: U = .{ .a = 42 }; - const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; - var arr2: [3]U = t2; - try expect(arr2[0].a == 42); - try expect(arr2[1].b == true); - try expect(mem.eql(u8, arr2[2].c, "hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type coercion of pointer to anon struct literal to pointer to array" { - const S = struct { - const U = union { - a: u32, - b: bool, - c: []const u8, - }; - - fn doTheTest() !void { - var x1: u8 = 42; - const t1 = &.{ x1, 56, 54 }; - var arr1: *const [3]u8 = t1; - try expect(arr1[0] == 42); - try expect(arr1[1] == 56); - try expect(arr1[2] == 54); - - var x2: U = .{ .a = 42 }; - const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; - var arr2: *const [3]U = t2; - try expect(arr2[0].a == 42); - try expect(arr2[1].b == true); - try expect(mem.eql(u8, arr2[2].c, "hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} diff --git a/test/behavior/array_stage1.zig b/test/behavior/array_stage1.zig new file mode 100644 index 0000000000..84a2cdb36c --- /dev/null +++ b/test/behavior/array_stage1.zig @@ -0,0 +1,489 @@ +const std = @import("std"); +const testing = std.testing; +const mem = std.mem; +const expect = testing.expect; +const expectEqual = testing.expectEqual; + +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = @as(u32, 0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + try expect(accumulator == 15); + try expect(getArrayLen(&array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} + +test "array with sentinels" { + const S = struct { + fn doTheTest(is_ct: bool) !void { + if (is_ct) { + var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; + // Disabled at runtime because of + // https://github.com/ziglang/zig/issues/4372 + try expectEqual(@as(u8, 0xde), zero_sized[0]); + var reinterpreted = @ptrCast(*[1]u8, &zero_sized); + try expectEqual(@as(u8, 0xde), reinterpreted[0]); + } + var arr: [3:0x55]u8 = undefined; + // Make sure the sentinel pointer is pointing after the last element + if (!is_ct) { + const sentinel_ptr = @ptrToInt(&arr[3]); + const last_elem_ptr = @ptrToInt(&arr[2]); + try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr); + } + // Make sure the sentinel is writeable + arr[3] = 0x55; + } + }; + + try S.doTheTest(false); + comptime try S.doTheTest(true); +} + +test "void arrays" { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + try expect(@sizeOf(@TypeOf(array)) == 0); + try expect(array.len == 4); +} + +test "array literal" { + const hex_mult = [_]u16{ + 4096, + 256, + 16, + 1, + }; + + try expect(hex_mult.len == 4); + try expect(hex_mult[1] == 256); +} + +test "array dot len const expr" { + try expect(comptime x: { + break :x some_array.len == 4; + }); +} + +const ArrayDotLenConstExpr = struct { + y: [some_array.len]u8, +}; +const some_array = [_]u8{ + 0, + 1, + 2, + 3, +}; + +test "nested arrays" { + const array_of_strings = [_][]const u8{ + "hello", + "this", + "is", + "my", + "thing", + }; + for (array_of_strings) |s, i| { + if (i == 0) try expect(mem.eql(u8, s, "hello")); + if (i == 1) try expect(mem.eql(u8, s, "this")); + if (i == 2) try expect(mem.eql(u8, s, "is")); + if (i == 3) try expect(mem.eql(u8, s, "my")); + if (i == 4) try expect(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { + b: u8, +}; +const Str = struct { + a: []Sub, +}; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + try expect(s_array[0].b == 1); + try expect(s_array[1].b == 2); + try expect(s_array[2].b == 3); +} + +test "array literal with specified size" { + var array = [2]u8{ + 1, + 2, + }; + try expect(array[0] == 1); + try expect(array[1] == 2); +} + +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + try expect(arr.len == 4); + comptime try expect(arr.len == 4); + try expect(ptr.len == 4); + comptime try expect(ptr.len == 4); +} + +test "single-item pointer to array indexing and slicing" { + try testSingleItemPtrArrayIndexSlice(); + comptime try testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() !void { + { + var array: [4]u8 = "aaaa".*; + doSomeMangling(&array); + try expect(mem.eql(u8, "azya", &array)); + } + { + var array = "aaaa".*; + doSomeMangling(&array); + try expect(mem.eql(u8, "azya", &array)); + } +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + try testImplicitCastSingleItemPtr(); + comptime try testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() !void { + var byte: u8 = 100; + const slice = @as(*[1]u8, &byte)[0..]; + slice[0] += 1; + try expect(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evalutating function that takes array by value" { + const arr = [_]u8{ 0, 1 }; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + try expect(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} + +test "runtime initialize array elem and then implicit cast to slice" { + var two: i32 = 2; + const x: []const i32 = &[_]i32{two}; + try expect(x[0] == 2); +} + +test "array literal as argument to function" { + const S = struct { + fn entry(two: i32) !void { + try foo(&[_]i32{ + 1, + 2, + 3, + }); + try foo(&[_]i32{ + 1, + two, + 3, + }); + try foo2(true, &[_]i32{ + 1, + 2, + 3, + }); + try foo2(true, &[_]i32{ + 1, + two, + 3, + }); + } + fn foo(x: []const i32) !void { + try expect(x[0] == 1); + try expect(x[1] == 2); + try expect(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) !void { + try expect(trash); + try expect(x[0] == 1); + try expect(x[1] == 2); + try expect(x[2] == 3); + } + }; + try S.entry(2); + comptime try S.entry(2); +} + +test "double nested array to const slice cast in array literal" { + const S = struct { + fn entry(two: i32) !void { + const cases = [_][]const []const i32{ + &[_][]const i32{&[_]i32{1}}, + &[_][]const i32{&[_]i32{ 2, 3 }}, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, + }, + }; + try check(&cases); + + const cases2 = [_][]const i32{ + &[_]i32{1}, + &[_]i32{ two, 3 }, + }; + try expect(cases2.len == 2); + try expect(cases2[0].len == 1); + try expect(cases2[0][0] == 1); + try expect(cases2[1].len == 2); + try expect(cases2[1][0] == 2); + try expect(cases2[1][1] == 3); + + const cases3 = [_][]const []const i32{ + &[_][]const i32{&[_]i32{1}}, + &[_][]const i32{&[_]i32{ two, 3 }}, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, + }, + }; + try check(&cases3); + } + + fn check(cases: []const []const []const i32) !void { + try expect(cases.len == 3); + try expect(cases[0].len == 1); + try expect(cases[0][0].len == 1); + try expect(cases[0][0][0] == 1); + try expect(cases[1].len == 1); + try expect(cases[1][0].len == 2); + try expect(cases[1][0][0] == 2); + try expect(cases[1][0][1] == 3); + try expect(cases[2].len == 2); + try expect(cases[2][0].len == 1); + try expect(cases[2][0][0] == 4); + try expect(cases[2][1].len == 3); + try expect(cases[2][1][0] == 5); + try expect(cases[2][1][1] == 6); + try expect(cases[2][1][2] == 7); + } + }; + try S.entry(2); + comptime try S.entry(2); +} + +test "read/write through global variable array of struct fields initialized via array mult" { + const S = struct { + fn doTheTest() !void { + try expect(storage[0].term == 1); + storage[0] = MyStruct{ .term = 123 }; + try expect(storage[0].term == 123); + } + + pub const MyStruct = struct { + term: usize, + }; + + var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; + }; + try S.doTheTest(); +} + +test "implicit cast zero sized array ptr to slice" { + { + var b = "".*; + const c: []const u8 = &b; + try expect(c.len == 0); + } + { + var b: [0]u8 = "".*; + const c: []const u8 = &b; + try expect(c.len == 0); + } +} + +test "anonymous list literal syntax" { + const S = struct { + fn doTheTest() !void { + var array: [4]u8 = .{ 1, 2, 3, 4 }; + try expect(array[0] == 1); + try expect(array[1] == 2); + try expect(array[2] == 3); + try expect(array[3] == 4); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "anonymous literal in array" { + const S = struct { + const Foo = struct { + a: usize = 2, + b: usize = 4, + }; + fn doTheTest() !void { + var array: [2]Foo = .{ + .{ .a = 3 }, + .{ .b = 3 }, + }; + try expect(array[0].a == 3); + try expect(array[0].b == 4); + try expect(array[1].a == 2); + try expect(array[1].b == 3); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "access the null element of a null terminated array" { + const S = struct { + fn doTheTest() !void { + var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; + try expect(array[4] == 0); + var len: usize = 4; + try expect(array[len] == 0); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type deduction for array subscript expression" { + const S = struct { + fn doTheTest() !void { + var array = [_]u8{ 0x55, 0xAA }; + var v0 = true; + try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]); + var v1 = false; + try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "sentinel element count towards the ABI size calculation" { + const S = struct { + fn doTheTest() !void { + const T = packed struct { + fill_pre: u8 = 0x55, + data: [0:0]u8 = undefined, + fill_post: u8 = 0xAA, + }; + var x = T{}; + var as_slice = mem.asBytes(&x); + try expectEqual(@as(usize, 3), as_slice.len); + try expectEqual(@as(u8, 0x55), as_slice[0]); + try expectEqual(@as(u8, 0xAA), as_slice[2]); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "zero-sized array with recursive type definition" { + const U = struct { + fn foo(comptime T: type, comptime n: usize) type { + return struct { + s: [n]T, + x: usize = n, + }; + } + }; + + const S = struct { + list: U.foo(@This(), 0), + }; + + var t: S = .{ .list = .{ .s = undefined } }; + try expectEqual(@as(usize, 0), t.list.x); +} + +test "type coercion of anon struct literal to array" { + const S = struct { + const U = union { + a: u32, + b: bool, + c: []const u8, + }; + + fn doTheTest() !void { + var x1: u8 = 42; + const t1 = .{ x1, 56, 54 }; + var arr1: [3]u8 = t1; + try expect(arr1[0] == 42); + try expect(arr1[1] == 56); + try expect(arr1[2] == 54); + + var x2: U = .{ .a = 42 }; + const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; + var arr2: [3]U = t2; + try expect(arr2[0].a == 42); + try expect(arr2[1].b == true); + try expect(mem.eql(u8, arr2[2].c, "hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type coercion of pointer to anon struct literal to pointer to array" { + const S = struct { + const U = union { + a: u32, + b: bool, + c: []const u8, + }; + + fn doTheTest() !void { + var x1: u8 = 42; + const t1 = &.{ x1, 56, 54 }; + var arr1: *const [3]u8 = t1; + try expect(arr1[0] == 42); + try expect(arr1[1] == 56); + try expect(arr1[2] == 54); + + var x2: U = .{ .a = 42 }; + const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; + var arr2: *const [3]U = t2; + try expect(arr2[0].a == 42); + try expect(arr2[1].b == true); + try expect(mem.eql(u8, arr2[2].c, "hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index a97aab7bb3..67103e01ff 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -130,3 +130,21 @@ test "no undeclared identifier error in unanalyzed branches" { lol_this_doesnt_exist = nonsense; } } + +test "a type constructed in a global expression" { + var l: List = undefined; + l.array[0] = 10; + l.array[1] = 11; + l.array[2] = 12; + const ptr = @ptrCast([*]u8, &l.array); + try expect(ptr[0] == 10); + try expect(ptr[1] == 11); + try expect(ptr[2] == 12); +} + +const List = blk: { + const T = [10]u8; + break :blk struct { + array: T, + }; +}; diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index 9e6e938a8c..09649f518c 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -32,18 +32,20 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64); case.addCompareOutput( - \\pub export fn main() void { + \\pub export fn main() c_int { \\ var i: u32 = 16; \\ assert(i >> 1, 8); + \\ return 0; \\} \\fn assert(a: u32, b: u32) void { \\ if (a != b) unreachable; \\} , ""); case.addCompareOutput( - \\pub export fn main() void { + \\pub export fn main() c_int { \\ var i: u32 = 16; \\ assert(i << 1, 32); + \\ return 0; \\} \\fn assert(a: u32, b: u32) void { \\ if (a != b) unreachable;