diff --git a/src/AstGen.zig b/src/AstGen.zig index 2c01779705..a6b2cc57eb 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1902,9 +1902,6 @@ fn varDecl( ) InnerError!*Scope { try emitDbgNode(gz, node); const astgen = gz.astgen; - if (var_decl.ast.align_node != 0) { - return astgen.failNode(var_decl.ast.align_node, "TODO implement alignment on locals", .{}); - } const gpa = astgen.gpa; const tree = &astgen.file.tree; const token_tags = tree.tokens.items(.tag); @@ -1919,12 +1916,12 @@ fn varDecl( .local_val => { const local_val = s.cast(Scope.LocalVal).?; if (mem.eql(u8, local_val.name, ident_name)) { - return astgen.failTokNotes(name_token, "redefinition of '{s}'", .{ + return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ ident_name, }, &[_]u32{ try astgen.errNoteTok( local_val.token_src, - "previous definition is here", + "previous declaration is here", .{}, ), }); @@ -1934,12 +1931,12 @@ fn varDecl( .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (mem.eql(u8, local_ptr.name, ident_name)) { - return astgen.failTokNotes(name_token, "redefinition of '{s}'", .{ + return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ ident_name, }, &[_]u32{ try astgen.errNoteTok( local_ptr.token_src, - "previous definition is here", + "previous declaration is here", .{}, ), }); @@ -1957,15 +1954,21 @@ fn varDecl( return astgen.failNode(node, "variables must be initialized", .{}); } + const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0) + try expr(gz, scope, align_rl, var_decl.ast.align_node) + else + .none; + switch (token_tags[var_decl.ast.mut_token]) { .keyword_const => { if (var_decl.comptime_token) |comptime_token| { return astgen.failTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); } + // Depending on the type of AST the initialization expression is, we may need an lvalue // or an rvalue as a result location. If it is an rvalue, we can use the instruction as // the variable, no memory location needed. - if (!nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) { + if (align_inst == .none and !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) { const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{ .ty = try typeExpr(gz, scope, var_decl.ast.type_node), } else .none; @@ -1997,10 +2000,29 @@ fn varDecl( if (var_decl.ast.type_node != 0) { const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node); opt_type_inst = type_inst; - init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); + if (align_inst == .none) { + init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); + } else { + init_scope.rl_ptr = try gz.addAllocExtended(.{ + .node = node, + .type_inst = type_inst, + .align_inst = align_inst, + .is_const = true, + .is_comptime = false, + }); + } init_scope.rl_ty_inst = type_inst; } else { - const alloc = try init_scope.addNode(.alloc_inferred, node); + const alloc = if (align_inst == .none) + try init_scope.addNode(.alloc_inferred, node) + else + try gz.addAllocExtended(.{ + .node = node, + .type_inst = .none, + .align_inst = align_inst, + .is_const = true, + .is_comptime = false, + }); resolve_inferred_alloc = alloc; init_scope.rl_ptr = alloc; } @@ -2010,7 +2032,7 @@ fn varDecl( const zir_datas = astgen.instructions.items(.data); const parent_zir = &gz.instructions; - if (init_scope.rvalue_rl_count == 1) { + if (align_inst == .none and init_scope.rvalue_rl_count == 1) { // Result location pointer not used. We don't need an alloc for this // const local, and type inference becomes trivial. // Move the init_scope instructions into the parent scope, eliding @@ -2070,12 +2092,36 @@ fn varDecl( alloc: Zir.Inst.Ref, } = if (var_decl.ast.type_node != 0) a: { const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); - const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut; - const alloc = try gz.addUnNode(tag, type_inst, node); + const alloc = alloc: { + if (align_inst == .none) { + const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut; + break :alloc try gz.addUnNode(tag, type_inst, node); + } else { + break :alloc try gz.addAllocExtended(.{ + .node = node, + .type_inst = type_inst, + .align_inst = align_inst, + .is_const = false, + .is_comptime = is_comptime, + }); + } + }; break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; } else a: { - const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut; - const alloc = try gz.addNode(tag, node); + const alloc = alloc: { + if (align_inst == .none) { + const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut; + break :alloc try gz.addNode(tag, node); + } else { + break :alloc try gz.addAllocExtended(.{ + .node = node, + .type_inst = .none, + .align_inst = align_inst, + .is_const = false, + .is_comptime = is_comptime, + }); + } + }; resolve_inferred_alloc = alloc; break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; }; diff --git a/src/Module.zig b/src/Module.zig index f9f135c721..d456968caf 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1630,6 +1630,57 @@ pub const Scope = struct { }); } + pub fn addAllocExtended( + gz: *GenZir, + args: struct { + /// Absolute node index. This function does the conversion to offset from Decl. + node: ast.Node.Index, + type_inst: Zir.Inst.Ref, + align_inst: Zir.Inst.Ref, + is_const: bool, + is_comptime: bool, + }, + ) !Zir.Inst.Ref { + const astgen = gz.astgen; + const gpa = astgen.gpa; + + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try astgen.instructions.ensureUnusedCapacity(gpa, 1); + try astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len + + @as(usize, @boolToInt(args.type_inst != .none)) + + @as(usize, @boolToInt(args.align_inst != .none)), + ); + const payload_index = gz.astgen.addExtra(Zir.Inst.AllocExtended{ + .src_node = gz.nodeIndexToRelative(args.node), + }) catch unreachable; // ensureUnusedCapacity above + if (args.type_inst != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.type_inst)); + } + if (args.align_inst != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst)); + } + + const has_type: u4 = @boolToInt(args.type_inst != .none); + const has_align: u4 = @boolToInt(args.align_inst != .none); + const is_const: u4 = @boolToInt(args.is_const); + const is_comptime: u4 = @boolToInt(args.is_comptime); + const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3); + + const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); + astgen.instructions.appendAssumeCapacity(.{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .alloc, + .small = small, + .operand = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + return gz.indexToRef(new_index); + } + /// Asserts that `str` is 8 or fewer bytes. pub fn addSmallStr( gz: *GenZir, diff --git a/src/Sema.zig b/src/Sema.zig index 5a40409c86..00ec5222ec 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -516,6 +516,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro .error_return_trace => return sema.zirErrorReturnTrace(block, extended), .frame => return sema.zirFrame( block, extended), .frame_address => return sema.zirFrameAddress( block, extended), + .alloc => return sema.zirAllocExtended( block, extended), .c_undef => return sema.zirCUndef( block, extended), .c_include => return sema.zirCInclude( block, extended), .c_define => return sema.zirCDefine( block, extended), @@ -1131,6 +1132,16 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In return sema.analyzeLoad(block, src, result_ptr, result_ptr.src); } +fn zirAllocExtended( + sema: *Sema, + block: *Scope.Block, + extended: Zir.Inst.Extended.InstData, +) InnerError!*Inst { + const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); + const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended", .{}); +} + fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); diff --git a/src/Zir.zig b/src/Zir.zig index 4cf7f75373..3da2c6aff8 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -926,7 +926,6 @@ pub const Inst = struct { /// Allocates stack local memory. /// Uses the `un_node` union field. The operand is the type of the allocated object. /// The node source location points to a var decl node. - /// Indicates the beginning of a new statement in debug info. alloc, /// Same as `alloc` except mutable. alloc_mut, @@ -1251,6 +1250,14 @@ pub const Inst = struct { /// Implements the `@frameAddress` builtin. /// `operand` is `src_node: i32`. frame_address, + /// Same as `alloc` from `Tag` but may contain an alignment instruction. + /// `operand` is payload index to `AllocExtended`. + /// `small`: + /// * 0b000X - has type + /// * 0b00X0 - has alignment + /// * 0b0X00 - 1=const, 0=var + /// * 0bX000 - is comptime + alloc, /// `operand` is payload index to `UnNode`. c_undef, /// `operand` is payload index to `UnNode`. @@ -2200,6 +2207,13 @@ pub const Inst = struct { args: Ref, }; + /// Trailing: + /// 0. type_inst: Ref, // if small 0b000X is set + /// 1. align_inst: Ref, // if small 0b00X0 is set + pub const AllocExtended = struct { + src_node: i32, + }; + /// Trailing: `CompileErrors.Item` for each `items_len`. pub const CompileErrors = struct { items_len: u32, @@ -2571,6 +2585,7 @@ const Writer = struct { => try self.writeExtNode(stream, extended), .func, + .alloc, .c_undef, .c_include, .c_define,