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 Struct

GenZir

Private Struct
std.zig.AstGen.GenZir
const GenZir = struct

This is a temporary structure; references to it are valid only while constructing a Zir.

Fields

base: Scope = Scope{ .tag = base_tag }
is_comptime: bool

Whether we're already in a scope known to be comptime. This is set whenever we know Sema will analyze the current block with is_comptime, for instance when we're within a struct_decl or a block_comptime.

is_typeof: bool = false

Whether we're in an expression within a @TypeOf operand. In this case, closure of runtime variables is permitted where it is usually not.

is_inline: bool = false

This is set to true for a GenZir of a block_inline, indicating that exits from this block should use break_inline rather than break.

decl_node_index: Ast.Node.Index

The containing decl AST node.

decl_line: u32

The containing decl line index, absolute.

parent: *Scope

Parents can be: LocalVal, LocalPtr, GenZir, Defer, Namespace.

astgen: *AstGen

All GenZir scopes for the same ZIR share this.

instructions: *ArrayList(Zir.Inst.Index)

Keeps track of the list of instructions in this scope. Possibly shared. Indexes to instructions in astgen.

instructions_top: usize

A sub-block may share its instructions ArrayList with containing GenZir, if use is strictly nested. This saves prior size of list for unstacking.

label: ?Label = null
allow_unlabeled_control_flow: bool = false

If true, unlabeled break and continue exprs can target this GenZir.

break_target: Zir.Inst.Index = undefined

If label is null and unlabeled_control_flow_target is false, this is unused and may be undefined. Otherwise, this is the target for a break instruction when a break targets this GenZir.

