Skip to content

Commit 3abf8a6

Browse files
Automatically collect parameter & return types of builtins
1 parent 0d77561 commit 3abf8a6

File tree

6 files changed

+110
-127
lines changed

6 files changed

+110
-127
lines changed

src/analysis.zig

Lines changed: 23 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub const DocumentScope = @import("DocumentScope.zig");
2323
pub const Declaration = DocumentScope.Declaration;
2424
pub const Scope = DocumentScope.Scope;
2525

26+
const version_data = @import("version_data");
27+
2628
const Analyser = @This();
2729

2830
gpa: std.mem.Allocator,
@@ -2118,10 +2120,6 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
21182120
return resolved_type.typeOf(analyser);
21192121
}
21202122

2121-
if (std.mem.eql(u8, call_name, "@typeInfo")) {
2122-
return analyser.instanceStdBuiltinType("Type");
2123-
}
2124-
21252123
const type_map: std.StaticStringMap(InternPool.Index) = .initComptime(.{
21262124
.{ "type", .type_type },
21272125
.{ "void", .void_type },
@@ -2212,20 +2210,10 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
22122210
return try analyser.resolveFieldAccess(lhs, field_name);
22132211
}
22142212

2215-
if (std.mem.eql(u8, call_name, "@src")) {
2216-
return analyser.instanceStdBuiltinType("SourceLocation");
2217-
}
2218-
22192213
if (std.mem.eql(u8, call_name, "@compileError")) {
22202214
return .{ .data = .{ .compile_error = node_handle }, .is_type_val = false };
22212215
}
22222216

2223-
if (std.mem.eql(u8, call_name, "@panic") or
2224-
std.mem.eql(u8, call_name, "@trap"))
2225-
{
2226-
return Type.fromIP(analyser, .noreturn_type, null);
2227-
}
2228-
22292217
if (std.mem.eql(u8, call_name, "@Vector")) {
22302218
if (params.len != 2) return null;
22312219

@@ -2249,6 +2237,17 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
22492237

22502238
return Type.fromIP(analyser, .type_type, vector_ty_ip_index);
22512239
}
2240+
2241+
if (version_data.builtins.get(call_name)) |data| {
2242+
const type_str = data.return_type;
2243+
if (try analyser.resolvePrimitive(type_str)) |primitive|
2244+
return Type.fromIP(analyser, primitive, null);
2245+
return analyser.instanceStdBuiltinType(blk: {
2246+
if (std.mem.startsWith(u8, type_str, "std.builtin."))
2247+
break :blk type_str["std.builtin.".len..];
2248+
break :blk type_str;
2249+
});
2250+
}
22522251
},
22532252
.fn_proto,
22542253
.fn_proto_multi,
@@ -5681,88 +5680,16 @@ pub fn resolveExpressionTypeFromAncestors(
56815680
return ty.instanceTypeVal(analyser);
56825681
}
56835682

