|
|
|
|
@@ -566,10 +566,10 @@ pub const Lowering = struct {
|
|
|
|
|
switch (decl.data) {
|
|
|
|
|
.const_decl => |cd| {
|
|
|
|
|
out.put(cd.name, {}) catch {};
|
|
|
|
|
if (cd.value.data == .fn_decl) self.collectBodyDeclNames(cd.value.data.fn_decl.body, out);
|
|
|
|
|
if (cd.value.data == .fn_decl) self.harvestScopeDecls(cd.value.data.fn_decl.body, out);
|
|
|
|
|
},
|
|
|
|
|
.struct_decl => |sd| out.put(sd.name, {}) catch {},
|
|
|
|
|
.fn_decl => |fd| self.collectBodyDeclNames(fd.body, out),
|
|
|
|
|
.fn_decl => |fd| self.harvestScopeDecls(fd.body, out),
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -585,73 +585,88 @@ pub const Lowering = struct {
|
|
|
|
|
while (it_al.next()) |k| out.put(k.*, {}) catch {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Collect names declared inside a function body — local `T :: struct/enum/
|
|
|
|
|
/// union` types and named consts — so a body-level type annotation
|
|
|
|
|
/// referencing one isn't flagged. Recurses control-flow bodies but stops at
|
|
|
|
|
/// nested function / closure boundaries (those have their own scope and are
|
|
|
|
|
/// not body-checked).
|
|
|
|
|
fn collectBodyDeclNames(self: *Lowering, node: *const Node, out: *std.StringHashMap(void)) void {
|
|
|
|
|
/// Harvest every type-declaration name (local `T :: struct/enum/union` and
|
|
|
|
|
/// named consts) anywhere in a function body — including inside nested
|
|
|
|
|
/// closure / function bodies — into the global declared set, so a type
|
|
|
|
|
/// annotation in any scope that references one isn't flagged. Over-collection
|
|
|
|
|
/// is safe: it only ever relaxes the unknown-type check, never tightens it.
|
|
|
|
|
fn harvestScopeDecls(self: *Lowering, node: *const Node, out: *std.StringHashMap(void)) void {
|
|
|
|
|
switch (node.data) {
|
|
|
|
|
.block => |b| for (b.stmts) |s| self.collectBodyDeclNames(s, out),
|
|
|
|
|
.block => |b| for (b.stmts) |s| self.harvestScopeDecls(s, out),
|
|
|
|
|
.if_expr => |ie| {
|
|
|
|
|
self.collectBodyDeclNames(ie.then_branch, out);
|
|
|
|
|
if (ie.else_branch) |e| self.collectBodyDeclNames(e, out);
|
|
|
|
|
self.harvestScopeDecls(ie.condition, out);
|
|
|
|
|
self.harvestScopeDecls(ie.then_branch, out);
|
|
|
|
|
if (ie.else_branch) |e| self.harvestScopeDecls(e, out);
|
|
|
|
|
},
|
|
|
|
|
.while_expr => |we| self.collectBodyDeclNames(we.body, out),
|
|
|
|
|
.for_expr => |fe| self.collectBodyDeclNames(fe.body, out),
|
|
|
|
|
.match_expr => |me| for (me.arms) |arm| self.collectBodyDeclNames(arm.body, out),
|
|
|
|
|
.push_stmt => |ps| self.collectBodyDeclNames(ps.body, out),
|
|
|
|
|
.defer_stmt => |ds| self.collectBodyDeclNames(ds.expr, out),
|
|
|
|
|
.onfail_stmt => |os| self.collectBodyDeclNames(os.body, out),
|
|
|
|
|
.while_expr => |we| {
|
|
|
|
|
self.harvestScopeDecls(we.condition, out);
|
|
|
|
|
self.harvestScopeDecls(we.body, out);
|
|
|
|
|
},
|
|
|
|
|
.for_expr => |fe| {
|
|
|
|
|
self.harvestScopeDecls(fe.iterable, out);
|
|
|
|
|
if (fe.range_end) |re| self.harvestScopeDecls(re, out);
|
|
|
|
|
self.harvestScopeDecls(fe.body, out);
|
|
|
|
|
},
|
|
|
|
|
.match_expr => |me| {
|
|
|
|
|
self.harvestScopeDecls(me.subject, out);
|
|
|
|
|
for (me.arms) |arm| self.harvestScopeDecls(arm.body, out);
|
|
|
|
|
},
|
|
|
|
|
.push_stmt => |ps| {
|
|
|
|
|
self.harvestScopeDecls(ps.context_expr, out);
|
|
|
|
|
self.harvestScopeDecls(ps.body, out);
|
|
|
|
|
},
|
|
|
|
|
.defer_stmt => |ds| self.harvestScopeDecls(ds.expr, out),
|
|
|
|
|
.onfail_stmt => |os| self.harvestScopeDecls(os.body, out),
|
|
|
|
|
.return_stmt => |r| if (r.value) |v| self.harvestScopeDecls(v, out),
|
|
|
|
|
.raise_stmt => |rs| self.harvestScopeDecls(rs.tag, out),
|
|
|
|
|
.assignment => |a| {
|
|
|
|
|
self.harvestScopeDecls(a.value, out);
|
|
|
|
|
self.harvestScopeDecls(a.target, out);
|
|
|
|
|
},
|
|
|
|
|
.multi_assign => |ma| for (ma.values) |v| self.harvestScopeDecls(v, out),
|
|
|
|
|
.destructure_decl => |dd| self.harvestScopeDecls(dd.value, out),
|
|
|
|
|
.var_decl => |vd| if (vd.value) |v| self.harvestScopeDecls(v, out),
|
|
|
|
|
.const_decl => |cd| {
|
|
|
|
|
out.put(cd.name, {}) catch {};
|
|
|
|
|
self.collectBodyDeclNames(cd.value, out);
|
|
|
|
|
self.harvestScopeDecls(cd.value, out);
|
|
|
|
|
},
|
|
|
|
|
.var_decl => |vd| if (vd.value) |v| self.collectBodyDeclNames(v, out),
|
|
|
|
|
.struct_decl => |sd| out.put(sd.name, {}) catch {},
|
|
|
|
|
.enum_decl => |ed| out.put(ed.name, {}) catch {},
|
|
|
|
|
.union_decl => |ud| out.put(ud.name, {}) catch {},
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Walk a function body checking type annotations on local var / const
|
|
|
|
|
/// declarations (and body-local struct fields). `in_scope` and `type_vals`
|
|
|
|
|
/// carry the enclosing function's generic params / value-`Type` params.
|
|
|
|
|
/// Recurses control-flow and decl-value blocks (so `x := if c { a: T; a }`
|
|
|
|
|
/// is reached) but not nested function / closure bodies.
|
|
|
|
|
fn checkBodyTypes(
|
|
|
|
|
self: *Lowering,
|
|
|
|
|
node: *const Node,
|
|
|
|
|
declared: *std.StringHashMap(void),
|
|
|
|
|
in_scope: []const ast.StructTypeParam,
|
|
|
|
|
type_vals: []const []const u8,
|
|
|
|
|
) void {
|
|
|
|
|
switch (node.data) {
|
|
|
|
|
.block => |b| for (b.stmts) |s| self.checkBodyTypes(s, declared, in_scope, type_vals),
|
|
|
|
|
.if_expr => |ie| {
|
|
|
|
|
self.checkBodyTypes(ie.then_branch, declared, in_scope, type_vals);
|
|
|
|
|
if (ie.else_branch) |e| self.checkBodyTypes(e, declared, in_scope, type_vals);
|
|
|
|
|
.call => |c| {
|
|
|
|
|
self.harvestScopeDecls(c.callee, out);
|
|
|
|
|
for (c.args) |a| self.harvestScopeDecls(a, out);
|
|
|
|
|
},
|
|
|
|
|
.while_expr => |we| self.checkBodyTypes(we.body, declared, in_scope, type_vals),
|
|
|
|
|
.for_expr => |fe| self.checkBodyTypes(fe.body, declared, in_scope, type_vals),
|
|
|
|
|
.match_expr => |me| for (me.arms) |arm| self.checkBodyTypes(arm.body, declared, in_scope, type_vals),
|
|
|
|
|
.push_stmt => |ps| self.checkBodyTypes(ps.body, declared, in_scope, type_vals),
|
|
|
|
|
.defer_stmt => |ds| self.checkBodyTypes(ds.expr, declared, in_scope, type_vals),
|
|
|
|
|
.onfail_stmt => |os| self.checkBodyTypes(os.body, declared, in_scope, type_vals),
|
|
|
|
|
.var_decl => |vd| {
|
|
|
|
|
if (vd.type_annotation) |ta| self.checkTypeNodeForUnknown(ta, declared, in_scope, type_vals);
|
|
|
|
|
if (vd.value) |v| self.checkBodyTypes(v, declared, in_scope, type_vals);
|
|
|
|
|
.binary_op => |b| {
|
|
|
|
|
self.harvestScopeDecls(b.lhs, out);
|
|
|
|
|
self.harvestScopeDecls(b.rhs, out);
|
|
|
|
|
},
|
|
|
|
|
.const_decl => |cd| {
|
|
|
|
|
if (cd.type_annotation) |ta| self.checkTypeNodeForUnknown(ta, declared, in_scope, type_vals);
|
|
|
|
|
self.checkBodyTypes(cd.value, declared, in_scope, type_vals);
|
|
|
|
|
.unary_op => |u| self.harvestScopeDecls(u.operand, out),
|
|
|
|
|
.field_access => |fa| self.harvestScopeDecls(fa.object, out),
|
|
|
|
|
.index_expr => |ix| {
|
|
|
|
|
self.harvestScopeDecls(ix.object, out);
|
|
|
|
|
self.harvestScopeDecls(ix.index, out);
|
|
|
|
|
},
|
|
|
|
|
.assignment => |a| self.checkBodyTypes(a.value, declared, in_scope, type_vals),
|
|
|
|
|
.return_stmt => |r| if (r.value) |v| self.checkBodyTypes(v, declared, in_scope, type_vals),
|
|
|
|
|
.struct_decl => |sd| if (sd.type_params.len == 0) {
|
|
|
|
|
for (sd.field_types) |ft| self.checkTypeNodeForUnknown(ft, declared, in_scope, type_vals);
|
|
|
|
|
.struct_literal => |sl| {
|
|
|
|
|
for (sl.field_inits) |fi| self.harvestScopeDecls(fi.value, out);
|
|
|
|
|
if (sl.init_block) |ib| self.harvestScopeDecls(ib, out);
|
|
|
|
|
},
|
|
|
|
|
.array_literal => |al| for (al.elements) |e| self.harvestScopeDecls(e, out),
|
|
|
|
|
.force_unwrap => |fu| self.harvestScopeDecls(fu.operand, out),
|
|
|
|
|
.null_coalesce => |nc| {
|
|
|
|
|
self.harvestScopeDecls(nc.lhs, out);
|
|
|
|
|
self.harvestScopeDecls(nc.rhs, out);
|
|
|
|
|
},
|
|
|
|
|
.deref_expr => |de| self.harvestScopeDecls(de.operand, out),
|
|
|
|
|
.try_expr => |te| self.harvestScopeDecls(te.operand, out),
|
|
|
|
|
.catch_expr => |ce| {
|
|
|
|
|
self.harvestScopeDecls(ce.operand, out);
|
|
|
|
|
self.harvestScopeDecls(ce.body, out);
|
|
|
|
|
},
|
|
|
|
|
.comptime_expr => |ce| self.harvestScopeDecls(ce.expr, out),
|
|
|
|
|
.spread_expr => |se| self.harvestScopeDecls(se.operand, out),
|
|
|
|
|
.lambda => |lm| self.harvestScopeDecls(lm.body, out),
|
|
|
|
|
.fn_decl => |fd| self.harvestScopeDecls(fd.body, out),
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -664,11 +679,36 @@ pub const Lowering = struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn checkFnSignatureTypes(self: *Lowering, fd: *const ast.FnDecl, declared: *std.StringHashMap(void)) void {
|
|
|
|
|
// Value params declared `: Type` (no `$`) — using one in a type
|
|
|
|
|
// position is the issue-0064 misuse; surface a tailored hint.
|
|
|
|
|
var in_scope = std.ArrayList(ast.StructTypeParam).empty;
|
|
|
|
|
defer in_scope.deinit(self.alloc);
|
|
|
|
|
var type_vals = std.ArrayList([]const u8).empty;
|
|
|
|
|
defer type_vals.deinit(self.alloc);
|
|
|
|
|
for (fd.params) |p| {
|
|
|
|
|
self.checkScope(fd.type_params, fd.params, fd.return_type, fd.body, declared, &in_scope, &type_vals);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check one function/closure scope: its generic params (`$T`) and value-
|
|
|
|
|
/// `Type` params become in-scope (accumulated onto the parent's, so a nested
|
|
|
|
|
/// closure still sees the outer function's `$T`), its param/return
|
|
|
|
|
/// annotations are checked, then its body is walked. The scope additions are
|
|
|
|
|
/// popped on return.
|
|
|
|
|
fn checkScope(
|
|
|
|
|
self: *Lowering,
|
|
|
|
|
type_params: []const ast.StructTypeParam,
|
|
|
|
|
params: []const ast.Param,
|
|
|
|
|
return_type: ?*Node,
|
|
|
|
|
body: *const Node,
|
|
|
|
|
declared: *std.StringHashMap(void),
|
|
|
|
|
in_scope: *std.ArrayList(ast.StructTypeParam),
|
|
|
|
|
type_vals: *std.ArrayList([]const u8),
|
|
|
|
|
) void {
|
|
|
|
|
const save_s = in_scope.items.len;
|
|
|
|
|
const save_v = type_vals.items.len;
|
|
|
|
|
defer in_scope.shrinkRetainingCapacity(save_s);
|
|
|
|
|
defer type_vals.shrinkRetainingCapacity(save_v);
|
|
|
|
|
for (type_params) |tp| in_scope.append(self.alloc, tp) catch {};
|
|
|
|
|
// Value params declared `: Type` (no `$`) — using one in a type position
|
|
|
|
|
// is the issue-0064 misuse; track them for the tailored hint.
|
|
|
|
|
for (params) |p| {
|
|
|
|
|
if (p.type_expr.data == .type_expr) {
|
|
|
|
|
const cn = p.type_expr.data.type_expr.name;
|
|
|
|
|
if (std.mem.eql(u8, cn, "Type") or std.mem.eql(u8, cn, "type")) {
|
|
|
|
|
@@ -676,9 +716,129 @@ pub const Lowering = struct {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (fd.params) |p| self.checkTypeNodeForUnknown(p.type_expr, declared, fd.type_params, type_vals.items);
|
|
|
|
|
if (fd.return_type) |rt| self.checkTypeNodeForUnknown(rt, declared, fd.type_params, type_vals.items);
|
|
|
|
|
self.checkBodyTypes(fd.body, declared, fd.type_params, type_vals.items);
|
|
|
|
|
for (params) |p| self.checkTypeNodeForUnknown(p.type_expr, declared, in_scope.items, type_vals.items);
|
|
|
|
|
if (return_type) |rt| self.checkTypeNodeForUnknown(rt, declared, in_scope.items, type_vals.items);
|
|
|
|
|
self.walkBodyTypes(body, declared, in_scope, type_vals);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Walk a scope body checking type annotations on local var / const
|
|
|
|
|
/// declarations (and body-local struct fields), descending control flow and
|
|
|
|
|
/// expressions. Nested closure / function literals re-enter via `checkScope`
|
|
|
|
|
/// with their own params added to `in_scope`.
|
|
|
|
|
fn walkBodyTypes(
|
|
|
|
|
self: *Lowering,
|
|
|
|
|
node: *const Node,
|
|
|
|
|
declared: *std.StringHashMap(void),
|
|
|
|
|
in_scope: *std.ArrayList(ast.StructTypeParam),
|
|
|
|
|
type_vals: *std.ArrayList([]const u8),
|
|
|
|
|
) void {
|
|
|
|
|
switch (node.data) {
|
|
|
|
|
.block => |b| for (b.stmts) |s| self.walkBodyTypes(s, declared, in_scope, type_vals),
|
|
|
|
|
.if_expr => |ie| {
|
|
|
|
|
self.walkBodyTypes(ie.condition, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(ie.then_branch, declared, in_scope, type_vals);
|
|
|
|
|
if (ie.else_branch) |e| self.walkBodyTypes(e, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.while_expr => |we| {
|
|
|
|
|
self.walkBodyTypes(we.condition, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(we.body, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.for_expr => |fe| {
|
|
|
|
|
self.walkBodyTypes(fe.iterable, declared, in_scope, type_vals);
|
|
|
|
|
if (fe.range_end) |re| self.walkBodyTypes(re, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(fe.body, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.match_expr => |me| {
|
|
|
|
|
self.walkBodyTypes(me.subject, declared, in_scope, type_vals);
|
|
|
|
|
for (me.arms) |arm| self.walkBodyTypes(arm.body, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.push_stmt => |ps| {
|
|
|
|
|
self.walkBodyTypes(ps.context_expr, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(ps.body, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.defer_stmt => |ds| self.walkBodyTypes(ds.expr, declared, in_scope, type_vals),
|
|
|
|
|
.onfail_stmt => |os| self.walkBodyTypes(os.body, declared, in_scope, type_vals),
|
|
|
|
|
.return_stmt => |r| if (r.value) |v| self.walkBodyTypes(v, declared, in_scope, type_vals),
|
|
|
|
|
.raise_stmt => |rs| self.walkBodyTypes(rs.tag, declared, in_scope, type_vals),
|
|
|
|
|
.assignment => |a| {
|
|
|
|
|
self.walkBodyTypes(a.value, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(a.target, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.multi_assign => |ma| for (ma.values) |v| self.walkBodyTypes(v, declared, in_scope, type_vals),
|
|
|
|
|
.destructure_decl => |dd| self.walkBodyTypes(dd.value, declared, in_scope, type_vals),
|
|
|
|
|
.var_decl => |vd| {
|
|
|
|
|
if (vd.type_annotation) |ta| self.checkTypeNodeForUnknown(ta, declared, in_scope.items, type_vals.items);
|
|
|
|
|
if (vd.value) |v| self.walkBodyTypes(v, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.const_decl => |cd| {
|
|
|
|
|
if (cd.type_annotation) |ta| self.checkTypeNodeForUnknown(ta, declared, in_scope.items, type_vals.items);
|
|
|
|
|
self.walkBodyTypes(cd.value, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.struct_decl => |sd| if (sd.type_params.len == 0) {
|
|
|
|
|
for (sd.field_types) |ft| self.checkTypeNodeForUnknown(ft, declared, in_scope.items, type_vals.items);
|
|
|
|
|
},
|
|
|
|
|
.call => |c| {
|
|
|
|
|
// `cast(T) x` parses to a `cast` call whose first arg is the
|
|
|
|
|
// target type spelled as an expression. An unknown *literal*
|
|
|
|
|
// target already errors via value resolution; only flag the
|
|
|
|
|
// otherwise-silent value-`Type`-param case here.
|
|
|
|
|
if (c.callee.data == .identifier and std.mem.eql(u8, c.callee.data.identifier.name, "cast") and c.args.len == 2) {
|
|
|
|
|
self.checkCastTarget(c.args[0], in_scope.items, type_vals.items);
|
|
|
|
|
}
|
|
|
|
|
self.walkBodyTypes(c.callee, declared, in_scope, type_vals);
|
|
|
|
|
for (c.args) |a| self.walkBodyTypes(a, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.binary_op => |b| {
|
|
|
|
|
self.walkBodyTypes(b.lhs, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(b.rhs, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.unary_op => |u| self.walkBodyTypes(u.operand, declared, in_scope, type_vals),
|
|
|
|
|
.field_access => |fa| self.walkBodyTypes(fa.object, declared, in_scope, type_vals),
|
|
|
|
|
.index_expr => |ix| {
|
|
|
|
|
self.walkBodyTypes(ix.object, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(ix.index, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.struct_literal => |sl| {
|
|
|
|
|
for (sl.field_inits) |fi| self.walkBodyTypes(fi.value, declared, in_scope, type_vals);
|
|
|
|
|
if (sl.init_block) |ib| self.walkBodyTypes(ib, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.array_literal => |al| for (al.elements) |e| self.walkBodyTypes(e, declared, in_scope, type_vals),
|
|
|
|
|
.force_unwrap => |fu| self.walkBodyTypes(fu.operand, declared, in_scope, type_vals),
|
|
|
|
|
.null_coalesce => |nc| {
|
|
|
|
|
self.walkBodyTypes(nc.lhs, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(nc.rhs, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.deref_expr => |de| self.walkBodyTypes(de.operand, declared, in_scope, type_vals),
|
|
|
|
|
.try_expr => |te| self.walkBodyTypes(te.operand, declared, in_scope, type_vals),
|
|
|
|
|
.catch_expr => |ce| {
|
|
|
|
|
self.walkBodyTypes(ce.operand, declared, in_scope, type_vals);
|
|
|
|
|
self.walkBodyTypes(ce.body, declared, in_scope, type_vals);
|
|
|
|
|
},
|
|
|
|
|
.comptime_expr => |ce| self.walkBodyTypes(ce.expr, declared, in_scope, type_vals),
|
|
|
|
|
.spread_expr => |se| self.walkBodyTypes(se.operand, declared, in_scope, type_vals),
|
|
|
|
|
.lambda => |lm| self.checkScope(lm.type_params, lm.params, lm.return_type, lm.body, declared, in_scope, type_vals),
|
|
|
|
|
.fn_decl => |fd| self.checkScope(fd.type_params, fd.params, fd.return_type, fd.body, declared, in_scope, type_vals),
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A `cast(T)` target naming a value-`Type` parameter (the otherwise-silent
|
|
|
|
|
/// issue-0064 case in cast position) gets the tailored `$T` hint.
|
|
|
|
|
fn checkCastTarget(self: *Lowering, arg: *const Node, in_scope: []const ast.StructTypeParam, type_vals: []const []const u8) void {
|
|
|
|
|
const name = switch (arg.data) {
|
|
|
|
|
.identifier => |id| id.name,
|
|
|
|
|
.type_expr => |te| te.name,
|
|
|
|
|
else => return,
|
|
|
|
|
};
|
|
|
|
|
for (in_scope) |tp| if (std.mem.eql(u8, tp.name, name)) return;
|
|
|
|
|
for (type_vals) |tv| {
|
|
|
|
|
if (std.mem.eql(u8, tv, name)) {
|
|
|
|
|
if (self.diagnostics) |diags| {
|
|
|
|
|
diags.addFmt(.err, arg.span, "'{s}' is a value parameter, not a type; introduce a generic type parameter with `${s}: Type`", .{ name, name });
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Recurse a type-annotation node to its leaf names, reporting any unknown.
|
|
|
|
|
|