From 3f60481be45fbde9e37d4b8aaff8a02e1db3e07d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 19 Apr 2021 11:09:00 -0700 Subject: AstGen: implement the remaining struct init ResultLoc forms --- src/AstGen.zig | 205 ++++++++++++++++++++++++++++++++++++++------------------- src/Sema.zig | 23 ++++--- src/Zir.zig | 45 +++++++++---- 3 files changed, 188 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/AstGen.zig b/src/AstGen.zig index d7c379c0a0..0d0dd5db85 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -877,63 +877,115 @@ pub fn structInitExpr( } } switch (rl) { - .discard => return astgen.failNode(node, "TODO implement structInitExpr discard", .{}), - .none, .none_or_ref => return astgen.failNode(node, "TODO implement structInitExpr none", .{}), - .ref => unreachable, // struct literal not valid as l-value - .ty => |ty_inst| { - const fields_list = try gpa.alloc(Zir.Inst.StructInit.Item, struct_init.ast.fields.len); + .discard => { + for (struct_init.ast.fields) |field_init| { + _ = try expr(gz, scope, .discard, field_init); + } + return Zir.Inst.Ref.void_value; + }, + .none, .none_or_ref => { + if (struct_init.ast.type_expr != 0) { + const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); + return structInitExprRlTy(gz, scope, rl, node, struct_init, ty_inst); + } + const fields_list = try gpa.alloc(Zir.Inst.StructInitAnon.Item, struct_init.ast.fields.len); defer gpa.free(fields_list); for (struct_init.ast.fields) |field_init, i| { const name_token = tree.firstToken(field_init) - 2; const str_index = try gz.identAsString(name_token); - const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ - .container_type = ty_inst, - .name_start = str_index, - }); fields_list[i] = .{ - .field_type = gz.refToIndex(field_ty_inst).?, - .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init), + .field_name = str_index, + .init = try expr(gz, scope, .none, field_init), }; } - const init_inst = try gz.addPlNode(.struct_init, node, Zir.Inst.StructInit{ + const init_inst = try gz.addPlNode(.struct_init_anon, node, Zir.Inst.StructInitAnon{ .fields_len = @intCast(u32, fields_list.len), }); try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - fields_list.len * @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); + fields_list.len * @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len); for (fields_list) |field| { _ = gz.astgen.addExtraAssumeCapacity(field); } - return rvalue(gz, scope, rl, init_inst, node); + return init_inst; }, - .ptr => |ptr_inst| { - const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); - defer gpa.free(field_ptr_list); + .ref => unreachable, // struct literal not valid as l-value + .ty => |ty_inst| return structInitExprRlTy(gz, scope, rl, node, struct_init, ty_inst), + .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst), + .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr), + } +} - for (struct_init.ast.fields) |field_init, i| { - const name_token = tree.firstToken(field_init) - 2; - const str_index = try gz.identAsString(name_token); - const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{ - .lhs = ptr_inst, - .field_name_start = str_index, - }); - field_ptr_list[i] = gz.refToIndex(field_ptr).?; - _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init); - } - const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{ - .body_len = @intCast(u32, field_ptr_list.len), - }); - try astgen.extra.appendSlice(gpa, field_ptr_list); - return validate_inst; - }, - .inferred_ptr => |ptr_inst| { - return astgen.failNode(node, "TODO implement structInitExpr inferred_ptr", .{}); - }, - .block_ptr => |block_gz| { - return astgen.failNode(node, "TODO implement structInitExpr block", .{}); - }, +pub fn structInitExprRlPtr( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + struct_init: ast.full.StructInit, + result_ptr: Zir.Inst.Ref, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + const gpa = astgen.gpa; + const tree = &astgen.file.tree; + + const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); + defer gpa.free(field_ptr_list); + + for (struct_init.ast.fields) |field_init, i| { + const name_token = tree.firstToken(field_init) - 2; + const str_index = try gz.identAsString(name_token); + const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{ + .lhs = result_ptr, + .field_name_start = str_index, + }); + field_ptr_list[i] = gz.refToIndex(field_ptr).?; + _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init); + } + const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{ + .body_len = @intCast(u32, field_ptr_list.len), + }); + try astgen.extra.appendSlice(gpa, field_ptr_list); + return validate_inst; +} + +pub fn structInitExprRlTy( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + struct_init: ast.full.StructInit, + ty_inst: Zir.Inst.Ref, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + const gpa = astgen.gpa; + const tree = &astgen.file.tree; + + const fields_list = try gpa.alloc(Zir.Inst.StructInit.Item, struct_init.ast.fields.len); + defer gpa.free(fields_list); + + for (struct_init.ast.fields) |field_init, i| { + const name_token = tree.firstToken(field_init) - 2; + const str_index = try gz.identAsString(name_token); + + const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ + .container_type = ty_inst, + .name_start = str_index, + }); + fields_list[i] = .{ + .field_type = gz.refToIndex(field_ty_inst).?, + .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init), + }; } + const init_inst = try gz.addPlNode(.struct_init, node, Zir.Inst.StructInit{ + .fields_len = @intCast(u32, fields_list.len), + }); + try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + + fields_list.len * @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); + for (fields_list) |field| { + _ = gz.astgen.addExtraAssumeCapacity(field); + } + return init_inst; } pub fn comptimeExpr( @@ -1318,7 +1370,6 @@ fn blockExprStmts( .elem_val, .elem_ptr_node, .elem_val_node, - .floatcast, .field_ptr, .field_val, .field_ptr_named, @@ -1403,8 +1454,10 @@ fn blockExprStmts( .switch_capture_else_ref, .struct_init_empty, .struct_init, + .struct_init_anon, .union_init_ptr, .field_type, + .field_type_ref, .struct_decl, .struct_decl_packed, .struct_decl_extern, @@ -4706,21 +4759,61 @@ fn as( const result = try expr(gz, scope, .{ .ty = dest_type }, rhs); return rvalue(gz, scope, rl, result, node); }, - - .ptr => |result_ptr| { + .ptr, .inferred_ptr => |result_ptr| { return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type); }, .block_ptr => |block_scope| { return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type); }, + } +} - .inferred_ptr => |result_alloc| { - // TODO here we should be able to resolve the inference; we now have a type for the result. - return gz.astgen.failNode(node, "TODO implement @as with inferred-type result location pointer", .{}); +fn unionInit( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + params: []const ast.Node.Index, +) InnerError!Zir.Inst.Ref { + const union_type = try typeExpr(gz, scope, params[0]); + const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); + switch (rl) { + .none, .none_or_ref, .discard, .ref, .ty, .inferred_ptr => { + const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{ + .container_type = union_type, + .field_name = field_name, + }); + const result = try expr(gz, scope, .{ .ty = union_type }, params[2]); + return rvalue(gz, scope, rl, result, node); + }, + .ptr => |result_ptr| { + return unionInitRlPtr(gz, scope, rl, node, result_ptr, params[2], union_type, field_name); + }, + .block_ptr => |block_scope| { + return unionInitRlPtr(gz, scope, rl, node, block_scope.rl_ptr, params[2], union_type, field_name); }, } } +fn unionInitRlPtr( + parent_gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + result_ptr: Zir.Inst.Ref, + expr_node: ast.Node.Index, + union_type: Zir.Inst.Ref, + field_name: Zir.Inst.Ref, +) InnerError!Zir.Inst.Ref { + const union_init_ptr = try parent_gz.addPlNode(.union_init_ptr, node, Zir.Inst.UnionInitPtr{ + .result_ptr = result_ptr, + .union_type = union_type, + .field_name = field_name, + }); + // TODO check if we need to do the elision like below in asRlPtr + return expr(parent_gz, scope, .{ .ptr = union_init_ptr }, expr_node); +} + fn asRlPtr( parent_gz: *GenZir, scope: *Scope, @@ -4750,8 +4843,7 @@ fn asRlPtr( // Busted! This expression didn't actually need a pointer. const zir_tags = astgen.instructions.items(.tag); const zir_datas = astgen.instructions.items(.data); - const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2; - try parent_zir.ensureCapacity(astgen.gpa, expected_len); + try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); for (as_scope.instructions.items) |src_inst| { if (parent_gz.indexToRef(src_inst) == as_scope.rl_ptr) continue; if (zir_tags[src_inst] == .store_to_block_ptr) { @@ -4759,7 +4851,6 @@ fn asRlPtr( } parent_zir.appendAssumeCapacity(src_inst); } - assert(parent_zir.items.len == expected_len); const casted_result = try parent_gz.addBin(.as, dest_type, result); return rvalue(parent_gz, scope, rl, casted_result, operand_node); } else { @@ -5445,24 +5536,6 @@ fn cImport( return rvalue(gz, scope, rl, .void_value, node); } -fn unionInit( - gz: *GenZir, - scope: *Scope, - rl: ResultLoc, - node: ast.Node.Index, - params: []const ast.Node.Index, -) InnerError!Zir.Inst.Ref { - const union_type = try typeExpr(gz, scope, params[0]); - const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); - const union_init_ptr = try gz.addPlNode(.union_init_ptr, node, Zir.Inst.UnionInitPtr{ - .union_type = union_type, - .field_name = field_name, - }); - // TODO: set up a store_to_block_ptr elision thing here - const result = try expr(gz, scope, .{ .ptr = union_init_ptr }, params[2]); - return rvalue(gz, scope, rl, result, node); -} - fn overflowArithmetic( gz: *GenZir, scope: *Scope, diff --git a/src/Sema.zig b/src/Sema.zig index 38400b72e8..32a4a63ef9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -193,7 +193,6 @@ pub fn analyzeBody( .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), - .floatcast => try sema.zirFloatcast(block, inst), .func => try sema.zirFunc(block, inst, false), .func_extra => try sema.zirFuncExtra(block, inst, false), .func_extra_var_args => try sema.zirFuncExtra(block, inst, true), @@ -266,8 +265,10 @@ pub fn analyzeBody( .xor => try sema.zirBitwise(block, inst, .xor), .struct_init_empty => try sema.zirStructInitEmpty(block, inst), .struct_init => try sema.zirStructInit(block, inst), + .struct_init_anon => try sema.zirStructInitAnon(block, inst), .union_init_ptr => try sema.zirUnionInitPtr(block, inst), .field_type => try sema.zirFieldType(block, inst), + .field_type_ref => try sema.zirFieldTypeRef(block, inst), .error_return_trace => try sema.zirErrorReturnTrace(block, inst), .frame => try sema.zirFrame(block, inst), .frame_address => try sema.zirFrameAddress(block, inst), @@ -2904,7 +2905,7 @@ fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError return sema.bitcast(block, dest_type, operand); } -fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -4984,6 +4985,18 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInit", .{}); } +fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInitAnon", .{}); +} + +fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldTypeRef", .{}); +} + fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -5092,12 +5105,6 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToPtr", .{}); } -fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatCast", .{}); -} - fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); diff --git a/src/Zir.zig b/src/Zir.zig index 4348b36fa4..a454ed500f 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -367,11 +367,6 @@ pub const Inst = struct { /// The field name is a comptime instruction. Used by @field. /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. field_val_named, - /// Convert a larger float type to any other float type, possibly causing - /// a loss of precision. - /// Uses the `pl_node` field. AST is the `@floatCast` syntax. - /// Payload is `Bin` with lhs as the dest type, rhs the operand. - floatcast, /// Returns a function type, or a function instance, depending on whether /// the body_len is 0. Calling convention is auto. /// Uses the `pl_node` union field. `payload_index` points to a `Func`. @@ -686,13 +681,19 @@ pub const Inst = struct { /// A struct literal with a specified type, with no fields. /// Uses the `un_node` field. struct_init_empty, - /// Given a struct, union, enum, or opaque and a field name, returns the field type. - /// Uses the `pl_node` field. Payload is `FieldType`. + /// Given a struct, union, enum, or opaque and a field name as a string index, + /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`. field_type, + /// Given a struct, union, enum, or opaque and a field name as a Ref, + /// 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. /// Uses the `pl_node` field. Payload is `StructInit`. struct_init, + /// Struct initialization without a type. + /// Uses the `pl_node` field. Payload is `StructInitAnon`. + struct_init_anon, /// Given a pointer to a union and a comptime known field name, activates that field /// and returns a pointer to it. /// Uses the `pl_node` field. Payload is `UnionInitPtr`. @@ -813,8 +814,10 @@ pub const Inst = struct { /// Converts an integer into an enum value. /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. int_to_enum, - /// Implements the `@floatCast` builtin. - /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. + /// Convert a larger float type to any other float type, possibly causing + /// a loss of precision. + /// Uses the `pl_node` field. AST is the `@floatCast` syntax. + /// Payload is `Bin` with lhs as the dest type, rhs the operand. float_cast, /// Implements the `@intCast` builtin. /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. @@ -1002,7 +1005,6 @@ pub const Inst = struct { .ensure_result_used, .ensure_result_non_error, .@"export", - .floatcast, .field_ptr, .field_val, .field_ptr_named, @@ -1099,8 +1101,10 @@ pub const Inst = struct { .validate_struct_init_ptr, .struct_init_empty, .struct_init, + .struct_init_anon, .union_init_ptr, .field_type, + .field_type_ref, .int_to_enum, .enum_to_int, .type_info, @@ -2031,12 +2035,29 @@ pub const Inst = struct { }; }; + /// Trailing is an item per field. + pub const StructInitAnon = struct { + fields_len: u32, + + pub const Item = struct { + /// Null-terminated string table index. + field_name: u32, + /// The field init expression to be used as the field value. + init: Ref, + }; + }; + pub const FieldType = struct { container_type: Ref, /// Offset into `string_bytes`, null terminated. name_start: u32, }; + pub const FieldTypeRef = struct { + container_type: Ref, + field_name: Ref, + }; + pub const OverflowArithmetic = struct { lhs: Ref, rhs: Ref, @@ -2059,6 +2080,7 @@ pub const Inst = struct { }; pub const UnionInitPtr = struct { + result_ptr: Ref, union_type: Ref, field_name: Ref, }; @@ -2279,14 +2301,15 @@ const Writer = struct { .elem_val_node, .field_ptr_named, .field_val_named, - .floatcast, .slice_start, .slice_end, .slice_sentinel, .union_decl, .struct_init, + .struct_init_anon, .union_init_ptr, .field_type, + .field_type_ref, .cmpxchg_strong, .cmpxchg_weak, .shuffle, -- cgit v1.2.3