5684-
if (std.mem.eql(u8, call_name, "@branchHint")) {
5685-
if (params.len == 0) return null;
5686-
if (params[0] != node) return null;
5687-
return analyser.instanceStdBuiltinType("BranchHint");
5688-
}
5689-
5690-
if (std.mem.eql(u8, call_name, "@atomicLoad")) {
5691-
if (params.len <= 2) return null;
5692-
if (params[2] != node) return null;
5693-
return analyser.instanceStdBuiltinType("AtomicOrder");
5694-
}
5695-
5696-
if (std.mem.eql(u8, call_name, "@atomicRmw")) {
5697-
if (params.len > 2 and params[2] == node)
5698-
return analyser.instanceStdBuiltinType("AtomicRmwOp");
5699-
if (params.len > 4 and params[4] == node)
5700-
return analyser.instanceStdBuiltinType("AtomicOrder");
5701-
return null;
5702-
}
5703-
5704-
if (std.mem.eql(u8, call_name, "@atomicStore")) {
5705-
if (params.len <= 3) return null;
5706-
if (params[3] != node) return null;
5707-
return analyser.instanceStdBuiltinType("AtomicOrder");
5708-
}
5709-
5710-
if (std.mem.eql(u8, call_name, "@cmpxchgStrong")) {
5711-
if (params.len > 4 and params[4] == node)
5712-
return analyser.instanceStdBuiltinType("AtomicOrder");
5713-
if (params.len > 5 and params[5] == node)
5714-
return analyser.instanceStdBuiltinType("AtomicOrder");
5715-
return null;
5716-
}
5717-
5718-
if (std.mem.eql(u8, call_name, "@cmpxchgWeak")) {
5719-
if (params.len > 4 and params[4] == node)
5720-
return analyser.instanceStdBuiltinType("AtomicOrder");
5721-
if (params.len > 5 and params[5] == node)
5722-
return analyser.instanceStdBuiltinType("AtomicOrder");
5723-
return null;
5724-
}
5725-
5726-
if (std.mem.eql(u8, call_name, "@call")) {
5727-
if (params.len == 0) return null;
5728-
if (params[0] != node) return null;
5729-
return analyser.instanceStdBuiltinType("CallModifier");
5730-
}
5731-
5732-
if (std.mem.eql(u8, call_name, "@export")) {
5733-
if (params.len <= 1) return null;
5734-
if (params[1] != node) return null;
5735-
return analyser.instanceStdBuiltinType("ExportOptions");
5736-
}
5737-
5738-
if (std.mem.eql(u8, call_name, "@extern")) {
5739-
if (params.len <= 1) return null;
5740-
if (params[1] != node) return null;
5741-
return analyser.instanceStdBuiltinType("ExternOptions");
5742-
}
5743-
5744-
if (std.mem.eql(u8, call_name, "@prefetch")) {
5745-
if (params.len <= 1) return null;
5746-
if (params[1] != node) return null;
5747-
return analyser.instanceStdBuiltinType("PrefetchOptions");
5748-
}
5749-
5750-
if (std.mem.eql(u8, call_name, "@reduce")) {
5751-
if (params.len == 0) return null;
5752-
if (params[0] != node) return null;
5753-
return analyser.instanceStdBuiltinType("ReduceOp");
5754-
}
5755-
5756-
if (std.mem.eql(u8, call_name, "@setFloatMode")) {
5757-
if (params.len == 0) return null;
5758-
if (params[0] != node) return null;
5759-
return analyser.instanceStdBuiltinType("FloatMode");
5760-
}
5761-
5762-
if (std.mem.eql(u8, call_name, "@Type")) {
5763-
if (params.len == 0) return null;
5764-
if (params[0] != node) return null;
5765-
return analyser.instanceStdBuiltinType("Type");
5683+
if (version_data.builtins.get(call_name)) |data| {
5684+
const index = std.mem.indexOfScalar(Ast.Node.Index, params, node) orelse return null;
5685+
if (index >= data.parameters.len) return null;
5686+
const parameter = data.parameters[index];
5687+
const type_str = parameter.type orelse return null;
5688+
return analyser.instanceStdBuiltinType(blk: {
5689+
if (std.mem.startsWith(u8, type_str, "std.builtin."))
5690+
break :blk type_str["std.builtin.".len..];
5691+
break :blk type_str;
5692+
});
57665693
}
57675694
},
57685695

src/features/completions.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ fn completeBuiltin(builder: *Builder) error{OutOfMemory}!void {
546546
.only_name => .{ name, .PlainText },
547547
.snippet => blk: {
548548
std.debug.assert(use_snippets);
549-
if (builtin.arguments.len == 0) break :blk .{ try std.fmt.allocPrint(builder.arena, "{s}()", .{name}), .PlainText };
549+
if (builtin.parameters.len == 0) break :blk .{ try std.fmt.allocPrint(builder.arena, "{s}()", .{name}), .PlainText };
550550
if (use_placeholders) break :blk .{ builtin.snippet, .Snippet };
551551
break :blk .{ try std.fmt.allocPrint(builder.arena, "{s}(${{1:}})", .{name}), .Snippet };
552552
},

src/features/inlay_hints.zig

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,26 +282,28 @@ fn writeCallHint(
282282
}
283283
}
284284

