const Scope = struct
const Scope = struct {
tag: Tag,
fn cast(base: *Scope, comptime T: type) ?*T {
if (T == Defer) {
switch (base.tag) {
.defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)),
else => return null,
}
}
if (T == Namespace) {
switch (base.tag) {
.namespace => return @alignCast(@fieldParentPtr("base", base)),
else => return null,
}
}
if (base.tag != T.base_tag)
return null;
return @alignCast(@fieldParentPtr("base", base));
}
fn parent(base: *Scope) ?*Scope {
return switch (base.tag) {
.gen_zir => base.cast(GenZir).?.parent,
.local_val => base.cast(LocalVal).?.parent,
.local_ptr => base.cast(LocalPtr).?.parent,
.defer_normal, .defer_error => base.cast(Defer).?.parent,
.namespace => base.cast(Namespace).?.parent,
.top => null,
};
}
fn unwrap(base: *Scope) Unwrapped {
return switch (base.tag) {
inline else => |tag| @unionInit(
Unwrapped,
@tagName(tag),
@alignCast(@fieldParentPtr("base", base)),
),
};
}
const Unwrapped = union(Tag) {
gen_zir: *GenZir,
local_val: *LocalVal,
local_ptr: *LocalPtr,
defer_normal: *Defer,
defer_error: *Defer,
namespace: *Namespace,
top: *Top,
};
const Tag = enum {
gen_zir,
local_val,
local_ptr,
defer_normal,
defer_error,
namespace,
top,
};
/// The category of identifier. These tag names are user-visible in compile errors.
const IdCat = enum {
@"function parameter",
@"local constant",
@"local variable",
@"switch tag capture",
capture,
};
/// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
/// This structure lives as long as the AST generation of the Block
/// node that contains the variable.
const LocalVal = struct {
const base_tag: Tag = .local_val;
base: Scope = Scope{ .tag = base_tag },
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
gen_zir: *GenZir,
inst: Zir.Inst.Ref,
/// Source location of the corresponding variable declaration.
token_src: Ast.TokenIndex,
/// Track the first identifier where it is referenced.
/// .none means never referenced.
used: Ast.OptionalTokenIndex = .none,
/// Track the identifier where it is discarded, like this `_ = foo;`.
/// .none means never discarded.
discarded: Ast.OptionalTokenIndex = .none,
is_used_or_discarded: ?*bool = null,
/// String table index.
name: Zir.NullTerminatedString,
id_cat: IdCat,
};
/// This could be a `const` or `var` local. It has a pointer instead of a value.
/// This structure lives as long as the AST generation of the Block
/// node that contains the variable.
const LocalPtr = struct {
const base_tag: Tag = .local_ptr;
base: Scope = Scope{ .tag = base_tag },
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
gen_zir: *GenZir,
ptr: Zir.Inst.Ref,
/// Source location of the corresponding variable declaration.
token_src: Ast.TokenIndex,
/// Track the first identifier where it is referenced.
/// .none means never referenced.
used: Ast.OptionalTokenIndex = .none,
/// Track the identifier where it is discarded, like this `_ = foo;`.
/// .none means never discarded.
discarded: Ast.OptionalTokenIndex = .none,
/// Whether this value is used as an lvalue after initialization.
/// If not, we know it can be `const`, so will emit a compile error if it is `var`.
used_as_lvalue: bool = false,
/// String table index.
name: Zir.NullTerminatedString,
id_cat: IdCat,
/// true means we find out during Sema whether the value is comptime.
/// false means it is already known at AstGen the value is runtime-known.
maybe_comptime: bool,
};
const Defer = struct {
base: Scope,
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
index: u32,
len: u32,
};
/// Represents a global scope that has any number of declarations in it.
/// Each declaration has this as the parent scope.
const Namespace = struct {
const base_tag: Tag = .namespace;
base: Scope = Scope{ .tag = base_tag },
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
/// Maps string table index to the source location of declaration,
/// for the purposes of reporting name shadowing compile errors.
decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty,
node: Ast.Node.Index,
inst: Zir.Inst.Index,
maybe_generic: bool,
/// The astgen scope containing this namespace.
/// Only valid during astgen.
declaring_gz: ?*GenZir,
/// Set of captures used by this namespace.
captures: std.array_hash_map.Auto(Zir.Inst.Capture, Zir.NullTerminatedString) = .empty,
fn deinit(self: *Namespace, gpa: Allocator) void {
self.decls.deinit(gpa);
self.captures.deinit(gpa);
self.* = undefined;
}
};
const Top = struct {
const base_tag: Scope.Tag = .top;
base: Scope = Scope{ .tag = base_tag },
};
}