Zig 0.17.0-dev (Split by item)

Shortcut keys
?
Show these tips
S
Focus search field
Esc
Clear focus & close tips
U
Jump to source code
↑
Move up in search results
↓
Move down in search results
↵
Go to active search result
This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig standard library. Private Function

containerDecl

Private Function
std.zig.AstGen.containerDecl
fn containerDecl(
    gz: *GenZir,
    scope: *Scope,
    ri: ResultInfo,
    node: Ast.Node.Index,
    container_decl: Ast.full.ContainerDecl,
    name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref

Params

Errors

OutOfMemory
AnalysisFail

File

zig/AstGen.zig:5325
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
fn containerDecl(
    gz: *GenZir,
    scope: *Scope,
    ri: ResultInfo,
    node: Ast.Node.Index,
    container_decl: Ast.full.ContainerDecl,
    name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref {
    const astgen = gz.astgen;
    const gpa = astgen.gpa;
    const tree = astgen.tree;

    const prev_fn_block = astgen.fn_block;
    astgen.fn_block = null;
    defer astgen.fn_block = prev_fn_block;

    // We must not create any types until Sema. Here the goal is only to generate
    // ZIR for all the field types, alignments, and default value expressions.

    switch (tree.tokenTag(container_decl.ast.main_token)) {
        .keyword_struct => {
            const layout: std.lang.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
                .keyword_packed => .@"packed",
                .keyword_extern => .@"extern",
                else => unreachable,
            } else .auto;

            const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg, name_strat);
            return rvalue(gz, ri, result, node);
        },
        .keyword_union => {
            const layout: std.lang.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
                .keyword_packed => .@"packed",
                .keyword_extern => .@"extern",
                else => unreachable,
            } else .auto;

            const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token, name_strat);
            return rvalue(gz, ri, result, node);
        },
        .keyword_enum => {
            if (container_decl.layout_token) |t| {
                return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
            }

            astgen.advanceSourceCursorToNode(node);

            const decl_inst = try gz.reserveInstructionIndex();
            const src_line = astgen.source_line;
            const src_column = astgen.source_column;

            var namespace: Scope.Namespace = .{
                .parent = scope,
                .node = node,
                .inst = decl_inst,
                .declaring_gz = gz,
                .maybe_generic = astgen.within_fn,
            };
            defer namespace.deinit(gpa);

            // The enum_decl instruction introduces a scope in which the decls of the enum
            // are in scope, so that tag values can refer to decls within the enum itself.
            var block_scope: GenZir = .{
                .parent = &namespace.base,
                .decl_node_index = node,
                .decl_line = gz.decl_line,
                .astgen = astgen,
                .is_comptime = true,
                .instructions = gz.instructions,
                .instructions_top = gz.instructions.items.len,
            };
            defer block_scope.unstack();

            const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
            // The name `_` is not actually a field; it marks a non-exhaustive enum.
            const fields_len: u32 = scan_result.fields_len - @intFromBool(scan_result.has_underscore_field);

            var scratch: Scratch = .init(astgen);
            defer scratch.reset();

            // Replicate the structure of the ZIR trailing data in `scratch`
            var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len);
            const field_names = try scratch.addSlice(fields_len);
            const field_value_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, fields_len);

            const old_hasher = astgen.src_hasher;
            defer astgen.src_hasher = old_hasher;
            astgen.src_hasher = .init(.{});

            // Before any field bodies comes the tag type, if specified.
            const tag_type_body_len: ?u32 = if (container_decl.ast.arg.unwrap()) |tag_type_node| len: {
                astgen.src_hasher.update(astgen.tree.getNodeSource(tag_type_node));
                const type_ref = try typeExpr(&block_scope, &namespace.base, tag_type_node);
                if (!block_scope.endsWithNoReturn()) {
                    _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref);
                }
                const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
                block_scope.instructions.items.len = block_scope.instructions_top;
                break :len body_len;
            } else null;

            var next_field_idx: u32 = 0;
            var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none;
            for (container_decl.ast.members) |member_node| {
                var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) {
                    .decl => continue,
                    .field => |field| field,
                };
                member.convertToNonTupleLike(astgen.tree);
                if (member.ast.tuple_like) return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
                if (member.comptime_token) |t| return astgen.failTok(t, "enum fields cannot be marked comptime", .{});
                if (member.ast.type_expr.unwrap()) |type_node| {
                    return astgen.failNodeNotes(type_node, "enum fields do not have types", .{}, &.{
                        try astgen.errNoteNode(node, "consider 'union(enum)' here to make it a tagged union", .{}),
                    });
                }
                if (member.ast.align_expr.unwrap()) |n| return astgen.failNode(n, "enum fields cannot be aligned", .{});
                if (mem.eql(u8, tree.tokenSlice(member.ast.main_token), "_")) {
                    // non-exhaustive mark
                    assert(scan_result.has_underscore_field);
                    if (opt_nonexhaustive_node.unwrap()) |prev_node| {
                        return astgen.failNodeNotes(member_node, "redundant non-exhaustive enum mark", .{}, &.{
                            try astgen.errNoteNode(prev_node, "other mark here", .{}),
                        });
                    }
                    if (member.ast.value_expr.unwrap()) |value_node| {
                        return astgen.failNode(value_node, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
                    }
                    if (next_field_idx != fields_len) {
                        return astgen.failNode(member_node, "'_' field of non-exhaustive enum must be last", .{});
                    }
                    if (tag_type_body_len == null) {
                        return astgen.failNodeNotes(node, "non-exhaustive enum missing integer tag type", .{}, &.{
                            try astgen.errNoteNode(member_node, "marked non-exhaustive here", .{}),
                        });
                    }
                    opt_nonexhaustive_node = member_node.toOptional();
                    continue;
                }

                // This is a real field rather than a non-exhaustive mark.
                const field_idx = next_field_idx;
                next_field_idx += 1;

                astgen.src_hasher.update(tree.getNodeSource(member_node));

                field_names.get(astgen)[field_idx] = @backingInt(try astgen.identAsString(member.ast.main_token));

                if (member.ast.value_expr.unwrap()) |value_node| {
                    if (tag_type_body_len == null) {
                        return astgen.failNodeNotes(node, "explicitly valued enum missing integer tag type", .{}, &.{
                            try astgen.errNoteNode(value_node, "tag value specified here", .{}),
                        });
                    }
                    const val_ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } };
                    const value_ref = try expr(&block_scope, &namespace.base, val_ri, value_node);
                    if (!block_scope.endsWithNoReturn()) {
                        _ = try block_scope.addBreak(.break_inline, decl_inst, value_ref);
                    }
                    const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
                    field_value_body_lens.?.get(astgen)[field_idx] = body_len;
                    block_scope.instructions.items.len = block_scope.instructions_top;
                } else if (field_value_body_lens) |lens| {
                    lens.get(astgen)[field_idx] = 0;
                }
            }
            assert(scan_result.has_underscore_field == (opt_nonexhaustive_node != .none));
            assert(next_field_idx == fields_len);
            wip_decls.finish();

            var fields_hash: std.zig.SrcHash = undefined;
            astgen.src_hasher.final(&fields_hash);

            try gz.setEnum(decl_inst, .{
                .src_line = src_line,
                .src_column = src_column,
                .src_node = node,
                .name_strat = name_strat,
                .tag_type_body_len = tag_type_body_len,
                .nonexhaustive = scan_result.has_underscore_field,
                .decls_len = scan_result.decls_len,
                .fields_len = fields_len,
                .any_field_values = scan_result.any_field_values,
                .fields_hash = fields_hash,
                .captures = namespace.captures.keys(),
                .capture_names = namespace.captures.values(),
                .remaining = scratch.all().get(astgen),
            });

            block_scope.unstack();
            return rvalue(gz, ri, decl_inst.toRef(), node);
        },
        .keyword_opaque => {
            assert(container_decl.ast.arg == .none);

            astgen.advanceSourceCursorToNode(node);

            const decl_inst = try gz.reserveInstructionIndex();
            const src_line = astgen.source_line;
            const src_column = astgen.source_column;

            var namespace: Scope.Namespace = .{
                .parent = scope,
                .node = node,
                .inst = decl_inst,
                .declaring_gz = gz,
                .maybe_generic = astgen.within_fn,
            };
            defer namespace.deinit(gpa);

            var block_scope: GenZir = .{
                .parent = &namespace.base,
                .decl_node_index = node,
                .decl_line = gz.decl_line,
                .astgen = astgen,
                .is_comptime = true,
                .instructions = gz.instructions,
                .instructions_top = gz.instructions.items.len,
            };
            defer block_scope.unstack();

            const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");

            var scratch: Scratch = .init(astgen);
            defer scratch.reset();
            var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len);

            if (container_decl.layout_token) |layout_token| {
                return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{});
            }

            for (container_decl.ast.members) |member_node| {
                switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) {
                    .decl => {},
                    .field => return astgen.failNode(member_node, "opaque types cannot have fields", .{}),
                }
            }

            wip_decls.finish();

            try gz.setOpaque(decl_inst, .{
                .src_line = src_line,
                .src_column = src_column,
                .src_node = node,
                .name_strat = name_strat,
                .decls_len = scan_result.decls_len,
                .captures = namespace.captures.keys(),
                .capture_names = namespace.captures.values(),
                .decls = @ptrCast(scratch.all().get(astgen)),
            });

            block_scope.unstack();
            return rvalue(gz, ri, decl_inst.toRef(), node);
        },
        else => unreachable,
    }
}