compiler: introduce @Restrict builtin

conservative, incomplete change
This commit is contained in:
Andrew Kelley 2025-08-31 20:40:15 -07:00
parent fc23fe90ce
commit f250802ce7
8 changed files with 96 additions and 0 deletions

View file

@ -2876,6 +2876,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.validate_array_init_ref_ty, .validate_array_init_ref_ty,
.array_init_elem_type, .array_init_elem_type,
.array_init_elem_ptr, .array_init_elem_ptr,
.restrict,
=> break :b false, => break :b false,
.extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) { .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
@ -9547,6 +9548,11 @@ fn builtinCall(
}); });
return rvalue(gz, ri, result, node); return rvalue(gz, ri, result, node);
}, },
.Restrict => {
const operand = try typeExpr(gz, scope, params[0]);
const result = try gz.addUnNode(.restrict, operand, node);
return rvalue(gz, ri, result, node);
},
.add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow), .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
.sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow), .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),

View file

@ -923,6 +923,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,
.work_group_id, .work_group_id,
.Restrict,
=> { => {
_ = try astrl.expr(args[0], block, ResultInfo.type_only); _ = try astrl.expr(args[0], block, ResultInfo.type_only);
return false; return false;

View file

@ -90,6 +90,7 @@ pub const Tag = enum {
size_of, size_of,
splat, splat,
reduce, reduce,
Restrict,
src, src,
sqrt, sqrt,
sin, sin,
@ -795,6 +796,13 @@ pub const list = list: {
.param_count = 2, .param_count = 2,
}, },
}, },
.{
"@Restrict",
.{
.tag = .Restrict,
.param_count = 1,
},
},
.{ .{
"@src", "@src",
.{ .{

View file

@ -1071,6 +1071,12 @@ pub const Inst = struct {
/// Uses the `un_node` field. /// Uses the `un_node` field.
restore_err_ret_index_fn_entry, restore_err_ret_index_fn_entry,
/// Creates a new restricted function pointer type based on the
/// provided function pointer type.
///
/// Uses the `un_node` field.
restrict,
/// The ZIR instruction tag is one of the `Extended` ones. /// The ZIR instruction tag is one of the `Extended` ones.
/// Uses the `extended` union field. /// Uses the `extended` union field.
extended, extended,
@ -1315,6 +1321,7 @@ pub const Inst = struct {
.validate_const, .validate_const,
.restore_err_ret_index_unconditional, .restore_err_ret_index_unconditional,
.restore_err_ret_index_fn_entry, .restore_err_ret_index_fn_entry,
.restrict,
=> false, => false,
.@"break", .@"break",
@ -1595,6 +1602,7 @@ pub const Inst = struct {
.validate_array_init_ref_ty, .validate_array_init_ref_ty,
.array_init_elem_type, .array_init_elem_type,
.array_init_elem_ptr, .array_init_elem_ptr,
.restrict,
=> false, => false,
.extended => switch (data.extended.opcode) { .extended => switch (data.extended.opcode) {
@ -1711,6 +1719,7 @@ pub const Inst = struct {
.merge_error_sets = .pl_node, .merge_error_sets = .pl_node,
.mod_rem = .pl_node, .mod_rem = .pl_node,
.ref = .un_tok, .ref = .un_tok,
.restrict = .un_node,
.ret_node = .un_node, .ret_node = .un_node,
.ret_load = .un_node, .ret_load = .un_node,
.ret_implicit = .un_tok, .ret_implicit = .un_tok,
@ -4755,6 +4764,8 @@ fn findTrackableInner(
try zir.findTrackableBody(gpa, contents, defers, body); try zir.findTrackableBody(gpa, contents, defers, body);
} }
}, },
// Restricted function pointer types need tracking, but have no body.
.restrict => return contents.other.append(gpa, inst),
} }
} }

View file

@ -117,6 +117,13 @@ pub const empty: InternPool = .{
.free_dep_entries = .empty, .free_dep_entries = .empty,
}; };
pub const RestrictedSetIndex = enum(u32) {
/// placeholder while I slowly work my way towards a more complete implementation
some = 0,
none = std.math.maxInt(u32),
_,
};
/// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole /// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole
/// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both /// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both
/// the file which the instruction lives in, and the instruction index itself, which is updated on /// 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) {
sentinel: Index = .none, sentinel: Index = .none,
flags: Flags = .{}, flags: Flags = .{},
packed_offset: PackedOffset = .{ .bit_offset = 0, .host_size = 0 }, packed_offset: PackedOffset = .{ .bit_offset = 0, .host_size = 0 },
restricted_set: RestrictedSetIndex = .none,
pub const VectorIndex = enum(u16) { pub const VectorIndex = enum(u16) {
none = std.math.maxInt(u16), none = std.math.maxInt(u16),
@ -5389,6 +5397,9 @@ pub const Tag = enum(u8) {
type_vector, type_vector,
/// A fully explicitly specified pointer type. /// A fully explicitly specified pointer type.
type_pointer, type_pointer,
/// A pointer type created by using the `@Restrict` builtin.
/// data is `Index` of underlying, non-restrict pointer type.
type_pointer_restricted,
/// A slice type. /// A slice type.
/// data is Index of underlying pointer type. /// data is Index of underlying pointer type.
type_slice, type_slice,
@ -5666,6 +5677,7 @@ pub const Tag = enum(u8) {
.type_array_small = .{ .summary = .@"[{.payload.len%value}]{.payload.child%summary}", .payload = Vector }, .type_array_small = .{ .summary = .@"[{.payload.len%value}]{.payload.child%summary}", .payload = Vector },
.type_vector = .{ .summary = .@"@Vector({.payload.len%value}, {.payload.child%summary})", .payload = Vector }, .type_vector = .{ .summary = .@"@Vector({.payload.len%value}, {.payload.child%summary})", .payload = Vector },
.type_pointer = .{ .summary = .@"*... {.payload.child%summary}", .payload = TypePointer }, .type_pointer = .{ .summary = .@"*... {.payload.child%summary}", .payload = TypePointer },
.type_pointer_restricted = .{ .summary = .@"@Restrict(*... {.payload.child%summary})", .data = Index },
.type_slice = .{ .summary = .@"[]... {.data.unwrapped.payload.child%summary}", .data = Index }, .type_slice = .{ .summary = .@"[]... {.data.unwrapped.payload.child%summary}", .data = Index },
.type_optional = .{ .summary = .@"?{.data%summary}", .data = Index }, .type_optional = .{ .summary = .@"?{.data%summary}", .data = Index },
.type_anyframe = .{ .summary = .@"anyframe->{.data%summary}", .data = Index }, .type_anyframe = .{ .summary = .@"anyframe->{.data%summary}", .data = Index },
@ -6970,6 +6982,16 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.type_pointer => .{ .ptr_type = extraData(unwrapped_index.getExtra(ip), Tag.TypePointer, data) }, .type_pointer => .{ .ptr_type = extraData(unwrapped_index.getExtra(ip), Tag.TypePointer, data) },
.type_pointer_restricted => {
const child_ptr_index: Index = @enumFromInt(data);
const child_ptr_unwrapped = child_ptr_index.unwrap(ip);
const child_ptr_item = child_ptr_unwrapped.getItem(ip);
assert(child_ptr_item.tag == .type_pointer);
var ptr_info = extraData(child_ptr_unwrapped.getExtra(ip), Tag.TypePointer, child_ptr_item.data);
ptr_info.restricted_set = .some;
return .{ .ptr_type = ptr_info };
},
.type_slice => { .type_slice => {
const many_ptr_index: Index = @enumFromInt(data); const many_ptr_index: Index = @enumFromInt(data);
const many_ptr_unwrapped = many_ptr_index.unwrap(ip); const many_ptr_unwrapped = many_ptr_index.unwrap(ip);
@ -10388,6 +10410,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
TrackedInst.Index, TrackedInst.Index,
TrackedInst.Index.Optional, TrackedInst.Index.Optional,
ComptimeAllocIndex, ComptimeAllocIndex,
RestrictedSetIndex,
=> @intFromEnum(@field(item, field.name)), => @intFromEnum(@field(item, field.name)),
u32, u32,
@ -10451,6 +10474,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
TrackedInst.Index, TrackedInst.Index,
TrackedInst.Index.Optional, TrackedInst.Index.Optional,
ComptimeAllocIndex, ComptimeAllocIndex,
RestrictedSetIndex,
=> @enumFromInt(extra_item), => @enumFromInt(extra_item),
u32, u32,
@ -11092,6 +11116,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
.type_array_big => @sizeOf(Array), .type_array_big => @sizeOf(Array),
.type_vector => @sizeOf(Vector), .type_vector => @sizeOf(Vector),
.type_pointer => @sizeOf(Tag.TypePointer), .type_pointer => @sizeOf(Tag.TypePointer),
.type_pointer_restricted => 0,
.type_slice => 0, .type_slice => 0,
.type_optional => 0, .type_optional => 0,
.type_anyframe => 0, .type_anyframe => 0,
@ -11319,6 +11344,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
.type_array_big, .type_array_big,
.type_vector, .type_vector,
.type_pointer, .type_pointer,
.type_pointer_restricted,
.type_optional, .type_optional,
.type_anyframe, .type_anyframe,
.type_error_union, .type_error_union,
@ -11902,6 +11928,19 @@ pub fn getOrPutTrailingString(
return value; return value;
} }
pub fn restrictedFunctionPointerType(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
fn_ty: Index,
) Allocator.Error!Index {
_ = ip;
_ = gpa;
_ = tid;
_ = fn_ty;
@panic("TODO");
}
pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString { pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {
const full_hash = Hash.hash(0, key); const full_hash = Hash.hash(0, key);
const hash: u32 = @truncate(full_hash >> 32); const hash: u32 = @truncate(full_hash >> 32);
@ -12055,6 +12094,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.type_array_small, .type_array_small,
.type_vector, .type_vector,
.type_pointer, .type_pointer,
.type_pointer_restricted,
.type_slice, .type_slice,
.type_optional, .type_optional,
.type_anyframe, .type_anyframe,
@ -12411,6 +12451,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.type_vector => .vector, .type_vector => .vector,
.type_pointer, .type_pointer,
.type_pointer_restricted,
.type_slice, .type_slice,
=> .pointer, => .pointer,

View file

@ -1305,6 +1305,7 @@ fn analyzeBodyInner(
.validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
.opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
.coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst),
.restrict => try sema.zirRestrict(block, inst),
.clz => try sema.zirBitCount(block, inst, .clz, Value.clz), .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
.ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
@ -4681,6 +4682,26 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
} }
} }
fn zirRestrict(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
const ptr_ty = try sema.resolveType(block, ty_src, inst_data.operand);
try sema.checkPtrOperand(block, ty_src, ptr_ty);
const ptr_info = ptr_ty.ptrInfo(zcu);
const pointee_ty: Type = .fromInterned(ptr_info.child);
if (ptr_info.flags.size != .one or pointee_ty.zigTypeTag(zcu) == .@"fn") {
return sema.fail(block, ty_src, "expected function pointer type; found {f}", .{ptr_ty.fmt(pt)});
}
const new_ty = try pt.restrictedFunctionPointerType(pointee_ty);
return .fromType(new_ty);
}
fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
const pt = sema.pt; const pt = sema.pt;
const zcu = pt.zcu; const zcu = pt.zcu;
@ -36062,6 +36083,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
.type_int_signed, // i0 handled above .type_int_signed, // i0 handled above
.type_int_unsigned, // u0 handled above .type_int_unsigned, // u0 handled above
.type_pointer, .type_pointer,
.type_pointer_restricted,
.type_slice, .type_slice,
.type_anyframe, .type_anyframe,
.type_error_union, .type_error_union,

View file

@ -3420,6 +3420,8 @@ pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!
/// this because it requires potentially pushing to the job queue. /// this because it requires potentially pushing to the job queue.
pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value { pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value {
const ip = &pt.zcu.intern_pool; const ip = &pt.zcu.intern_pool;
// TODO: avoid indexToKey
// TODO: check if dest is restricted function pointer type
switch (ip.indexToKey(val.toIntern())) { switch (ip.indexToKey(val.toIntern())) {
.@"extern" => |e| { .@"extern" => |e| {
const coerced = try pt.getExtern(.{ const coerced = try pt.getExtern(.{
@ -3544,6 +3546,10 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err
return Type.fromInterned(try pt.zcu.intern_pool.getFuncType(pt.zcu.gpa, pt.tid, key)); return Type.fromInterned(try pt.zcu.intern_pool.getFuncType(pt.zcu.gpa, pt.tid, key));
} }
pub fn restrictedFunctionPointerType(pt: Zcu.PerThread, fn_ty: Type) Allocator.Error!Type {
return .fromInterned(try pt.zcu.intern_pool.restrictedFunctionPointerType(pt.zcu.gpa, pt.tid, fn_ty.toIntern()));
}
/// Use this for `anyframe->T` only. /// Use this for `anyframe->T` only.
/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly. /// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type { pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {

View file

@ -266,6 +266,7 @@ const Writer = struct {
.opt_eu_base_ptr_init, .opt_eu_base_ptr_init,
.restore_err_ret_index_unconditional, .restore_err_ret_index_unconditional,
.restore_err_ret_index_fn_entry, .restore_err_ret_index_fn_entry,
.restrict,
=> try self.writeUnNode(stream, inst), => try self.writeUnNode(stream, inst),
.ref, .ref,