authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-31 20:40:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-31 20:40:15-07:00
logf250802ce794589f77367113335216c1993f7412
tree80f260bc0447d67924caa4162344ad21bf71ea2a
parentfc23fe90ce1f3d28841c5a5a93a8bdf7edaefc54

compiler: introduce `@Restrict` builtin

conservative, incomplete change

8 files changed, 96 insertions(+), 0 deletions(-)

lib/std/zig/AstGen.zig+6
...@@ -2876,6 +2876,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2876,6 +2876,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2876 .validate_array_init_ref_ty,2876 .validate_array_init_ref_ty,
2877 .array_init_elem_type,2877 .array_init_elem_type,
2878 .array_init_elem_ptr,2878 .array_init_elem_ptr,
2879 .restrict,
2879 => break :b false,2880 => break :b false,
28802881
2881 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {2882 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
...@@ -9547,6 +9548,11 @@ fn builtinCall(...@@ -9547,6 +9548,11 @@ fn builtinCall(
9547 });9548 });
9548 return rvalue(gz, ri, result, node);9549 return rvalue(gz, ri, result, node);
9549 },9550 },
9551 .Restrict => {
9552 const operand = try typeExpr(gz, scope, params[0]);
9553 const result = try gz.addUnNode(.restrict, operand, node);
9554 return rvalue(gz, ri, result, node);
9555 },
95509556
9551 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),9557 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
9552 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),9558 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
lib/std/zig/AstRlAnnotate.zig+1
...@@ -923,6 +923,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -923,6 +923,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
923 .work_item_id,923 .work_item_id,
924 .work_group_size,924 .work_group_size,
925 .work_group_id,925 .work_group_id,
926 .Restrict,
926 => {927 => {
927 _ = try astrl.expr(args[0], block, ResultInfo.type_only);928 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
928 return false;929 return false;
lib/std/zig/BuiltinFn.zig+8
...@@ -90,6 +90,7 @@ pub const Tag = enum {...@@ -90,6 +90,7 @@ pub const Tag = enum {
90 size_of,90 size_of,
91 splat,91 splat,
92 reduce,92 reduce,
93 Restrict,
93 src,94 src,
94 sqrt,95 sqrt,
95 sin,96 sin,
...@@ -795,6 +796,13 @@ pub const list = list: {...@@ -795,6 +796,13 @@ pub const list = list: {
795 .param_count = 2,796 .param_count = 2,
796 },797 },
797 },798 },
799 .{
800 "@Restrict",
801 .{
802 .tag = .Restrict,
803 .param_count = 1,
804 },
805 },
798 .{806 .{
799 "@src",807 "@src",
800 .{808 .{
lib/std/zig/Zir.zig+11
...@@ -1071,6 +1071,12 @@ pub const Inst = struct {...@@ -1071,6 +1071,12 @@ pub const Inst = struct {
1071 /// Uses the `un_node` field.1071 /// Uses the `un_node` field.
1072 restore_err_ret_index_fn_entry,1072 restore_err_ret_index_fn_entry,
10731073
1074 /// Creates a new restricted function pointer type based on the
1075 /// provided function pointer type.
1076 ///
1077 /// Uses the `un_node` field.
1078 restrict,
1079
1074 /// The ZIR instruction tag is one of the `Extended` ones.1080 /// The ZIR instruction tag is one of the `Extended` ones.
1075 /// Uses the `extended` union field.1081 /// Uses the `extended` union field.
1076 extended,1082 extended,
...@@ -1315,6 +1321,7 @@ pub const Inst = struct {...@@ -1315,6 +1321,7 @@ pub const Inst = struct {
1315 .validate_const,1321 .validate_const,
1316 .restore_err_ret_index_unconditional,1322 .restore_err_ret_index_unconditional,
1317 .restore_err_ret_index_fn_entry,1323 .restore_err_ret_index_fn_entry,
1324 .restrict,
1318 => false,1325 => false,
13191326
1320 .@"break",1327 .@"break",
...@@ -1595,6 +1602,7 @@ pub const Inst = struct {...@@ -1595,6 +1602,7 @@ pub const Inst = struct {
1595 .validate_array_init_ref_ty,1602 .validate_array_init_ref_ty,
1596 .array_init_elem_type,1603 .array_init_elem_type,
1597 .array_init_elem_ptr,1604 .array_init_elem_ptr,
1605 .restrict,
1598 => false,1606 => false,
15991607
1600 .extended => switch (data.extended.opcode) {1608 .extended => switch (data.extended.opcode) {
...@@ -1711,6 +1719,7 @@ pub const Inst = struct {...@@ -1711,6 +1719,7 @@ pub const Inst = struct {
1711 .merge_error_sets = .pl_node,1719 .merge_error_sets = .pl_node,
1712 .mod_rem = .pl_node,1720 .mod_rem = .pl_node,
1713 .ref = .un_tok,1721 .ref = .un_tok,
1722 .restrict = .un_node,
1714 .ret_node = .un_node,1723 .ret_node = .un_node,
1715 .ret_load = .un_node,1724 .ret_load = .un_node,
1716 .ret_implicit = .un_tok,1725 .ret_implicit = .un_tok,
...@@ -4755,6 +4764,8 @@ fn findTrackableInner(...@@ -4755,6 +4764,8 @@ fn findTrackableInner(
4755 try zir.findTrackableBody(gpa, contents, defers, body);4764 try zir.findTrackableBody(gpa, contents, defers, body);
4756 }4765 }
4757 },4766 },
4767 // Restricted function pointer types need tracking, but have no body.
4768 .restrict => return contents.other.append(gpa, inst),
4758 }4769 }
4759}4770}
47604771
src/InternPool.zig+41
...@@ -117,6 +117,13 @@ pub const empty: InternPool = .{...@@ -117,6 +117,13 @@ pub const empty: InternPool = .{
117 .free_dep_entries = .empty,117 .free_dep_entries = .empty,
118};118};
119119
120pub const RestrictedSetIndex = enum(u32) {
121 /// placeholder while I slowly work my way towards a more complete implementation
122 some = 0,
123 none = std.math.maxInt(u32),
124 _,
125};
126
120/// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole127/// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole
121/// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both128/// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both
122/// the file which the instruction lives in, and the instruction index itself, which is updated on129/// the file which the instruction lives in, and the instruction index itself, which is updated on
...@@ -2079,6 +2086,7 @@ pub const Key = union(enum) {...@@ -2079,6 +2086,7 @@ pub const Key = union(enum) {
2079 sentinel: Index = .none,2086 sentinel: Index = .none,
2080 flags: Flags = .{},2087 flags: Flags = .{},
2081 packed_offset: PackedOffset = .{ .bit_offset = 0, .host_size = 0 },2088 packed_offset: PackedOffset = .{ .bit_offset = 0, .host_size = 0 },
2089 restricted_set: RestrictedSetIndex = .none,
20822090
2083 pub const VectorIndex = enum(u16) {2091 pub const VectorIndex = enum(u16) {
2084 none = std.math.maxInt(u16),2092 none = std.math.maxInt(u16),
...@@ -5389,6 +5397,9 @@ pub const Tag = enum(u8) {...@@ -5389,6 +5397,9 @@ pub const Tag = enum(u8) {
5389 type_vector,5397 type_vector,
5390 /// A fully explicitly specified pointer type.5398 /// A fully explicitly specified pointer type.
5391 type_pointer,5399 type_pointer,
5400 /// A pointer type created by using the `@Restrict` builtin.
5401 /// data is `Index` of underlying, non-restrict pointer type.
5402 type_pointer_restricted,
5392 /// A slice type.5403 /// A slice type.
5393 /// data is Index of underlying pointer type.5404 /// data is Index of underlying pointer type.
5394 type_slice,5405 type_slice,
...@@ -5666,6 +5677,7 @@ pub const Tag = enum(u8) {...@@ -5666,6 +5677,7 @@ pub const Tag = enum(u8) {
5666 .type_array_small = .{ .summary = .@"[{.payload.len%value}]{.payload.child%summary}", .payload = Vector },5677 .type_array_small = .{ .summary = .@"[{.payload.len%value}]{.payload.child%summary}", .payload = Vector },
5667 .type_vector = .{ .summary = .@"@Vector({.payload.len%value}, {.payload.child%summary})", .payload = Vector },5678 .type_vector = .{ .summary = .@"@Vector({.payload.len%value}, {.payload.child%summary})", .payload = Vector },
5668 .type_pointer = .{ .summary = .@"*... {.payload.child%summary}", .payload = TypePointer },5679 .type_pointer = .{ .summary = .@"*... {.payload.child%summary}", .payload = TypePointer },
5680 .type_pointer_restricted = .{ .summary = .@"@Restrict(*... {.payload.child%summary})", .data = Index },
5669 .type_slice = .{ .summary = .@"[]... {.data.unwrapped.payload.child%summary}", .data = Index },5681 .type_slice = .{ .summary = .@"[]... {.data.unwrapped.payload.child%summary}", .data = Index },
5670 .type_optional = .{ .summary = .@"?{.data%summary}", .data = Index },5682 .type_optional = .{ .summary = .@"?{.data%summary}", .data = Index },
5671 .type_anyframe = .{ .summary = .@"anyframe->{.data%summary}", .data = Index },5683 .type_anyframe = .{ .summary = .@"anyframe->{.data%summary}", .data = Index },
...@@ -6970,6 +6982,16 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -6970,6 +6982,16 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
69706982
6971 .type_pointer => .{ .ptr_type = extraData(unwrapped_index.getExtra(ip), Tag.TypePointer, data) },6983 .type_pointer => .{ .ptr_type = extraData(unwrapped_index.getExtra(ip), Tag.TypePointer, data) },
69726984
6985 .type_pointer_restricted => {
6986 const child_ptr_index: Index = @enumFromInt(data);
6987 const child_ptr_unwrapped = child_ptr_index.unwrap(ip);
6988 const child_ptr_item = child_ptr_unwrapped.getItem(ip);
6989 assert(child_ptr_item.tag == .type_pointer);
6990 var ptr_info = extraData(child_ptr_unwrapped.getExtra(ip), Tag.TypePointer, child_ptr_item.data);
6991 ptr_info.restricted_set = .some;
6992 return .{ .ptr_type = ptr_info };
6993 },
6994
6973 .type_slice => {6995 .type_slice => {
6974 const many_ptr_index: Index = @enumFromInt(data);6996 const many_ptr_index: Index = @enumFromInt(data);
6975 const many_ptr_unwrapped = many_ptr_index.unwrap(ip);6997 const many_ptr_unwrapped = many_ptr_index.unwrap(ip);
...@@ -10388,6 +10410,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {...@@ -10388,6 +10410,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
10388 TrackedInst.Index,10410 TrackedInst.Index,
10389 TrackedInst.Index.Optional,10411 TrackedInst.Index.Optional,
10390 ComptimeAllocIndex,10412 ComptimeAllocIndex,
10413 RestrictedSetIndex,
10391 => @intFromEnum(@field(item, field.name)),10414 => @intFromEnum(@field(item, field.name)),
1039210415
10393 u32,10416 u32,
...@@ -10451,6 +10474,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat...@@ -10451,6 +10474,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
10451 TrackedInst.Index,10474 TrackedInst.Index,
10452 TrackedInst.Index.Optional,10475 TrackedInst.Index.Optional,
10453 ComptimeAllocIndex,10476 ComptimeAllocIndex,
10477 RestrictedSetIndex,
10454 => @enumFromInt(extra_item),10478 => @enumFromInt(extra_item),
1045510479
10456 u32,10480 u32,
...@@ -11092,6 +11116,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {...@@ -11092,6 +11116,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
11092 .type_array_big => @sizeOf(Array),11116 .type_array_big => @sizeOf(Array),
11093 .type_vector => @sizeOf(Vector),11117 .type_vector => @sizeOf(Vector),
11094 .type_pointer => @sizeOf(Tag.TypePointer),11118 .type_pointer => @sizeOf(Tag.TypePointer),
11119 .type_pointer_restricted => 0,
11095 .type_slice => 0,11120 .type_slice => 0,
11096 .type_optional => 0,11121 .type_optional => 0,
11097 .type_anyframe => 0,11122 .type_anyframe => 0,
...@@ -11319,6 +11344,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {...@@ -11319,6 +11344,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
11319 .type_array_big,11344 .type_array_big,
11320 .type_vector,11345 .type_vector,
11321 .type_pointer,11346 .type_pointer,
11347 .type_pointer_restricted,
11322 .type_optional,11348 .type_optional,
11323 .type_anyframe,11349 .type_anyframe,
11324 .type_error_union,11350 .type_error_union,
...@@ -11902,6 +11928,19 @@ pub fn getOrPutTrailingString(...@@ -11902,6 +11928,19 @@ pub fn getOrPutTrailingString(
11902 return value;11928 return value;
11903}11929}
1190411930
11931pub fn restrictedFunctionPointerType(
11932 ip: *InternPool,
11933 gpa: Allocator,
11934 tid: Zcu.PerThread.Id,
11935 fn_ty: Index,
11936) Allocator.Error!Index {
11937 _ = ip;
11938 _ = gpa;
11939 _ = tid;
11940 _ = fn_ty;
11941 @panic("TODO");
11942}
11943
11905pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {11944pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {
11906 const full_hash = Hash.hash(0, key);11945 const full_hash = Hash.hash(0, key);
11907 const hash: u32 = @truncate(full_hash >> 32);11946 const hash: u32 = @truncate(full_hash >> 32);
...@@ -12055,6 +12094,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {...@@ -12055,6 +12094,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
12055 .type_array_small,12094 .type_array_small,
12056 .type_vector,12095 .type_vector,
12057 .type_pointer,12096 .type_pointer,
12097 .type_pointer_restricted,
12058 .type_slice,12098 .type_slice,
12059 .type_optional,12099 .type_optional,
12060 .type_anyframe,12100 .type_anyframe,
...@@ -12411,6 +12451,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {...@@ -12411,6 +12451,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
12411 .type_vector => .vector,12451 .type_vector => .vector,
1241212452
12413 .type_pointer,12453 .type_pointer,
12454 .type_pointer_restricted,
12414 .type_slice,12455 .type_slice,
12415 => .pointer,12456 => .pointer,
1241612457
src/Sema.zig+22
...@@ -1305,6 +1305,7 @@ fn analyzeBodyInner(...@@ -1305,6 +1305,7 @@ fn analyzeBodyInner(
1305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),1305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
1306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),1306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
1307 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst),1307 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst),
1308 .restrict => try sema.zirRestrict(block, inst),
13081309
1309 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),1310 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
1310 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),1311 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
...@@ -4681,6 +4682,26 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -4681,6 +4682,26 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
4681 }4682 }
4682}4683}
46834684
4685fn zirRestrict(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4686 const pt = sema.pt;
4687 const zcu = pt.zcu;
4688
4689 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
4690 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
4691 const ptr_ty = try sema.resolveType(block, ty_src, inst_data.operand);
4692
4693 try sema.checkPtrOperand(block, ty_src, ptr_ty);
4694
4695 const ptr_info = ptr_ty.ptrInfo(zcu);
4696 const pointee_ty: Type = .fromInterned(ptr_info.child);
4697 if (ptr_info.flags.size != .one or pointee_ty.zigTypeTag(zcu) == .@"fn") {
4698 return sema.fail(block, ty_src, "expected function pointer type; found {f}", .{ptr_ty.fmt(pt)});
4699 }
4700
4701 const new_ty = try pt.restrictedFunctionPointerType(pointee_ty);
4702 return .fromType(new_ty);
4703}
4704
4684fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {4705fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
4685 const pt = sema.pt;4706 const pt = sema.pt;
4686 const zcu = pt.zcu;4707 const zcu = pt.zcu;
...@@ -36062,6 +36083,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -36062,6 +36083,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
36062 .type_int_signed, // i0 handled above36083 .type_int_signed, // i0 handled above
36063 .type_int_unsigned, // u0 handled above36084 .type_int_unsigned, // u0 handled above
36064 .type_pointer,36085 .type_pointer,
36086 .type_pointer_restricted,
36065 .type_slice,36087 .type_slice,
36066 .type_anyframe,36088 .type_anyframe,
36067 .type_error_union,36089 .type_error_union,
src/Zcu/PerThread.zig+6
...@@ -3420,6 +3420,8 @@ pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!...@@ -3420,6 +3420,8 @@ pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!
3420/// this because it requires potentially pushing to the job queue.3420/// this because it requires potentially pushing to the job queue.
3421pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value {3421pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value {
3422 const ip = &pt.zcu.intern_pool;3422 const ip = &pt.zcu.intern_pool;
3423 // TODO: avoid indexToKey
3424 // TODO: check if dest is restricted function pointer type
3423 switch (ip.indexToKey(val.toIntern())) {3425 switch (ip.indexToKey(val.toIntern())) {
3424 .@"extern" => |e| {3426 .@"extern" => |e| {
3425 const coerced = try pt.getExtern(.{3427 const coerced = try pt.getExtern(.{
...@@ -3544,6 +3546,10 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err...@@ -3544,6 +3546,10 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err
3544 return Type.fromInterned(try pt.zcu.intern_pool.getFuncType(pt.zcu.gpa, pt.tid, key));3546 return Type.fromInterned(try pt.zcu.intern_pool.getFuncType(pt.zcu.gpa, pt.tid, key));
3545}3547}
35463548
3549pub fn restrictedFunctionPointerType(pt: Zcu.PerThread, fn_ty: Type) Allocator.Error!Type {
3550 return .fromInterned(try pt.zcu.intern_pool.restrictedFunctionPointerType(pt.zcu.gpa, pt.tid, fn_ty.toIntern()));
3551}
3552
3547/// Use this for `anyframe->T` only.3553/// Use this for `anyframe->T` only.
3548/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.3554/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
3549pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {3555pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {
src/print_zir.zig+1
...@@ -266,6 +266,7 @@ const Writer = struct {...@@ -266,6 +266,7 @@ const Writer = struct {
266 .opt_eu_base_ptr_init,266 .opt_eu_base_ptr_init,
267 .restore_err_ret_index_unconditional,267 .restore_err_ret_index_unconditional,
268 .restore_err_ret_index_fn_entry,268 .restore_err_ret_index_fn_entry,
269 .restrict,
269 => try self.writeUnNode(stream, inst),270 => try self.writeUnNode(stream, inst),
270271
271 .ref,272 .ref,