285-
/// takes parameter nodes from the ast and function parameter names from `Builtin.arguments` and writes parameter hints into `builder.hints`
286-
fn writeBuiltinHint(builder: *Builder, parameters: []const Ast.Node.Index, arguments: []const []const u8) !void {
285+
/// takes parameter nodes from the ast and function parameter names from `Builtin.parameters` and writes parameter hints into `builder.hints`
286+
fn writeBuiltinHint(builder: *Builder, parameters: []const Ast.Node.Index, params: []const data.Builtin.Parameter) !void {
287287
const tracy_zone = tracy.trace(@src());
288288
defer tracy_zone.end();
289289

290290
const handle = builder.handle;
291291
const tree = handle.tree;
292292

293-
const len = @min(arguments.len, parameters.len);
294-
for (arguments[0..len], parameters[0..len]) |arg, parameter| {
295-
if (arg.len == 0) continue;
293+
const len = @min(params.len, parameters.len);
294+
for (params[0..len], parameters[0..len]) |param, parameter| {
295+
const signature = param.signature;
296+
if (signature.len == 0) continue;
296297

297-
const colonIndex = std.mem.indexOfScalar(u8, arg, ':');
298-
const type_expr: []const u8 = if (colonIndex) |index| arg[index + 1 ..] else &.{};
298+
const colonIndex = std.mem.indexOfScalar(u8, signature, ':');
299+
const type_expr = param.type orelse "";
299300

301+
// TODO: parse noalias/comptime/label in config_gen.zig
300302
var maybe_label: ?[]const u8 = null;
301303
var no_alias = false;
302304
var comp_time = false;
303305

304-
var it = std.mem.splitScalar(u8, arg[0 .. colonIndex orelse arg.len], ' ');
306+
var it = std.mem.splitScalar(u8, signature[0 .. colonIndex orelse signature.len], ' ');
305307
while (it.next()) |item| {
306308
if (item.len == 0) continue;
307309
maybe_label = item;
@@ -518,7 +520,7 @@ fn writeNodeInlayHint(
518520
if (params.len == 0) return;
519521

520522
if (data.builtins.get(name)) |builtin| {
521-
try writeBuiltinHint(builder, params, builtin.arguments);
523+
try writeBuiltinHint(builder, params, builtin.parameters);
522524
}
523525
},
524526
.struct_init_one,

src/features/signature_help.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ pub fn getSignatureInfo(
193193
const builtin = data.builtins.get(tree.tokenSlice(expr_last_token)) orelse return null;
194194
const param_infos = try arena.alloc(
195195
types.ParameterInformation,
196-
builtin.arguments.len,
196+
builtin.parameters.len,
197197
);
198-
for (param_infos, builtin.arguments) |*info, argument| {
198+
for (param_infos, builtin.parameters) |*info, parameter| {
199199
info.* = .{
200-
.label = .{ .string = argument },
200+
.label = .{ .string = parameter.signature },
201201
.documentation = null,
202202
};
203203
}

src/tools/config_gen.zig

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -774,28 +774,57 @@ fn writeMarkdownFromHtmlInternal(html: []const u8, single_line: bool, depth: u32
774774
try writeLine(html[index..], single_line, writer);
775775
}
776776

777-
/// takes in a signature like this: `@intToEnum(comptime DestType: type, integer: anytype) DestType`
778-
/// and outputs its arguments: `comptime DestType: type`, `integer: anytype`
779-
fn extractArgumentsFromSignature(allocator: std.mem.Allocator, signature: []const u8) error{OutOfMemory}![][]const u8 {
780-
var arguments: std.ArrayListUnmanaged([]const u8) = .empty;
781-
defer arguments.deinit(allocator);
777+
const Parameter = struct {
778+
signature: []const u8,
779+
type: ?[]const u8,
780+
};
781+
782+
/// takes in a signature (without name or leading parenthesis) like this:
783+
/// `comptime DestType: type, integer: anytype) DestType`
784+
/// and outputs its parameters and return type:
785+
/// `comptime DestType: type`, `integer: anytype`, `DestType`
786+
fn extractParametersAndReturnTypeFromSignature(allocator: std.mem.Allocator, signature: []const u8) error{OutOfMemory}!struct { []Parameter, []const u8 } {
787+
var parameters: std.ArrayListUnmanaged(Parameter) = .empty;
788+
defer parameters.deinit(allocator);
782789

783790
var argument_start: usize = 0;
791+
var type_start: ?usize = null;
784792
var index: usize = 0;
785-
while (std.mem.indexOfAnyPos(u8, signature, index, ",()")) |token_index| {
793+
while (std.mem.indexOfAnyPos(u8, signature, index, ",():")) |token_index| {
786794
if (signature[token_index] == '(') {
787-
argument_start = index;
788-
index = 1 + std.mem.indexOfScalarPos(u8, signature, token_index + 1, ')').?;
795+
index = token_index + 1;
796+
var paren_depth: usize = 1;
797+
while (paren_depth > 0) : (index += 1) {
798+
switch (signature[index]) {
799+
'(' => paren_depth += 1,
800+
')' => paren_depth -= 1,
801+
else => {},
802+
}
803+
}
804+
continue;
805+
}
806+
if (signature[token_index] == ':') {
807+
std.debug.assert(signature[token_index + 1] == ' ');
808+
type_start = token_index + 2;
809+
index = token_index + 2;
789810
continue;
790811
}
791812
const argument = std.mem.trim(u8, signature[argument_start..token_index], &std.ascii.whitespace);
792-
if (argument.len != 0) try arguments.append(allocator, argument);
793-
if (signature[token_index] == ')') break;
813+
if (argument.len != 0) {
814+
try parameters.append(allocator, .{
815+
.signature = argument,
816+
.type = if (type_start) |i| std.mem.trim(u8, signature[i..token_index], &std.ascii.whitespace) else null,
817+
});
818+
}
794819
argument_start = token_index + 1;
795820
index = token_index + 1;
821+
type_start = null;
822+
if (signature[token_index] == ')') break;
796823
}
797824

798-
return arguments.toOwnedSlice(allocator);
825+
std.debug.assert(signature[index] == ' ');
826+
const return_type = signature[index + 1 ..];
827+
return .{ try parameters.toOwnedSlice(allocator), return_type };
799828
}
800829

801830
/// takes in a signature like this: `@intToEnum(comptime DestType: type, integer: anytype) DestType`
@@ -861,9 +890,15 @@ fn generateVersionDataFile(allocator: std.mem.Allocator, version: []const u8, ou
861890
\\
862891
\\pub const Builtin = struct {
863892
\\ signature: []const u8,
893+
\\ return_type: []const u8,
864894
\\ snippet: []const u8,
865895
\\ documentation: []const u8,
866-
\\ arguments: []const []const u8,
896+
\\ parameters: []const Parameter,
897+
\\
898+
\\ pub const Parameter = struct {
899+
\\ signature: []const u8,
900+
\\ type: ?[]const u8,
901+
\\ };
867902
\\};
868903
\\
869904
\\pub const builtins: std.StaticStringMap(Builtin) = .initComptime(&.{
@@ -877,19 +912,21 @@ fn generateVersionDataFile(allocator: std.mem.Allocator, version: []const u8, ou
877912
const snippet = try extractSnippetFromSignature(allocator, signature);
878913
defer allocator.free(snippet);
879914

880-
const arguments = try extractArgumentsFromSignature(allocator, signature[builtin.name.len + 1 ..]);
881-
defer allocator.free(arguments);
915+
const parameters, const return_type = try extractParametersAndReturnTypeFromSignature(allocator, signature[builtin.name.len + 1 ..]);
916+
defer allocator.free(parameters);
882917

883918
try writer.print(
884919
\\ .{{
885920
\\ "{}",
886921
\\ Builtin{{
887922
\\ .signature = "{}",
923+
\\ .return_type = "{}",
888924
\\ .snippet = "{}",
889925
\\
890926
, .{
891927
std.zig.fmtEscapes(builtin.name),
892928
std.zig.fmtEscapes(signature),
929+
std.zig.fmtEscapes(return_type),
893930
std.zig.fmtEscapes(snippet),
894931
});
895932

@@ -906,13 +943,23 @@ fn generateVersionDataFile(allocator: std.mem.Allocator, version: []const u8, ou
906943

907944
try writer.writeAll(
908945
\\ ,
909-
\\ .arguments = &[_][]const u8{
946+
\\ .parameters = &[_]Builtin.Parameter{
910947
);
911948

912-
if (arguments.len != 0) {
949+
if (parameters.len != 0) {
913950
try writer.writeByte('\n');
914-
for (arguments) |arg| {
915-
try writer.print(" \"{}\",\n", .{std.zig.fmtEscapes(arg)});
951+
for (parameters) |param| {
952+
try writer.print(
953+
\\ .{{
954+
\\ .signature = "{}",
955+
\\
956+
, .{std.zig.fmtEscapes(param.signature)});
957+
if (param.type) |t| {
958+
try writer.print(" .type = \"{}\",\n", .{std.zig.fmtEscapes(t)});
959+
} else {
960+
try writer.writeAll(" .type = null,\n");
961+
}
962+
try writer.writeAll(" },\n");
916963
}
917964
try writer.writeAll(" },\n");
918965
} else {

tests/analysis/builtins.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ const type_null: @Type(.null) = null;
135135
const type_enum_literal: @Type(.enum_literal) = .foo;
136136
// ^^^^^^^^^^^^^^^^^ (@Type(.enum_literal))()
137137

138+
const type_info = @typeInfo(u8);
139+
// ^^^^^^^^^ (Type)()
140+
138141
comptime {
139142
// Use @compileLog to verify the expected type with the compiler
140143
// @compileLog(vector_builtin_13);
@@ -170,4 +173,8 @@ fn builtin_calls() void {
170173
// ^^^^^^^ (FloatMode)()
171174
@Type(.type);
172175
// ^^^^^ (void)()
176+
177+
const src = @src();
178+
// ^^^ (SourceLocation)()
179+
_ = src;
173180
}

0 commit comments

Comments
 (0)