@@ -335,6 +335,32 @@ const ResultInfo = struct {
335
335
},
336
336
}
337
337
}
338
+
339
+ /// Find the result type for a cast builtin given the result location.
340
+ /// If the location does not have a known result type, emits an error on
341
+ /// the given node.
342
+ fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
343
+ const astgen = gz.astgen;
344
+ switch (rl) {
345
+ .discard, .none, .ref, .inferred_ptr => {},
346
+ .ty, .coerced_ty => |ty_ref| return ty_ref,
347
+ .ptr => |ptr| {
348
+ const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
349
+ return gz.addUnNode(.elem_type, ptr_ty, node);
350
+ },
351
+ .block_ptr => |block_scope| {
352
+ if (block_scope.rl_ty_inst != .none) return block_scope.rl_ty_inst;
353
+ if (block_scope.break_result_info.rl == .ptr) {
354
+ const ptr_ty = try gz.addUnNode(.typeof, block_scope.break_result_info.rl.ptr.inst, node);
355
+ return gz.addUnNode(.elem_type, ptr_ty, node);
356
+ }
357
+ },
358
+ }
359
+
360
+ return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
361
+ try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
362
+ });
363
+ }
338
364
};
339
365
340
366
const Context = enum {
@@ -2521,6 +2547,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2521
2547
.array_type,
2522
2548
.array_type_sentinel,
2523
2549
.elem_type_index,
2550
+ .elem_type,
2524
2551
.vector_type,
2525
2552
.indexable_ptr_len,
2526
2553
.anyframe_type,
@@ -2662,7 +2689,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2662
2689
.int_cast,
2663
2690
.ptr_cast,
2664
2691
.truncate,
2665
- .align_cast,
2666
2692
.has_decl,
2667
2693
.has_field,
2668
2694
.clz,
@@ -7924,18 +7950,127 @@ fn bitCast(
7924
7950
scope: *Scope,
7925
7951
ri: ResultInfo,
7926
7952
node: Ast.Node.Index,
7927
- lhs: Ast.Node.Index,
7928
- rhs: Ast.Node.Index,
7953
+ operand_node: Ast.Node.Index,
7929
7954
) InnerError!Zir.Inst.Ref {
7930
- const dest_type = try reachableTypeExpr (gz, scope, lhs, node );
7931
- const operand = try reachableExpr(gz, scope, .{ .rl = .none }, rhs , node);
7955
+ const dest_type = try ri.rl.resultType (gz, node, "@bitCast" );
7956
+ const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node , node);
7932
7957
const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
7933
7958
.lhs = dest_type,
7934
7959
.rhs = operand,
7935
7960
});
7936
7961
return rvalue(gz, ri, result, node);
7937
7962
}
7938
7963
7964
+ /// Handle one or more nested pointer cast builtins:
7965
+ /// * @ptrCast
7966
+ /// * @alignCast
7967
+ /// * @addrSpaceCast
7968
+ /// * @constCast
7969
+ /// * @volatileCast
7970
+ /// Any sequence of such builtins is treated as a single operation. This allowed
7971
+ /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
7972
+ /// intermediate result type being unknown.
7973
+ fn ptrCast(
7974
+ gz: *GenZir,
7975
+ scope: *Scope,
7976
+ ri: ResultInfo,
7977
+ root_node: Ast.Node.Index,
7978
+ ) InnerError!Zir.Inst.Ref {
7979
+ const astgen = gz.astgen;
7980
+ const tree = astgen.tree;
7981
+ const main_tokens = tree.nodes.items(.main_token);
7982
+ const node_datas = tree.nodes.items(.data);
7983
+ const node_tags = tree.nodes.items(.tag);
7984
+
7985
+ var flags: Zir.Inst.FullPtrCastFlags = .{};
7986
+
7987
+ // Note that all pointer cast builtins have one parameter, so we only need
7988
+ // to handle `builtin_call_two`.
7989
+ var node = root_node;
7990
+ while (true) {
7991
+ switch (node_tags[node]) {
7992
+ .builtin_call_two, .builtin_call_two_comma => {},
7993
+ .grouped_expression => {
7994
+ // Handle the chaining even with redundant parentheses
7995
+ node = node_datas[node].lhs;
7996
+ continue;
7997
+ },
7998
+ else => break,
7999
+ }
8000
+
8001
+ if (node_datas[node].lhs == 0) break; // 0 args
8002
+ if (node_datas[node].rhs != 0) break; // 2 args
8003
+
8004
+ const builtin_token = main_tokens[node];
8005
+ const builtin_name = tree.tokenSlice(builtin_token);
8006
+ const info = BuiltinFn.list.get(builtin_name) orelse break;
8007
+ if (info.param_count != 1) break;
8008
+
8009
+ switch (info.tag) {
8010
+ else => break,
8011
+ inline .ptr_cast,
8012
+ .align_cast,
8013
+ .addrspace_cast,
8014
+ .const_cast,
8015
+ .volatile_cast,
8016
+ => |tag| {
8017
+ if (@field(flags, @tagName(tag))) {
8018
+ return astgen.failNode(node, "redundant {s}", .{builtin_name});
8019
+ }
8020
+ @field(flags, @tagName(tag)) = true;
8021
+ },
8022
+ }
8023
+
8024
+ node = node_datas[node].lhs;
8025
+ }
8026
+
8027
+ const flags_i = @bitCast(u5, flags);
8028
+ assert(flags_i != 0);
8029
+
8030
+ const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
8031
+ if (flags_i == @bitCast(u5, ptr_only)) {
8032
+ // Special case: simpler representation
8033
+ return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
8034
+ }
8035
+
8036
+ const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
8037
+ .const_cast = true,
8038
+ .volatile_cast = true,
8039
+ };
8040
+ if ((flags_i & ~@bitCast(u5, no_result_ty_flags)) == 0) {
8041
+ // Result type not needed
8042
+ const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8043
+ const operand = try expr(gz, scope, .{ .rl = .none }, node);
8044
+ try emitDbgStmt(gz, cursor);
8045
+ const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_i, Zir.Inst.UnNode{
8046
+ .node = gz.nodeIndexToRelative(root_node),
8047
+ .operand = operand,
8048
+ });
8049
+ return rvalue(gz, ri, result, root_node);
8050
+ }
8051
+
8052
+ // Full cast including result type
8053
+ const need_result_type_builtin = if (flags.ptr_cast)
8054
+ "@ptrCast"
8055
+ else if (flags.align_cast)
8056
+ "@alignCast"
8057
+ else if (flags.addrspace_cast)
8058
+ "@addrSpaceCast"
8059
+ else
8060
+ unreachable;
8061
+
8062
+ const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8063
+ const result_type = try ri.rl.resultType(gz, root_node, need_result_type_builtin);
8064
+ const operand = try expr(gz, scope, .{ .rl = .none }, node);
8065
+ try emitDbgStmt(gz, cursor);
8066
+ const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_i, Zir.Inst.BinNode{
8067
+ .node = gz.nodeIndexToRelative(root_node),
8068
+ .lhs = result_type,
8069
+ .rhs = operand,
8070
+ });
8071
+ return rvalue(gz, ri, result, root_node);
8072
+ }
8073
+
7939
8074
fn typeOf(
7940
8075
gz: *GenZir,
7941
8076
scope: *Scope,
@@ -8123,7 +8258,7 @@ fn builtinCall(
8123
8258
8124
8259
// zig fmt: off
8125
8260
.as => return as( gz, scope, ri, node, params[0], params[1]),
8126
- .bit_cast => return bitCast( gz, scope, ri, node, params[0], params[1] ),
8261
+ .bit_cast => return bitCast( gz, scope, ri, node, params[0]),
8127
8262
.TypeOf => return typeOf( gz, scope, ri, node, params),
8128
8263
.union_init => return unionInit(gz, scope, ri, node, params),
8129
8264
.c_import => return cImport( gz, scope, node, params[0]),
@@ -8308,14 +8443,13 @@ fn builtinCall(
8308
8443
.Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
8309
8444
.frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
8310
8445
8311
- .int_from_float => return typeCast(gz, scope, ri, node, params[0], params[1], .int_from_float),
8312
- .float_from_int => return typeCast(gz, scope, ri, node, params[0], params[1], .float_from_int),
8313
- .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_from_int),
8314
- .enum_from_int => return typeCast(gz, scope, ri, node, params[0], params[1], .enum_from_int),
8315
- .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
8316
- .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
8317
- .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
8318
- .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
8446
+ .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
8447
+ .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
8448
+ .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
8449
+ .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
8450
+ .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
8451
+ .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
8452
+ .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
8319
8453
// zig fmt: on
8320
8454
8321
8455
.Type => {
@@ -8368,49 +8502,22 @@ fn builtinCall(
8368
8502
});
8369
8503
return rvalue(gz, ri, result, node);
8370
8504
},
8371
- .align_cast => {
8372
- const dest_align = try comptimeExpr(gz, scope, align_ri, params[0]);
8373
- const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
8374
- const result = try gz.addPlNode(.align_cast, node, Zir.Inst.Bin{
8375
- .lhs = dest_align,
8376
- .rhs = rhs,
8377
- });
8378
- return rvalue(gz, ri, result, node);
8379
- },
8380
8505
.err_set_cast => {
8381
8506
try emitDbgNode(gz, node);
8382
8507
8383
8508
const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
8384
- .lhs = try typeExpr (gz, scope, params[0] ),
8385
- .rhs = try expr(gz, scope, .{ .rl = .none }, params[1 ]),
8509
+ .lhs = try ri.rl.resultType (gz, node, "@errSetCast" ),
8510
+ .rhs = try expr(gz, scope, .{ .rl = .none }, params[0 ]),
8386
8511
.node = gz.nodeIndexToRelative(node),
8387
8512
});
8388
8513
return rvalue(gz, ri, result, node);
8389
8514
},
8390
- .addrspace_cast => {
8391
- const result = try gz.addExtendedPayload(.addrspace_cast, Zir.Inst.BinNode{
8392
- .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .address_space_type } }, params[0]),
8393
- .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
8394
- .node = gz.nodeIndexToRelative(node),
8395
- });
8396
- return rvalue(gz, ri, result, node);
8397
- },
8398
- .const_cast => {
8399
- const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
8400
- const result = try gz.addExtendedPayload(.const_cast, Zir.Inst.UnNode{
8401
- .node = gz.nodeIndexToRelative(node),
8402
- .operand = operand,
8403
- });
8404
- return rvalue(gz, ri, result, node);
8405
- },
8406
- .volatile_cast => {
8407
- const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
8408
- const result = try gz.addExtendedPayload(.volatile_cast, Zir.Inst.UnNode{
8409
- .node = gz.nodeIndexToRelative(node),
8410
- .operand = operand,
8411
- });
8412
- return rvalue(gz, ri, result, node);
8413
- },
8515
+ .ptr_cast,
8516
+ .align_cast,
8517
+ .addrspace_cast,
8518
+ .const_cast,
8519
+ .volatile_cast,
8520
+ => return ptrCast(gz, scope, ri, node),
8414
8521
8415
8522
// zig fmt: off
8416
8523
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
@@ -8725,13 +8832,13 @@ fn typeCast(
8725
8832
scope: *Scope,
8726
8833
ri: ResultInfo,
8727
8834
node: Ast.Node.Index,
8728
- lhs_node: Ast.Node.Index,
8729
- rhs_node: Ast.Node.Index,
8835
+ operand_node: Ast.Node.Index,
8730
8836
tag: Zir.Inst.Tag,
8837
+ builtin_name: []const u8,
8731
8838
) InnerError!Zir.Inst.Ref {
8732
8839
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
8733
- const result_type = try typeExpr (gz, scope, lhs_node );
8734
- const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node );
8840
+ const result_type = try ri.rl.resultType (gz, node, builtin_name );
8841
+ const operand = try expr(gz, scope, .{ .rl = .none }, operand_node );
8735
8842
8736
8843
try emitDbgStmt(gz, cursor);
8737
8844
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
@@ -9432,6 +9539,7 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
9432
9539
switch (builtin_info.needs_mem_loc) {
9433
9540
.never => return false,
9434
9541
.always => return true,
9542
+ .forward0 => node = node_datas[node].lhs,
9435
9543
.forward1 => node = node_datas[node].rhs,
9436
9544
}
9437
9545
// Missing builtin arg is not a parsing error, expect an error later.
@@ -9448,6 +9556,7 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
9448
9556
switch (builtin_info.needs_mem_loc) {
9449
9557
.never => return false,
9450
9558
.always => return true,
9559
+ .forward0 => node = params[0],
9451
9560
.forward1 => node = params[1],
9452
9561
}
9453
9562
// Missing builtin arg is not a parsing error, expect an error later.
0 commit comments