continue_target: union(enum) { /// A `continue` cannot target this `GenZir`; emit an error. none, /// Emit a `break` instruction targeting this block. @"break": Zir.Inst.Index, /// Emit a `switch_continue` instruction targeting this `switch_block`. switch_continue: Zir.Inst.Index, } = undefined

If label is null and unlabeled_control_flow_target is false, this is unused and may be undefined.

break_result_info: AstGen.ResultInfo = undefined

Only valid when setBreakResultInfo is called.

continue_result_info: AstGen.ResultInfo = undefined

If continue_target is not switch_continue, this is unused and may be undefined.

suspend_node: Ast.Node.OptionalIndex = .none
nosuspend_node: Ast.Node.OptionalIndex = .none
cur_defer_node: Ast.Node.OptionalIndex = .none

Set if this GenZir is a defer.

any_defer_node: Ast.Node.OptionalIndex = .none

Set if this GenZir is a defer or it is inside a defer.

Structs

Types

Values

Functions

fn unstack(self: *GenZir) void

Call unstack before adding any new instructions to containing GenZir.

fn isEmpty(self: *const GenZir) bool
fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index
fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index
fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index
fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir
fn endsWithNoReturn(gz: GenZir) bool

Assumes nothing stacked on gz.

fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool

TODO all uses of this should be replaced with uses of endsWithNoReturn.

fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset
fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset
fn srcToken(gz: GenZir) Ast.TokenIndex
fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void
fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void

Assumes nothing stacked on gz. Unstacks gz.

fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void

Assumes nothing stacked on gz. Unstacks gz. Asserts inst is not a block_comptime.

fn setBlockComptimeBody(gz: *GenZir, inst: Zir.Inst.Index, comptime_reason: std.zig.SimpleComptimeReason) !void

Assumes nothing stacked on gz. Unstacks gz. Asserts inst is a block_comptime.

fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void

Assumes nothing stacked on gz. Unstacks gz.

fn addFunc(gz: *GenZir, args: struct

Must be called with the following stack set up:

  • gz (bottom)
  • ret_gz
  • cc_gz
  • body_gz (top) Unstacks all of those except for gz.
fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32
fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref
fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref
fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref
fn addUnNode(gz: *GenZir, tag: Zir.Inst.Tag, operand: Zir.Inst.Ref, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index) !Zir.Inst.Ref
fn makeUnNode(gz: *GenZir, tag: Zir.Inst.Tag, operand: Zir.Inst.Ref, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index) !Zir.Inst.Index
fn addPlNode(gz: *GenZir, tag: Zir.Inst.Tag, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index, extra: anytype) !Zir.Inst.Ref
fn addPlNodePayloadIndex(gz: *GenZir, tag: Zir.Inst.Tag, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index, payload_index: u32) !Zir.Inst.Ref
fn addParam(gz: *GenZir, param_gz: *GenZir, /// Previous parameters, which might be referenced in `param_gz` (the new parameter type). /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`. prev_param_insts: []const Zir.Inst.Index, ty_is_generic: bool, tag: Zir.Inst.Tag, /// Absolute token index. This function does the conversion to Decl offset. abs_tok_index: Ast.TokenIndex, name: Zir.NullTerminatedString) !Zir.Inst.Index

Supports param_gz stacked on gz. Assumes nothing stacked on param_gz. Unstacks param_gz.

fn addStdLangValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.StdLangValue) !Zir.Inst.Ref
fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref
fn addExtendedPayloadSmall(gz: *GenZir, opcode: Zir.Inst.Extended, small: u16, extra: anytype) !Zir.Inst.Ref
fn addExtendedMultiOp(gz: *GenZir, opcode: Zir.Inst.Extended, node: Ast.Node.Index, operands: []const Zir.Inst.Ref) !Zir.Inst.Ref
fn addExtendedMultiOpPayloadIndex(gz: *GenZir, opcode: Zir.Inst.Extended, payload_index: u32, trailing_len: usize) !Zir.Inst.Ref
fn addExtendedNodeSmall(gz: *GenZir, opcode: Zir.Inst.Extended, src_node: Ast.Node.Index, small: u16) !Zir.Inst.Ref
fn addUnTok(gz: *GenZir, tag: Zir.Inst.Tag, operand: Zir.Inst.Ref, /// Absolute token index. This function does the conversion to Decl offset. abs_tok_index: Ast.TokenIndex) !Zir.Inst.Ref
fn makeUnTok(gz: *GenZir, tag: Zir.Inst.Tag, operand: Zir.Inst.Ref, /// Absolute token index. This function does the conversion to Decl offset. abs_tok_index: Ast.TokenIndex) !Zir.Inst.Index
fn addStrTok(gz: *GenZir, tag: Zir.Inst.Tag, str_index: Zir.NullTerminatedString, /// Absolute token index. This function does the conversion to Decl offset. abs_tok_index: Ast.TokenIndex) !Zir.Inst.Ref
fn addSaveErrRetIndex(gz: *GenZir, cond: union(enum)
fn addRestoreErrRetIndex(gz: *GenZir, bt: BranchTarget, cond: union(enum)
fn addBreak(gz: *GenZir, tag: Zir.Inst.Tag, block_inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !Zir.Inst.Index
fn makeBreak(gz: *GenZir, tag: Zir.Inst.Tag, block_inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !Zir.Inst.Index
fn addBreakWithSrcNode(gz: *GenZir, tag: Zir.Inst.Tag, block_inst: Zir.Inst.Index, operand: Zir.Inst.Ref, operand_src_node: Ast.Node.Index) !Zir.Inst.Index
fn makeBreakWithSrcNode(gz: *GenZir, tag: Zir.Inst.Tag, block_inst: Zir.Inst.Index, operand: Zir.Inst.Ref, operand_src_node: Ast.Node.Index) !Zir.Inst.Index
fn makeBreakCommon(gz: *GenZir, tag: Zir.Inst.Tag, block_inst: Zir.Inst.Index, operand: Zir.Inst.Ref, operand_src_node: ?Ast.Node.Index) !Zir.Inst.Index
fn addBin(gz: *GenZir, tag: Zir.Inst.Tag, lhs: Zir.Inst.Ref, rhs: Zir.Inst.Ref) !Zir.Inst.Ref
fn addDefer(gz: *GenZir, index: u32, len: u32) !void
fn addDecl(gz: *GenZir, tag: Zir.Inst.Tag, decl_index: u32, src_node: Ast.Node.Index) !Zir.Inst.Ref
fn addNode(gz: *GenZir, tag: Zir.Inst.Tag, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index) !Zir.Inst.Ref
fn addInstNode(gz: *GenZir, tag: Zir.Inst.Tag, inst: Zir.Inst.Index, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index) !Zir.Inst.Ref
fn addNodeExtended(gz: *GenZir, opcode: Zir.Inst.Extended, /// Absolute node index. This function does the conversion to offset from Decl. src_node: Ast.Node.Index) !Zir.Inst.Ref
fn addAllocExtended(gz: *GenZir, args: struct
fn addAsm(gz: *GenZir, args: struct
fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index

Note that this returns a Zir.Inst.Index not a ref. Does not append the block instruction to the scope. Leaves the payload_index field undefined.

fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index

Note that this returns a Zir.Inst.Index not a ref. Does not append the block instruction to the scope. Leaves the payload_index field undefined. Use setDeclaration to finalize.

fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index

Note that this returns a Zir.Inst.Index not a ref. Leaves the payload_index field undefined.

fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct
fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct
fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct
fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct
fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref
fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index
fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index
fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void
fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void

File

zig/AstGen.zig:11241
11241
11242
11243
11244
11245
11246
11247
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
11409
11410
11411
11412
11413
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
11447
11448
11449
11450
11451
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
11474
11475
11476
11477
11478
11479
11480
11481
11482
11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11536
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
11587
11588
11589
11590
11591
11592
11593
11594
11595
11596
11597
11598
11599
11600
11601
11602
11603
11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
11735
11736
11737
11738
11739
11740
11741
11742
11743
11744
11745
11746
11747
11748
11749
11750
11751
11752
11753
11754
11755
11756
11757
11758
11759
11760
11761
11762
11763
11764
11765
11766
11767
11768
11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
11887
11888
11889
11890
11891
11892
11893
11894
11895
11896
11897
11898
11899
11900
11901
11902
11903
11904
11905
11906
11907
11908
11909
11910
11911
11912
11913
11914
11915
11916
11917
11918
11919
11920
11921
11922
11923
11924
11925
11926
11927
11928
11929
11930
11931
11932
11933
11934
11935
11936
11937
11938
11939
11940
11941
11942
11943
11944
11945
11946
11947
11948
11949
11950
11951
11952
11953
11954
11955
11956
11957
11958
11959
11960
11961
11962
11963
11964
11965
11966
11967
11968
11969
11970
11971
11972
11973
11974
11975
11976
11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
12001
12002
12003
12004
12005
12006
12007
12008
12009
12010
12011
12012
12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040
12041
12042
12043
12044
12045
12046
12047
12048
12049
12050
12051
12052
12053
12054
12055
12056
12057
12058
12059
12060
12061
12062
12063
12064
12065
12066
12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091
12092
12093
12094
12095
12096
12097
12098
12099
12100
12101
12102
12103
12104
12105
12106
12107
12108
12109
12110
12111
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127
12128
12129
12130
12131
12132
12133
12134
12135
12136
12137
12138
12139
12140
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
12162
12163
12164
12165
12166
12167
12168
12169
12170
12171
12172
12173
12174
12175
12176
12177
12178
12179
12180
12181
12182
12183
12184
12185
12186
12187
12188
12189
12190
12191
12192
12193
12194
12195
12196
12197
12198
12199
12200
12201
12202
12203
12204
12205
12206
12207
12208
12209
12210
12211
12212
12213
12214
12215
12216
12217
12218
12219
12220
12221
12222
12223
12224
12225
12226
12227
12228
12229
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
12261
12262
12263
12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377
12378
12379
12380
12381
12382
12383
12384
12385
12386
12387
12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399
12400
12401
12402
12403
12404
12405
12406
12407
12408
12409
12410
12411
12412
12413
12414
12415
12416
12417
12418
12419
12420
12421
12422
12423
12424
12425
12426
12427
12428
12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
12439
12440
12441
12442
12443
12444
12445
12446
12447
12448
12449
12450
12451
12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
12480
12481
12482
12483
12484
12485
12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526
12527
12528
12529
12530
12531
12532
12533
12534
12535
12536
12537
12538
12539
12540
12541
12542
12543
12544
12545
12546
12547
12548
12549
12550
12551
12552
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
12604
12605
12606
12607
12608
12609
12610
12611
12612
12613
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
12629
12630
12631
12632
12633
12634
12635
12636
12637
12638
12639
12640
12641
12642
12643
12644
12645
12646
12647
12648
12649
12650
12651
12652
12653
12654
12655
12656
12657
12658
12659
12660
12661
12662
12663
12664
12665
12666
12667
12668
12669
12670
12671
12672
12673
12674
12675
12676
12677
12678
12679
12680
12681
12682
12683
12684
12685
12686
12687
12688
12689
12690
12691
12692
12693
12694
12695
12696
12697
12698
12699
12700
12701
12702
12703
12704
12705
12706
12707
12708
12709
12710
12711
12712
12713
12714
12715
12716
12717
12718
12719
12720
12721
const GenZir = struct {
    const base_tag: Scope.Tag = .gen_zir;
    base: Scope = Scope{ .tag = base_tag },
    /// Whether we're already in a scope known to be comptime. This is set
    /// whenever we know Sema will analyze the current block with `is_comptime`,
    /// for instance when we're within a `struct_decl` or a `block_comptime`.
    is_comptime: bool,
    /// Whether we're in an expression within a `@TypeOf` operand. In this case,
    /// closure of runtime variables is permitted where it is usually not.
    is_typeof: bool = false,
    /// This is set to true for a `GenZir` of a `block_inline`, indicating that
    /// exits from this block should use `break_inline` rather than `break`.
    is_inline: bool = false,
    /// The containing decl AST node.
    decl_node_index: Ast.Node.Index,
    /// The containing decl line index, absolute.
    decl_line: u32,
    /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
    parent: *Scope,
    /// All `GenZir` scopes for the same ZIR share this.
    astgen: *AstGen,
    /// Keeps track of the list of instructions in this scope. Possibly shared.
    /// Indexes to instructions in `astgen`.
    instructions: *ArrayList(Zir.Inst.Index),
    /// A sub-block may share its instructions ArrayList with containing GenZir,
    /// if use is strictly nested. This saves prior size of list for unstacking.
    instructions_top: usize,
    label: ?Label = null,
    /// If `true`, unlabeled `break` and `continue` exprs can target this `GenZir`.
    allow_unlabeled_control_flow: bool = false,
    /// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
    /// this is unused and may be `undefined`.
    /// Otherwise, this is the target for a `break` instruction when a `break`
    /// targets this `GenZir`.
    break_target: Zir.Inst.Index = undefined,
    /// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
    /// this is unused and may be `undefined`.
    continue_target: union(enum) {
        /// A `continue` cannot target this `GenZir`; emit an error.
        none,
        /// Emit a `break` instruction targeting this block.
        @"break": Zir.Inst.Index,
        /// Emit a `switch_continue` instruction targeting this `switch_block`.
        switch_continue: Zir.Inst.Index,
    } = undefined,
    /// Only valid when setBreakResultInfo is called.
    break_result_info: AstGen.ResultInfo = undefined,
    /// If `continue_target` is *not* `switch_continue`, this is unused and may
    /// be `undefined`.
    continue_result_info: AstGen.ResultInfo = undefined,

    suspend_node: Ast.Node.OptionalIndex = .none,
    nosuspend_node: Ast.Node.OptionalIndex = .none,
    /// Set if this GenZir is a defer.
    cur_defer_node: Ast.Node.OptionalIndex = .none,
    // Set if this GenZir is a defer or it is inside a defer.
    any_defer_node: Ast.Node.OptionalIndex = .none,

    const unstacked_top = std.math.maxInt(usize);
    /// Call unstack before adding any new instructions to containing GenZir.
    fn unstack(self: *GenZir) void {
        if (self.instructions_top != unstacked_top) {
            self.instructions.items.len = self.instructions_top;
            self.instructions_top = unstacked_top;
        }
    }

    fn isEmpty(self: *const GenZir) bool {
        return (self.instructions_top == unstacked_top) or
            (self.instructions.items.len == self.instructions_top);
    }

    fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
        return if (self.instructions_top == unstacked_top)
            &[0]Zir.Inst.Index{}
        else
            self.instructions.items[self.instructions_top..];
    }

    fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
        return if (self.instructions_top == unstacked_top)
            &[0]Zir.Inst.Index{}
        else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
            self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
        else
            self.instructions.items[self.instructions_top..];
    }

    fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index {
        if (maybe_stacked_gz) |stacked_gz| {
            return gz.instructionsSliceUpto(stacked_gz);
        } else {
            return gz.instructionsSlice();
        }
    }

    fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
        return .{
            .is_comptime = gz.is_comptime,
            .is_typeof = gz.is_typeof,
            .decl_node_index = gz.decl_node_index,
            .decl_line = gz.decl_line,
            .parent = scope,
            .astgen = gz.astgen,
            .suspend_node = gz.suspend_node,
            .nosuspend_node = gz.nosuspend_node,
            .any_defer_node = gz.any_defer_node,
            .instructions = gz.instructions,
            .instructions_top = gz.instructions.items.len,
        };
    }

    const Label = struct {
        token: Ast.TokenIndex,
        used: bool = false,
        used_for_continue: bool = false,
    };

    /// Assumes nothing stacked on `gz`.
    fn endsWithNoReturn(gz: GenZir) bool {
        if (gz.isEmpty()) return false;
        const tags = gz.astgen.instructions.items(.tag);
        const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
        return tags[@backingInt(last_inst)].isNoReturn();
    }

    /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
    fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
        if (inst_ref == .unreachable_value) return true;
        if (inst_ref.toIndex()) |inst_index| {
            return gz.astgen.instructions.items(.tag)[@backingInt(inst_index)].isNoReturn();
        }
        return false;
    }

    fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset {
        return gz.decl_node_index.toOffset(node_index);
    }

    fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset {
        return .init(gz.srcToken(), token);
    }

    fn srcToken(gz: GenZir) Ast.TokenIndex {
        return gz.astgen.tree.firstToken(gz.decl_node_index);
    }

    fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
        // Depending on whether the result location is a pointer or value, different
        // ZIR needs to be generated. In the former case we rely on storing to the
        // pointer to communicate the result, and use breakvoid; in the latter case
        // the block break instructions will have the result values.
        switch (parent_ri.rl) {
            .coerced_ty => |ty_inst| {
                // Type coercion needs to happen before breaks.
                gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
            },
            .discard => {
                // We don't forward the result context here. This prevents
                // "unnecessary discard" errors from being caused by expressions
                // far from the actual discard, such as a `break` from a
                // discarded block.
                gz.break_result_info = .{ .rl = .discard };
            },
            else => {
                gz.break_result_info = parent_ri;
            },
        }
    }

    /// Assumes nothing stacked on `gz`. Unstacks `gz`.
    fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;
        const body = gz.instructionsSlice();
        const body_len = astgen.countBodyLenAfterFixups(body);
        try astgen.extra.ensureUnusedCapacity(
            gpa,
            @typeInfo(Zir.Inst.BoolBr).@"struct".field_names.len + body_len,
        );
        const zir_datas = astgen.instructions.items(.data);
        zir_datas[@backingInt(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
            .lhs = bool_br_lhs,
            .body_len = body_len,
        });
        astgen.appendBodyWithFixups(body);
        gz.unstack();
    }

    /// Assumes nothing stacked on `gz`. Unstacks `gz`.
    /// Asserts `inst` is not a `block_comptime`.
    fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;
        const body = gz.instructionsSlice();
        const body_len = astgen.countBodyLenAfterFixups(body);

        const zir_tags = astgen.instructions.items(.tag);
        assert(zir_tags[@backingInt(inst)] != .block_comptime); // use `setComptimeBlockBody` instead

        try astgen.extra.ensureUnusedCapacity(
            gpa,
            @typeInfo(Zir.Inst.Block).@"struct".field_names.len + body_len,
        );
        const zir_datas = astgen.instructions.items(.data);
        zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
            Zir.Inst.Block{ .body_len = body_len },
        );
        astgen.appendBodyWithFixups(body);
        gz.unstack();
    }

    /// Assumes nothing stacked on `gz`. Unstacks `gz`.
    /// Asserts `inst` is a `block_comptime`.
    fn setBlockComptimeBody(gz: *GenZir, inst: Zir.Inst.Index, comptime_reason: std.zig.SimpleComptimeReason) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;
        const body = gz.instructionsSlice();
        const body_len = astgen.countBodyLenAfterFixups(body);

        const zir_tags = astgen.instructions.items(.tag);
        assert(zir_tags[@backingInt(inst)] == .block_comptime); // use `setBlockBody` instead

        try astgen.extra.ensureUnusedCapacity(
            gpa,
            @typeInfo(Zir.Inst.BlockComptime).@"struct".field_names.len + body_len,
        );
        const zir_datas = astgen.instructions.items(.data);
        zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
            Zir.Inst.BlockComptime{
                .reason = comptime_reason,
                .body_len = body_len,
            },
        );
        astgen.appendBodyWithFixups(body);
        gz.unstack();
    }

    /// Assumes nothing stacked on `gz`. Unstacks `gz`.
    fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;
        const body = gz.instructionsSlice();
        const body_len = astgen.countBodyLenAfterFixups(body);
        try astgen.extra.ensureUnusedCapacity(
            gpa,
            @typeInfo(Zir.Inst.Try).@"struct".field_names.len + body_len,
        );
        const zir_datas = astgen.instructions.items(.data);
        zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
            Zir.Inst.Try{
                .operand = operand,
                .body_len = body_len,
            },
        );
        astgen.appendBodyWithFixups(body);
        gz.unstack();
    }

    /// Must be called with the following stack set up:
    ///  * gz (bottom)
    ///  * ret_gz
    ///  * cc_gz
    ///  * body_gz (top)
    /// Unstacks all of those except for `gz`.
    fn addFunc(
        gz: *GenZir,
        args: struct {
            src_node: Ast.Node.Index,
            lbrace_line: u32 = 0,
            lbrace_column: u32 = 0,
            param_block: Zir.Inst.Index,

            ret_gz: ?*GenZir,
            body_gz: ?*GenZir,
            cc_gz: ?*GenZir,

            ret_param_refs: []Zir.Inst.Index,
            param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table`
            ret_ty_is_generic: bool,

            cc_ref: Zir.Inst.Ref,
            ret_ref: Zir.Inst.Ref,

            noalias_bits: u32,
            is_var_args: bool,
            is_inferred_error: bool,
            is_noinline: bool,

            /// Ignored if `body_gz == null`.
            proto_hash: std.zig.SrcHash,
        },
    ) !Zir.Inst.Ref {
        assert(args.src_node != .root);
        const astgen = gz.astgen;
        const gpa = astgen.gpa;
        const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));

        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try astgen.instructions.ensureUnusedCapacity(gpa, 1);

        const body, const cc_body, const ret_body = bodies: {
            var stacked_gz: ?*GenZir = null;
            const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: {
                const body = body_gz.instructionsSliceUptoOpt(stacked_gz);
                stacked_gz = body_gz;
                break :body body;
            } else &.{};
            const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
                const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
                stacked_gz = cc_gz;
                break :body cc_body;
            } else &.{};
            const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
                const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
                stacked_gz = ret_gz;
                break :body ret_body;
            } else &.{};
            break :bodies .{ body, cc_body, ret_body };
        };

        var src_locs_and_hash_buffer: [7]u32 = undefined;
        const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: {
            const tree = astgen.tree;
            const fn_decl = args.src_node;
            const block = switch (tree.nodeTag(fn_decl)) {
                .fn_decl => tree.nodeData(fn_decl).node_and_node[1],
                .test_decl => tree.nodeData(fn_decl).opt_token_and_node[1],
                else => unreachable,
            };
            const rbrace_start = tree.tokenStart(tree.lastToken(block));
            astgen.advanceSourceCursor(rbrace_start);
            const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
            const rbrace_column: u32 = @intCast(astgen.source_column);

            const columns = args.lbrace_column | (rbrace_column << 16);

            const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);

            src_locs_and_hash_buffer = .{
                args.lbrace_line,
                rbrace_line,
                columns,
                proto_hash_arr[0],
                proto_hash_arr[1],
                proto_hash_arr[2],
                proto_hash_arr[3],
            };
            break :src_locs_and_hash &src_locs_and_hash_buffer;
        } else &.{};

        const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);

        const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or
            args.is_var_args or args.noalias_bits != 0 or args.is_noinline)
        inst_info: {
            try astgen.extra.ensureUnusedCapacity(
                gpa,
                @typeInfo(Zir.Inst.FuncFancy).@"struct".field_names.len +
                    fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
                    fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
                    body_len + src_locs_and_hash.len +
                    @intFromBool(args.noalias_bits != 0),
            );
            const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
                .param_block = args.param_block,
                .body_len = body_len,
                .bits = .{
                    .is_var_args = args.is_var_args,
                    .is_inferred_error = args.is_inferred_error,
                    .is_noinline = args.is_noinline,
                    .has_any_noalias = args.noalias_bits != 0,

                    .has_cc_ref = args.cc_ref != .none,
                    .has_ret_ty_ref = ret_ref != .none,

                    .has_cc_body = cc_body.len != 0,
                    .has_ret_ty_body = ret_body.len != 0,

                    .ret_ty_is_generic = args.ret_ty_is_generic,
                },
            });

            const zir_datas = astgen.instructions.items(.data);
            if (cc_body.len != 0) {
                astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body));
                astgen.appendBodyWithFixups(cc_body);
                const break_extra = zir_datas[@backingInt(cc_body[cc_body.len - 1])].@"break".payload_index;
                astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
                    @backingInt(new_index);
            } else if (args.cc_ref != .none) {
                astgen.extra.appendAssumeCapacity(@backingInt(args.cc_ref));
            }
            if (ret_body.len != 0) {
                astgen.extra.appendAssumeCapacity(
                    astgen.countBodyLenAfterFixups(args.ret_param_refs) +
                        astgen.countBodyLenAfterFixups(ret_body),
                );
                astgen.appendBodyWithFixups(args.ret_param_refs);
                astgen.appendBodyWithFixups(ret_body);
                const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
                astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
                    @backingInt(new_index);
            } else if (ret_ref != .none) {
                astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
            }

            if (args.noalias_bits != 0) {
                astgen.extra.appendAssumeCapacity(args.noalias_bits);
            }

            astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
            astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);

            break :inst_info .{ .func_fancy, payload_index };
        } else inst_info: {
            try astgen.extra.ensureUnusedCapacity(
                gpa,
                @typeInfo(Zir.Inst.Func).@"struct".field_names.len + 1 +
                    fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
                    body_len + src_locs_and_hash.len,
            );

            const ret_body_len = if (ret_body.len != 0)
                countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body)
            else
                @intFromBool(ret_ref != .none);

            const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
                .param_block = args.param_block,
                .ret_ty = .{
                    .body_len = @intCast(ret_body_len),
                    .is_generic = args.ret_ty_is_generic,
                },
                .body_len = body_len,
            });
            const zir_datas = astgen.instructions.items(.data);
            if (ret_body.len != 0) {
                astgen.appendBodyWithFixups(args.ret_param_refs);
                astgen.appendBodyWithFixups(ret_body);

                const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
                astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
                    @backingInt(new_index);
            } else if (ret_ref != .none) {
                astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
            }
            astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
            astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);

            break :inst_info .{
                if (args.is_inferred_error) .func_inferred else .func,
                payload_index,
            };
        };

        // Order is important when unstacking.
        if (args.body_gz) |body_gz| body_gz.unstack();
        if (args.cc_gz) |cc_gz| cc_gz.unstack();
        if (args.ret_gz) |ret_gz| ret_gz.unstack();

        astgen.instructions.appendAssumeCapacity(.{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(args.src_node),
                .payload_index = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
        return countBodyLenAfterFixups(astgen, param_refs_body) +
            countBodyLenAfterFixups(astgen, main_body) +
            // If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that.
            @intFromBool(main_body.len > 0 or ref != .none);
    }

    fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = .int,
            .data = .{ .int = integer },
        });
    }

    fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !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.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .int_big,
            .data = .{ .str = .{
                .start = @fromBackingInt(@intCast(astgen.string_bytes.items.len)),
                .len = @intCast(limbs.len),
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
        return new_index.toRef();
    }

    fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = .float,
            .data = .{ .float = number },
        });
    }

    fn addUnNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        operand: Zir.Inst.Ref,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Ref {
        assert(operand != .none);
        return gz.add(.{
            .tag = tag,
            .data = .{ .un_node = .{
                .operand = operand,
                .src_node = gz.nodeIndexToRelative(src_node),
            } },
        });
    }

    fn makeUnNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        operand: Zir.Inst.Ref,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Index {
        assert(operand != .none);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        try gz.astgen.instructions.append(gz.astgen.gpa, .{
            .tag = tag,
            .data = .{ .un_node = .{
                .operand = operand,
                .src_node = gz.nodeIndexToRelative(src_node),
            } },
        });
        return new_index;
    }

    fn addPlNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
        extra: anytype,
    ) !Zir.Inst.Ref {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);

        const payload_index = try gz.astgen.addExtra(extra);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.appendAssumeCapacity(.{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(src_node),
                .payload_index = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn addPlNodePayloadIndex(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
        payload_index: u32,
    ) !Zir.Inst.Ref {
        return try gz.add(.{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(src_node),
                .payload_index = payload_index,
            } },
        });
    }

    /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
    fn addParam(
        gz: *GenZir,
        param_gz: *GenZir,
        /// Previous parameters, which might be referenced in `param_gz` (the new parameter type).
        /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`.
        prev_param_insts: []const Zir.Inst.Index,
        ty_is_generic: bool,
        tag: Zir.Inst.Tag,
        /// Absolute token index. This function does the conversion to Decl offset.
        abs_tok_index: Ast.TokenIndex,
        name: Zir.NullTerminatedString,
    ) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        const param_body = param_gz.instructionsSlice();
        const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts);
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".field_names.len + body_len);

        const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
            .name = name,
            .type = .{
                .body_len = @intCast(body_len),
                .is_generic = ty_is_generic,
            },
        });
        gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts);
        param_gz.unstack();

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.appendAssumeCapacity(.{
            .tag = tag,
            .data = .{ .pl_tok = .{
                .src_tok = gz.tokenIndexToRelative(abs_tok_index),
                .payload_index = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn addStdLangValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.StdLangValue) !Zir.Inst.Ref {
        return addExtendedNodeSmall(gz, .std_lang_value, src_node, @backingInt(val));
    }

    fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
        return addExtendedPayloadSmall(gz, opcode, undefined, extra);
    }

    fn addExtendedPayloadSmall(
        gz: *GenZir,
        opcode: Zir.Inst.Extended,
        small: u16,
        extra: anytype,
    ) !Zir.Inst.Ref {
        const gpa = gz.astgen.gpa;

        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);

        const payload_index = try gz.astgen.addExtra(extra);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = opcode,
                .small = small,
                .operand = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn addExtendedMultiOp(
        gz: *GenZir,
        opcode: Zir.Inst.Extended,
        node: Ast.Node.Index,
        operands: []const Zir.Inst.Ref,
    ) !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.NodeMultiOp).@"struct".field_names.len + operands.len,
        );

        const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
            .src_node = gz.nodeIndexToRelative(node),
        });
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = opcode,
                .small = @intCast(operands.len),
                .operand = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        astgen.appendRefsAssumeCapacity(operands);
        return new_index.toRef();
    }

    fn addExtendedMultiOpPayloadIndex(
        gz: *GenZir,
        opcode: Zir.Inst.Extended,
        payload_index: u32,
        trailing_len: usize,
    ) !Zir.Inst.Ref {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try astgen.instructions.ensureUnusedCapacity(gpa, 1);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = opcode,
                .small = @intCast(trailing_len),
                .operand = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn addExtendedNodeSmall(
        gz: *GenZir,
        opcode: Zir.Inst.Extended,
        src_node: Ast.Node.Index,
        small: u16,
    ) !Zir.Inst.Ref {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try astgen.instructions.ensureUnusedCapacity(gpa, 1);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = opcode,
                .small = small,
                .operand = @bitCast(@backingInt(gz.nodeIndexToRelative(src_node))),
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn addUnTok(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        operand: Zir.Inst.Ref,
        /// Absolute token index. This function does the conversion to Decl offset.
        abs_tok_index: Ast.TokenIndex,
    ) !Zir.Inst.Ref {
        assert(operand != .none);
        return gz.add(.{
            .tag = tag,
            .data = .{ .un_tok = .{
                .operand = operand,
                .src_tok = gz.tokenIndexToRelative(abs_tok_index),
            } },
        });
    }

    fn makeUnTok(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        operand: Zir.Inst.Ref,
        /// Absolute token index. This function does the conversion to Decl offset.
        abs_tok_index: Ast.TokenIndex,
    ) !Zir.Inst.Index {
        const astgen = gz.astgen;
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        assert(operand != .none);
        try astgen.instructions.append(astgen.gpa, .{
            .tag = tag,
            .data = .{ .un_tok = .{
                .operand = operand,
                .src_tok = gz.tokenIndexToRelative(abs_tok_index),
            } },
        });
        return new_index;
    }

    fn addStrTok(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        str_index: Zir.NullTerminatedString,
        /// Absolute token index. This function does the conversion to Decl offset.
        abs_tok_index: Ast.TokenIndex,
    ) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = tag,
            .data = .{ .str_tok = .{
                .start = str_index,
                .src_tok = gz.tokenIndexToRelative(abs_tok_index),
            } },
        });
    }

    fn addSaveErrRetIndex(
        gz: *GenZir,
        cond: union(enum) {
            always: void,
            if_of_error_type: Zir.Inst.Ref,
        },
    ) !Zir.Inst.Index {
        return gz.addAsIndex(.{
            .tag = .save_err_ret_index,
            .data = .{ .save_err_ret_index = .{
                .operand = switch (cond) {
                    .if_of_error_type => |x| x,
                    else => .none,
                },
            } },
        });
    }

    const BranchTarget = union(enum) {
        ret,
        block: Zir.Inst.Index,
    };

    fn addRestoreErrRetIndex(
        gz: *GenZir,
        bt: BranchTarget,
        cond: union(enum) {
            always: void,
            if_non_error: Zir.Inst.Ref,
        },
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Index {
        switch (cond) {
            .always => return gz.addAsIndex(.{
                .tag = .restore_err_ret_index_unconditional,
                .data = .{ .un_node = .{
                    .operand = switch (bt) {
                        .ret => .none,
                        .block => |b| b.toRef(),
                    },
                    .src_node = gz.nodeIndexToRelative(src_node),
                } },
            }),
            .if_non_error => |operand| switch (bt) {
                .ret => return gz.addAsIndex(.{
                    .tag = .restore_err_ret_index_fn_entry,
                    .data = .{ .un_node = .{
                        .operand = operand,
                        .src_node = gz.nodeIndexToRelative(src_node),
                    } },
                }),
                .block => |block| return (try gz.addExtendedPayload(
                    .restore_err_ret_index,
                    Zir.Inst.RestoreErrRetIndex{
                        .src_node = gz.nodeIndexToRelative(src_node),
                        .block = block.toRef(),
                        .operand = operand,
                    },
                )).toIndex().?,
            },
        }
    }

    fn addBreak(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        block_inst: Zir.Inst.Index,
        operand: Zir.Inst.Ref,
    ) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);

        const new_index = try gz.makeBreak(tag, block_inst, operand);
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn makeBreak(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        block_inst: Zir.Inst.Index,
        operand: Zir.Inst.Ref,
    ) !Zir.Inst.Index {
        return gz.makeBreakCommon(tag, block_inst, operand, null);
    }

    fn addBreakWithSrcNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        block_inst: Zir.Inst.Index,
        operand: Zir.Inst.Ref,
        operand_src_node: Ast.Node.Index,
    ) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);

        const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn makeBreakWithSrcNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        block_inst: Zir.Inst.Index,
        operand: Zir.Inst.Ref,
        operand_src_node: Ast.Node.Index,
    ) !Zir.Inst.Index {
        return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
    }

    fn makeBreakCommon(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        block_inst: Zir.Inst.Index,
        operand: Zir.Inst.Ref,
        operand_src_node: ?Ast.Node.Index,
    ) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".field_names.len);

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.appendAssumeCapacity(.{
            .tag = tag,
            .data = .{ .@"break" = .{
                .operand = operand,
                .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
                    .operand_src_node = if (operand_src_node) |src_node|
                        gz.nodeIndexToRelative(src_node).toOptional()
                    else
                        .none,
                    .block_inst = block_inst,
                }),
            } },
        });
        return new_index;
    }

    fn addBin(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        lhs: Zir.Inst.Ref,
        rhs: Zir.Inst.Ref,
    ) !Zir.Inst.Ref {
        assert(lhs != .none);
        assert(rhs != .none);
        return gz.add(.{
            .tag = tag,
            .data = .{ .bin = .{
                .lhs = lhs,
                .rhs = rhs,
            } },
        });
    }

    fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
        _ = try gz.add(.{
            .tag = .@"defer",
            .data = .{ .@"defer" = .{
                .index = index,
                .len = len,
            } },
        });
    }

    fn addDecl(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        decl_index: u32,
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(src_node),
                .payload_index = decl_index,
            } },
        });
    }

    fn addNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = tag,
            .data = .{ .node = gz.nodeIndexToRelative(src_node) },
        });
    }

    fn addInstNode(
        gz: *GenZir,
        tag: Zir.Inst.Tag,
        inst: Zir.Inst.Index,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = tag,
            .data = .{ .inst_node = .{
                .inst = inst,
                .src_node = gz.nodeIndexToRelative(src_node),
            } },
        });
    }

    fn addNodeExtended(
        gz: *GenZir,
        opcode: Zir.Inst.Extended,
        /// Absolute node index. This function does the conversion to offset from Decl.
        src_node: Ast.Node.Index,
    ) !Zir.Inst.Ref {
        return gz.add(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = opcode,
                .small = undefined,
                .operand = @bitCast(@backingInt(gz.nodeIndexToRelative(src_node))),
            } },
        });
    }

    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".field_names.len +
                @intFromBool(args.type_inst != .none) +
                @intFromBool(args.align_inst != .none),
        );
        const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
            .src_node = gz.nodeIndexToRelative(args.node),
        });
        if (args.type_inst != .none) {
            astgen.extra.appendAssumeCapacity(@backingInt(args.type_inst));
        }
        if (args.align_inst != .none) {
            astgen.extra.appendAssumeCapacity(@backingInt(args.align_inst));
        }

        const has_type: u4 = @intFromBool(args.type_inst != .none);
        const has_align: u4 = @intFromBool(args.align_inst != .none);
        const is_const: u4 = @intFromBool(args.is_const);
        const is_comptime: u4 = @intFromBool(args.is_comptime);
        const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = .alloc,
                .small = small,
                .operand = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    fn addAsm(
        gz: *GenZir,
        args: struct {
            tag: Zir.Inst.Extended,
            /// Absolute node index. This function does the conversion to offset from Decl.
            node: Ast.Node.Index,
            asm_source: Zir.NullTerminatedString,
            output_type_bits: u32,
            is_volatile: bool,
            outputs: []const Zir.Inst.Asm.Output,
            inputs: []const Zir.Inst.Asm.Input,
            clobbers: Zir.Inst.Ref,
        },
    ) !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.Asm).@"struct".field_names.len +
            args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".field_names.len +
            args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".field_names.len);

        const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
            .src_node = gz.nodeIndexToRelative(args.node),
            .asm_source = args.asm_source,
            .output_type_bits = args.output_type_bits,
            .clobbers = args.clobbers,
        });
        for (args.outputs) |output| {
            _ = gz.astgen.addExtraAssumeCapacity(output);
        }
        for (args.inputs) |input| {
            _ = gz.astgen.addExtraAssumeCapacity(input);
        }

        const small: Zir.Inst.Asm.Small = .{
            .is_volatile = args.is_volatile,
            .outputs_len = @intCast(args.outputs.len),
            .inputs_len = @intCast(args.inputs.len),
        };

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
        astgen.instructions.appendAssumeCapacity(.{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = args.tag,
                .small = @bitCast(small),
                .operand = payload_index,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index.toRef();
    }

    /// Note that this returns a `Zir.Inst.Index` not a ref.
    /// Does *not* append the block instruction to the scope.
    /// Leaves the `payload_index` field undefined.
    fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        const gpa = gz.astgen.gpa;
        try gz.astgen.instructions.append(gpa, .{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(node),
                .payload_index = undefined,
            } },
        });
        return new_index;
    }

    /// Note that this returns a `Zir.Inst.Index` not a ref.
    /// Does *not* append the block instruction to the scope.
    /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize.
    fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index {
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        try gz.astgen.instructions.append(gz.astgen.gpa, .{
            .tag = .declaration,
            .data = .{ .declaration = .{
                .src_node = node,
                .payload_index = undefined,
            } },
        });
        return new_index;
    }

    /// Note that this returns a `Zir.Inst.Index` not a ref.
    /// Leaves the `payload_index` field undefined.
    fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        try gz.astgen.instructions.append(gpa, .{
            .tag = tag,
            .data = .{ .pl_node = .{
                .src_node = gz.nodeIndexToRelative(node),
                .payload_index = undefined,
            } },
        });
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
        src_line: u32,
        src_column: u32,
        src_node: Ast.Node.Index,
        name_strat: Zir.Inst.NameStrategy,
        layout: std.lang.Type.ContainerLayout,
        backing_int_type_body_len: ?u32,
        decls_len: u32,
        fields_len: u32,
        any_field_aligns: bool,
        any_field_defaults: bool,
        any_comptime_fields: bool,
        fields_hash: std.zig.SrcHash,
        captures: []const Zir.Inst.Capture,
        capture_names: []const Zir.NullTerminatedString,
        /// The trailing declaration list, field information, and body instructions.
        remaining: []const u32,
    }) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        // Node .root is valid for the root `struct_decl` of a file!
        assert(args.src_node != .root or gz.parent.tag == .top);

        const captures_len: u32 = @intCast(args.captures.len);
        assert(args.capture_names.len == captures_len);

        const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);

        try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".field_names.len +
            4 + // `captures_len`, `decls_len`, `fields_len`, `backing_int_type_body_len`
            captures_len * 2 + // `capture`, `capture_name`
            args.remaining.len);

        const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
            .fields_hash_0 = fields_hash_arr[0],
            .fields_hash_1 = fields_hash_arr[1],
            .fields_hash_2 = fields_hash_arr[2],
            .fields_hash_3 = fields_hash_arr[3],
            .src_line = args.src_line,
            .src_column = args.src_column,
            .src_node = args.src_node,
        });

        if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
        if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
        if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
        if (args.backing_int_type_body_len) |n| astgen.extra.appendAssumeCapacity(n);
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
        astgen.extra.appendSliceAssumeCapacity(args.remaining);

        astgen.instructions.set(@backingInt(inst), .{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = .struct_decl,
                .small = @bitCast(Zir.Inst.StructDecl.Small{
                    .has_captures_len = captures_len != 0,
                    .has_decls_len = args.decls_len != 0,
                    .has_fields_len = args.fields_len != 0,
                    .name_strategy = args.name_strat,
                    .layout = args.layout,
                    .has_backing_int_type = args.backing_int_type_body_len != null,
                    .any_field_aligns = args.any_field_aligns,
                    .any_field_defaults = args.any_field_defaults,
                    .any_comptime_fields = args.any_comptime_fields,
                }),
                .operand = payload_index,
            } },
        });
    }

    fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
        src_line: u32,
        src_column: u32,
        src_node: Ast.Node.Index,
        name_strat: Zir.Inst.NameStrategy,
        kind: Zir.Inst.UnionDecl.Kind,
        arg_type_body_len: ?u32,
        decls_len: u32,
        fields_len: u32,
        any_field_aligns: bool,
        any_field_values: bool,
        fields_hash: std.zig.SrcHash,
        captures: []const Zir.Inst.Capture,
        capture_names: []const Zir.NullTerminatedString,
        /// The trailing declaration list, field information, and body instructions.
        remaining: []const u32,
    }) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        assert(args.src_node != .root);

        const captures_len: u32 = @intCast(args.captures.len);
        assert(args.capture_names.len == captures_len);

        const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);

        try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".field_names.len +
            4 + // `captures_len`, `decls_len`, `fields_len`, `arg_type_body_len`
            captures_len * 2 + // `capture`, `capture_name`
            args.remaining.len);

        const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
            .fields_hash_0 = fields_hash_arr[0],
            .fields_hash_1 = fields_hash_arr[1],
            .fields_hash_2 = fields_hash_arr[2],
            .fields_hash_3 = fields_hash_arr[3],
            .src_line = args.src_line,
            .src_column = args.src_column,
            .src_node = args.src_node,
        });

        if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
        if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
        if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
        if (args.kind.hasArgType()) {
            astgen.extra.appendAssumeCapacity(args.arg_type_body_len.?);
        } else {
            assert(args.arg_type_body_len == null);
        }
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
        astgen.extra.appendSliceAssumeCapacity(args.remaining);

        astgen.instructions.set(@backingInt(inst), .{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = .union_decl,
                .small = @bitCast(Zir.Inst.UnionDecl.Small{
                    .has_captures_len = captures_len != 0,
                    .has_decls_len = args.decls_len != 0,
                    .has_fields_len = args.fields_len != 0,
                    .name_strategy = args.name_strat,
                    .kind = args.kind,
                    .any_field_aligns = args.any_field_aligns,
                    .any_field_values = args.any_field_values,
                }),
                .operand = payload_index,
            } },
        });
    }

    fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
        src_line: u32,
        src_column: u32,
        src_node: Ast.Node.Index,
        name_strat: Zir.Inst.NameStrategy,
        tag_type_body_len: ?u32,
        nonexhaustive: bool,
        decls_len: u32,
        fields_len: u32,
        any_field_values: bool,
        fields_hash: std.zig.SrcHash,
        captures: []const Zir.Inst.Capture,
        capture_names: []const Zir.NullTerminatedString,
        /// The trailing declaration list, field information, and body instructions.
        remaining: []const u32,
    }) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        assert(args.src_node != .root);

        const captures_len: u32 = @intCast(args.captures.len);
        assert(args.capture_names.len == captures_len);

        const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);

        try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".field_names.len +
            4 + // `captures_len`, `decls_len`, `fields_len`, `tag_type_body_len`
            captures_len * 2 + // `capture`, `capture_name`
            args.remaining.len);

        const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
            .fields_hash_0 = fields_hash_arr[0],
            .fields_hash_1 = fields_hash_arr[1],
            .fields_hash_2 = fields_hash_arr[2],
            .fields_hash_3 = fields_hash_arr[3],
            .src_line = args.src_line,
            .src_column = args.src_column,
            .src_node = args.src_node,
        });

        if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
        if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
        if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
        if (args.tag_type_body_len) |n| astgen.extra.appendAssumeCapacity(n);
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
        astgen.extra.appendSliceAssumeCapacity(args.remaining);

        astgen.instructions.set(@backingInt(inst), .{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = .enum_decl,
                .small = @bitCast(Zir.Inst.EnumDecl.Small{
                    .has_captures_len = captures_len != 0,
                    .has_decls_len = args.decls_len != 0,
                    .has_fields_len = args.fields_len != 0,
                    .name_strategy = args.name_strat,
                    .has_tag_type = args.tag_type_body_len != null,
                    .nonexhaustive = args.nonexhaustive,
                    .any_field_values = args.any_field_values,
                }),
                .operand = payload_index,
            } },
        });
    }

    fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
        src_line: u32,
        src_column: u32,
        src_node: Ast.Node.Index,
        name_strat: Zir.Inst.NameStrategy,
        decls_len: u32,
        captures: []const Zir.Inst.Capture,
        capture_names: []const Zir.NullTerminatedString,
        decls: []const Zir.Inst.Index,
    }) !void {
        const astgen = gz.astgen;
        const gpa = astgen.gpa;

        assert(args.src_node != .root);

        const captures_len: u32 = @intCast(args.captures.len);
        assert(args.capture_names.len == captures_len);

        try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".field_names.len +
            2 + // `captures_len`, `decls_len`
            captures_len * 2 + // `capture`, `capture_name`
            args.decls.len);

        const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
            .src_line = args.src_line,
            .src_column = args.src_column,
            .src_node = args.src_node,
        });
        if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
        if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
        astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.decls));

        astgen.instructions.set(@backingInt(inst), .{
            .tag = .extended,
            .data = .{ .extended = .{
                .opcode = .opaque_decl,
                .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
                    .has_captures_len = captures_len != 0,
                    .has_decls_len = args.decls_len != 0,
                    .name_strategy = args.name_strat,
                }),
                .operand = payload_index,
            } },
        });
    }

    fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
        return (try gz.addAsIndex(inst)).toRef();
    }

    fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.appendAssumeCapacity(inst);
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
        const gpa = gz.astgen.gpa;
        try gz.instructions.ensureUnusedCapacity(gpa, 1);
        try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);

        const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
        gz.astgen.instructions.len += 1;
        gz.instructions.appendAssumeCapacity(new_index);
        return new_index;
    }

    fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
        switch (ri.rl) {
            .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
            .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
            else => unreachable,
        }
    }

    fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
        if (gz.is_comptime) return;

        _ = try gz.add(.{ .tag = tag, .data = .{
            .str_op = .{
                .str = name,
                .operand = inst,
            },
        } });
    }
}