const ResultInfo = struct fn br(ri: ResultInfo) ResultInfoTurns a coerced_ty back into a ty. Should be called at branch points such as if and switch expressions.
fn zirTag(ri: ResultInfo) Zir.Inst.Tag267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394const ResultInfo = struct {
/// The semantics requested for the result location
rl: Loc,
/// The "operator" consuming the result location
ctx: Context = .none,
/// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
/// such as if and switch expressions.
fn br(ri: ResultInfo) ResultInfo {
return switch (ri.rl) {
.coerced_ty => |ty| .{
.rl = .{ .ty = ty },
.ctx = ri.ctx,
},
else => ri,
};
}
fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
switch (ri.rl) {
.ty => return switch (ri.ctx) {
.shift_op => .as_shift_operand,
else => .as_node,
},
else => unreachable,
}
}
const Loc = union(enum) {
/// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
/// expression should be generated. The result instruction from the expression must
/// be ignored.
discard,
/// The expression has an inferred type, and it will be evaluated as an rvalue.
none,
/// The expression will be coerced into this type, but it will be evaluated as an rvalue.
ty: Zir.Inst.Ref,
/// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
/// so no `as` instruction needs to be emitted.
coerced_ty: Zir.Inst.Ref,
/// The expression must generate a pointer rather than a value. For example, the left hand side
/// of an assignment uses this kind of result location.
ref,
/// The expression must generate a pointer rather than a value, and the pointer will be coerced
/// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
ref_coerced_ty: Zir.Inst.Ref,
/// Like `ref`, but the pointer will never be stored to, so local variables should not be
/// marked as possibly being mutated.
ref_const,
/// The expression must store its result into this typed pointer. The result instruction
/// from the expression must be ignored.
ptr: PtrResultLoc,
/// The expression must store its result into this allocation, which has an inferred type.
/// The result instruction from the expression must be ignored.
/// Always an instruction with tag `alloc_inferred`.
inferred_ptr: Zir.Inst.Ref,
/// The expression has a sequence of pointers to store its results into due to a destructure
/// operation. Each of these pointers may or may not have an inferred type.
destructure: struct {
/// The AST node of the destructure operation itself.
src_node: Ast.Node.Index,
/// The pointers to store results into.
components: []const DestructureComponent,
},
const DestructureComponent = union(enum) {
typed_ptr: PtrResultLoc,
inferred_ptr: Zir.Inst.Ref,
discard,
};
const PtrResultLoc = struct {
inst: Zir.Inst.Ref,
src_node: ?Ast.Node.Index = null,
};
/// Find the result type for a cast builtin given the result location.
/// If the location does not have a known result type, returns `null`.
fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
return switch (rl) {
.discard, .none, .ref, .ref_const, .inferred_ptr, .destructure => null,
.ty, .coerced_ty => |ty_ref| ty_ref,
.ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
.ptr => |ptr| {
const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
return try gz.addUnNode(.elem_type, ptr_ty, node);
},
};
}
/// Find the result type for a cast builtin given the result location.
/// If the location does not have a known result type, emits an error on
/// the given node.
fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
const astgen = gz.astgen;
if (try rl.resultType(gz, node)) |ty| return ty;
switch (rl) {
.destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
}),
else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
}),
}
}
};
const Context = enum {
/// The expression is the operand to a return expression.
@"return",
/// The expression is the input to an error-handling operator (if-else, try, or catch).
error_handling_expr,
/// The expression is the right-hand side of a shift operation.
shift_op,
/// The expression is an argument in a function call.
fn_arg,
/// The expression is the right-hand side of an initializer for a `const` variable
const_init,
/// The expression is the right-hand side of an assignment expression.
assignment,
/// No specific operator in particular.
none,
/// The expression is operand to address-of which is the operand to a return expression.
return_addrof,
};
}