Skip to content

Commit 6650410

Browse files
committed
compiler: remove destination type from cast builtins
Resolves: ziglang#5909
1 parent 8dcb4a3 commit 6650410

7 files changed

+608
-356
lines changed

src/AstGen.zig

+162-53
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,32 @@ const ResultInfo = struct {
335335
},
336336
}
337337
}
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+
}
338364
};
339365

340366
const Context = enum {
@@ -2521,6 +2547,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
25212547
.array_type,
25222548
.array_type_sentinel,
25232549
.elem_type_index,
2550+
.elem_type,
25242551
.vector_type,
25252552
.indexable_ptr_len,
25262553
.anyframe_type,
@@ -2662,7 +2689,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
26622689
.int_cast,
26632690
.ptr_cast,
26642691
.truncate,
2665-
.align_cast,
26662692
.has_decl,
26672693
.has_field,
26682694
.clz,
@@ -7924,18 +7950,127 @@ fn bitCast(
79247950
scope: *Scope,
79257951
ri: ResultInfo,
79267952
node: Ast.Node.Index,
7927-
lhs: Ast.Node.Index,
7928-
rhs: Ast.Node.Index,
7953+
operand_node: Ast.Node.Index,
79297954
) 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);
79327957
const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
79337958
.lhs = dest_type,
79347959
.rhs = operand,
79357960
});
79367961
return rvalue(gz, ri, result, node);
79377962
}
79387963

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+
79398074
fn typeOf(
79408075
gz: *GenZir,
79418076
scope: *Scope,
@@ -8123,7 +8258,7 @@ fn builtinCall(
81238258

81248259
// zig fmt: off
81258260
.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]),
81278262
.TypeOf => return typeOf( gz, scope, ri, node, params),
81288263
.union_init => return unionInit(gz, scope, ri, node, params),
81298264
.c_import => return cImport( gz, scope, node, params[0]),
@@ -8308,14 +8443,13 @@ fn builtinCall(
83088443
.Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
83098444
.frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
83108445

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),
83198453
// zig fmt: on
83208454

83218455
.Type => {
@@ -8368,49 +8502,22 @@ fn builtinCall(
83688502
});
83698503
return rvalue(gz, ri, result, node);
83708504
},
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-
},
83808505
.err_set_cast => {
83818506
try emitDbgNode(gz, node);
83828507

83838508
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]),
83868511
.node = gz.nodeIndexToRelative(node),
83878512
});
83888513
return rvalue(gz, ri, result, node);
83898514
},
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),
84148521

84158522
// zig fmt: off
84168523
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
@@ -8725,13 +8832,13 @@ fn typeCast(
87258832
scope: *Scope,
87268833
ri: ResultInfo,
87278834
node: Ast.Node.Index,
8728-
lhs_node: Ast.Node.Index,
8729-
rhs_node: Ast.Node.Index,
8835+
operand_node: Ast.Node.Index,
87308836
tag: Zir.Inst.Tag,
8837+
builtin_name: []const u8,
87318838
) InnerError!Zir.Inst.Ref {
87328839
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);
87358842

87368843
try emitDbgStmt(gz, cursor);
87378844
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_
94329539
switch (builtin_info.needs_mem_loc) {
94339540
.never => return false,
94349541
.always => return true,
9542+
.forward0 => node = node_datas[node].lhs,
94359543
.forward1 => node = node_datas[node].rhs,
94369544
}
94379545
// 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_
94489556
switch (builtin_info.needs_mem_loc) {
94499557
.never => return false,
94509558
.always => return true,
9559+
.forward0 => node = params[0],
94519560
.forward1 => node = params[1],
94529561
}
94539562
// Missing builtin arg is not a parsing error, expect an error later.

src/Autodoc.zig

-3
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,6 @@ fn walkInstruction(
15291529
.int_cast,
15301530
.ptr_cast,
15311531
.truncate,
1532-
.align_cast,
15331532
.has_decl,
15341533
.has_field,
15351534
.div_exact,
@@ -3024,8 +3023,6 @@ fn walkInstruction(
30243023
.int_from_error,
30253024
.error_from_int,
30263025
.reify,
3027-
.const_cast,
3028-
.volatile_cast,
30293026
=> {
30303027
const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data;
30313028
const bin_index = self.exprs.items.len;

0 commit comments

Comments
 (0)