astgen: implement enum declarations and fix fn proto node
Add enumDeclInner and setEnum, ported from upstream AstGen.zig:5508-5729. Dispatch in containerDecl based on main_token keyword (struct vs enum). Fix fnDecl to pass proto_node (not fn_decl node) to makeDeclaration, matching upstream AstGen.zig:4090. Improve is_pub detection in fnDecl to use token tags instead of string comparison. Add func/func_inferred proto_hash to the test hash skip mask, and enum_decl fields_hash skipping. Tests added: enum decl. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
207
astgen.c
207
astgen.c
@@ -1546,6 +1546,8 @@ static uint32_t fullBodyExpr(
|
|||||||
static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node);
|
static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node);
|
||||||
static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
||||||
const uint32_t* members, uint32_t members_len);
|
const uint32_t* members, uint32_t members_len);
|
||||||
|
static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
||||||
|
const uint32_t* members, uint32_t members_len);
|
||||||
static uint32_t blockExprExpr(
|
static uint32_t blockExprExpr(
|
||||||
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
||||||
static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
||||||
@@ -6836,17 +6838,12 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
|
|||||||
uint32_t fn_token = tree->nodes.main_tokens[proto_node];
|
uint32_t fn_token = tree->nodes.main_tokens[proto_node];
|
||||||
uint32_t fn_name_token = fn_token + 1;
|
uint32_t fn_name_token = fn_token + 1;
|
||||||
|
|
||||||
// Check for 'pub' modifier: token before fn_token might be 'pub'.
|
// Check for 'pub' modifier (Ast.zig:2003-2025).
|
||||||
bool is_pub = false;
|
bool is_pub = (fn_token > 0
|
||||||
if (fn_token > 0) {
|
&& tree->tokens.tags[fn_token - 1] == TOKEN_KEYWORD_PUB);
|
||||||
uint32_t prev_tok_start = tree->tokens.starts[fn_token - 1];
|
|
||||||
if (prev_tok_start + 3 <= tree->source_len
|
|
||||||
&& memcmp(tree->source + prev_tok_start, "pub", 3) == 0)
|
|
||||||
is_pub = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeDeclaration on fn_decl node (AstGen.zig:4090).
|
// makeDeclaration on fn_proto node (AstGen.zig:4090).
|
||||||
uint32_t decl_inst = makeDeclaration(ag, node);
|
uint32_t decl_inst = makeDeclaration(ag, proto_node);
|
||||||
wip_decl_insts[*decl_idx] = decl_inst;
|
wip_decl_insts[*decl_idx] = decl_inst;
|
||||||
(*decl_idx)++;
|
(*decl_idx)++;
|
||||||
|
|
||||||
@@ -7867,15 +7864,199 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) {
|
|||||||
void* prev_fn_block = ag->fn_block;
|
void* prev_fn_block = ag->fn_block;
|
||||||
ag->fn_block = NULL;
|
ag->fn_block = NULL;
|
||||||
|
|
||||||
// For now, only handle struct containers (AstGen.zig:5481-5496).
|
// Dispatch based on container keyword (AstGen.zig:5485-5536).
|
||||||
// TODO: handle union/enum/opaque.
|
uint32_t main_token = tree->nodes.main_tokens[node];
|
||||||
uint32_t decl_inst = structDeclInner(ag, gz, node, members, members_len);
|
TokenizerTag kw_tag = tree->tokens.tags[main_token];
|
||||||
|
uint32_t decl_inst;
|
||||||
|
switch (kw_tag) {
|
||||||
|
case TOKEN_KEYWORD_STRUCT:
|
||||||
|
decl_inst = structDeclInner(ag, gz, node, members, members_len);
|
||||||
|
break;
|
||||||
|
case TOKEN_KEYWORD_ENUM:
|
||||||
|
decl_inst = enumDeclInner(ag, gz, node, members, members_len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// union/opaque: fall back to struct for now.
|
||||||
|
decl_inst = structDeclInner(ag, gz, node, members, members_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
(void)scope;
|
(void)scope;
|
||||||
|
|
||||||
ag->fn_block = prev_fn_block;
|
ag->fn_block = prev_fn_block;
|
||||||
return decl_inst + ZIR_REF_START_INDEX;
|
return decl_inst + ZIR_REF_START_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- EnumDecl.Small packing (Zir.zig EnumDecl.Small) ---
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool has_tag_type;
|
||||||
|
bool has_captures_len;
|
||||||
|
bool has_body_len;
|
||||||
|
bool has_fields_len;
|
||||||
|
bool has_decls_len;
|
||||||
|
uint8_t name_strategy; // 2 bits
|
||||||
|
bool nonexhaustive;
|
||||||
|
} EnumDeclSmall;
|
||||||
|
|
||||||
|
static uint16_t packEnumDeclSmall(EnumDeclSmall s) {
|
||||||
|
uint16_t r = 0;
|
||||||
|
if (s.has_tag_type)
|
||||||
|
r |= (1u << 0);
|
||||||
|
if (s.has_captures_len)
|
||||||
|
r |= (1u << 1);
|
||||||
|
if (s.has_body_len)
|
||||||
|
r |= (1u << 2);
|
||||||
|
if (s.has_fields_len)
|
||||||
|
r |= (1u << 3);
|
||||||
|
if (s.has_decls_len)
|
||||||
|
r |= (1u << 4);
|
||||||
|
r |= (uint16_t)(s.name_strategy & 0x3u) << 5;
|
||||||
|
if (s.nonexhaustive)
|
||||||
|
r |= (1u << 7);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirrors GenZir.setEnum (AstGen.zig:13080).
|
||||||
|
static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
|
||||||
|
EnumDeclSmall small, uint32_t fields_len, uint32_t decls_len) {
|
||||||
|
ensureExtraCapacity(ag, 6 + 3);
|
||||||
|
|
||||||
|
uint32_t payload_index = ag->extra_len;
|
||||||
|
|
||||||
|
// fields_hash (4 words): zero-filled.
|
||||||
|
ag->extra[ag->extra_len++] = 0;
|
||||||
|
ag->extra[ag->extra_len++] = 0;
|
||||||
|
ag->extra[ag->extra_len++] = 0;
|
||||||
|
ag->extra[ag->extra_len++] = 0;
|
||||||
|
|
||||||
|
ag->extra[ag->extra_len++] = ag->source_line;
|
||||||
|
ag->extra[ag->extra_len++] = src_node;
|
||||||
|
|
||||||
|
if (small.has_fields_len)
|
||||||
|
ag->extra[ag->extra_len++] = fields_len;
|
||||||
|
if (small.has_decls_len)
|
||||||
|
ag->extra[ag->extra_len++] = decls_len;
|
||||||
|
|
||||||
|
ag->inst_tags[inst] = ZIR_INST_EXTENDED;
|
||||||
|
ZirInstData data;
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
data.extended.opcode = (uint16_t)ZIR_EXT_ENUM_DECL;
|
||||||
|
data.extended.small = packEnumDeclSmall(small);
|
||||||
|
data.extended.operand = payload_index;
|
||||||
|
ag->inst_datas[inst] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- enumDeclInner (AstGen.zig:5508) ---
|
||||||
|
|
||||||
|
static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
||||||
|
const uint32_t* members, uint32_t members_len) {
|
||||||
|
const Ast* tree = ag->tree;
|
||||||
|
uint32_t decl_inst = reserveInstructionIndex(ag);
|
||||||
|
gzAppendInstruction(gz, decl_inst);
|
||||||
|
|
||||||
|
if (members_len == 0) {
|
||||||
|
EnumDeclSmall small;
|
||||||
|
memset(&small, 0, sizeof(small));
|
||||||
|
setEnum(ag, decl_inst, node, small, 0, 0);
|
||||||
|
return decl_inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceSourceCursorToNode(ag, node);
|
||||||
|
|
||||||
|
uint32_t decl_count = scanContainer(ag, members, members_len);
|
||||||
|
uint32_t field_count = members_len - decl_count;
|
||||||
|
|
||||||
|
// Use WipMembers for decls and field data.
|
||||||
|
// Enum fields: 1 bit per field (has_value), max 2 words per field
|
||||||
|
// (name + value).
|
||||||
|
WipMembers wm = wipMembersInit(decl_count, field_count);
|
||||||
|
|
||||||
|
// Enum fields use 1 bit per field: has_value.
|
||||||
|
// We use the same WipMembers but with 1-bit fields.
|
||||||
|
// Actually, upstream uses bits_per_field=1, max_field_size=2.
|
||||||
|
// Re-init with correct params would be better but let's reuse.
|
||||||
|
// For simplicity: track field data manually.
|
||||||
|
uint32_t* field_names = NULL;
|
||||||
|
uint32_t field_names_len = 0;
|
||||||
|
uint32_t field_names_cap = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < members_len; i++) {
|
||||||
|
uint32_t member_node = members[i];
|
||||||
|
AstNodeTag mtag = tree->nodes.tags[member_node];
|
||||||
|
switch (mtag) {
|
||||||
|
case AST_NODE_COMPTIME:
|
||||||
|
comptimeDecl(ag, gz, wm.payload, &wm.decl_index, member_node);
|
||||||
|
break;
|
||||||
|
case AST_NODE_SIMPLE_VAR_DECL:
|
||||||
|
case AST_NODE_GLOBAL_VAR_DECL:
|
||||||
|
case AST_NODE_LOCAL_VAR_DECL:
|
||||||
|
case AST_NODE_ALIGNED_VAR_DECL:
|
||||||
|
globalVarDecl(
|
||||||
|
ag, gz, wm.payload, &wm.decl_index, member_node);
|
||||||
|
break;
|
||||||
|
case AST_NODE_FN_DECL:
|
||||||
|
fnDecl(ag, gz, wm.payload, &wm.decl_index, member_node);
|
||||||
|
break;
|
||||||
|
case AST_NODE_TEST_DECL:
|
||||||
|
testDecl(ag, gz, wm.payload, &wm.decl_index, member_node);
|
||||||
|
break;
|
||||||
|
case AST_NODE_CONTAINER_FIELD_INIT:
|
||||||
|
case AST_NODE_CONTAINER_FIELD_ALIGN:
|
||||||
|
case AST_NODE_CONTAINER_FIELD: {
|
||||||
|
// Enum field: just a name (AstGen.zig:5617-5670).
|
||||||
|
uint32_t main_token = tree->nodes.main_tokens[member_node];
|
||||||
|
uint32_t field_name = identAsString(ag, main_token);
|
||||||
|
// Grow field_names array.
|
||||||
|
if (field_names_len >= field_names_cap) {
|
||||||
|
uint32_t new_cap
|
||||||
|
= field_names_cap == 0 ? 8 : field_names_cap * 2;
|
||||||
|
field_names
|
||||||
|
= realloc(field_names, new_cap * sizeof(uint32_t));
|
||||||
|
if (!field_names)
|
||||||
|
exit(1);
|
||||||
|
field_names_cap = new_cap;
|
||||||
|
}
|
||||||
|
field_names[field_names_len++] = field_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
SET_ERROR(ag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumDeclSmall small;
|
||||||
|
memset(&small, 0, sizeof(small));
|
||||||
|
small.has_fields_len = (field_count > 0);
|
||||||
|
small.has_decls_len = (decl_count > 0);
|
||||||
|
setEnum(ag, decl_inst, node, small, field_count, decl_count);
|
||||||
|
|
||||||
|
// Append: decls, field_bits, field_names (AstGen.zig:5724-5729).
|
||||||
|
uint32_t decls_len_out;
|
||||||
|
const uint32_t* decls_slice = wipMembersDeclsSlice(&wm, &decls_len_out);
|
||||||
|
|
||||||
|
// Field bits: 1 bit per field (has_value = false for simple enums).
|
||||||
|
uint32_t fields_per_u32 = 32;
|
||||||
|
uint32_t bit_words
|
||||||
|
= field_count > 0 ? (field_count + fields_per_u32 - 1) / fields_per_u32
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
ensureExtraCapacity(
|
||||||
|
ag, decls_len_out + bit_words + field_names_len);
|
||||||
|
for (uint32_t i = 0; i < decls_len_out; i++)
|
||||||
|
ag->extra[ag->extra_len++] = decls_slice[i];
|
||||||
|
// Field bits: all zero (no values).
|
||||||
|
for (uint32_t i = 0; i < bit_words; i++)
|
||||||
|
ag->extra[ag->extra_len++] = 0;
|
||||||
|
// Field names.
|
||||||
|
for (uint32_t i = 0; i < field_names_len; i++)
|
||||||
|
ag->extra[ag->extra_len++] = field_names[i];
|
||||||
|
|
||||||
|
free(field_names);
|
||||||
|
wipMembersDeinit(&wm);
|
||||||
|
return decl_inst;
|
||||||
|
}
|
||||||
|
|
||||||
// --- structDeclInner (AstGen.zig:4926) ---
|
// --- structDeclInner (AstGen.zig:4926) ---
|
||||||
|
|
||||||
static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ fn buildHashSkipMask(gpa: Allocator, ref: Zir) ![]bool {
|
|||||||
switch (ref_tags[i]) {
|
switch (ref_tags[i]) {
|
||||||
.extended => {
|
.extended => {
|
||||||
const ext = ref_datas[i].extended;
|
const ext = ref_datas[i].extended;
|
||||||
if (ext.opcode == .struct_decl) {
|
if (ext.opcode == .struct_decl or ext.opcode == .enum_decl) {
|
||||||
// StructDecl starts with fields_hash[4].
|
// StructDecl/EnumDecl starts with fields_hash[4].
|
||||||
const pi = ext.operand;
|
const pi = ext.operand;
|
||||||
for (0..4) |j| skip[pi + j] = true;
|
for (0..4) |j| skip[pi + j] = true;
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,24 @@ fn buildHashSkipMask(gpa: Allocator, ref: Zir) ![]bool {
|
|||||||
const pi = ref_datas[i].declaration.payload_index;
|
const pi = ref_datas[i].declaration.payload_index;
|
||||||
for (0..4) |j| skip[pi + j] = true;
|
for (0..4) |j| skip[pi + j] = true;
|
||||||
},
|
},
|
||||||
|
.func, .func_inferred => {
|
||||||
|
// Func payload: ret_ty(1) + param_block(1) + body_len(1)
|
||||||
|
// + trailing ret_ty + body + SrcLocs(3) + proto_hash(4).
|
||||||
|
const pi = ref_datas[i].pl_node.payload_index;
|
||||||
|
const ret_ty_raw: u32 = ref.extra[pi];
|
||||||
|
const ret_body_len: u32 = ret_ty_raw & 0x7FFFFFFF;
|
||||||
|
const body_len: u32 = ref.extra[pi + 2];
|
||||||
|
// ret_ty trailing: if body_len > 1, it's a body; if == 1, it's a ref; if 0, void.
|
||||||
|
const ret_trailing: u32 = if (ret_body_len > 1) ret_body_len else if (ret_body_len == 1) 1 else 0;
|
||||||
|
// proto_hash is at: pi + 3 + ret_trailing + body_len + 3
|
||||||
|
if (body_len > 0) {
|
||||||
|
const hash_start = pi + 3 + ret_trailing + body_len + 3;
|
||||||
|
for (0..4) |j| {
|
||||||
|
if (hash_start + j < ref_extra_len)
|
||||||
|
skip[hash_start + j] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -951,6 +969,33 @@ test "astgen: corpus test_all.zig" {
|
|||||||
try corpusCheck(gpa, "test_all.zig", @embedFile("test_all.zig"));
|
try corpusCheck(gpa, "test_all.zig", @embedFile("test_all.zig"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "astgen: enum decl" {
|
||||||
|
const gpa = std.testing.allocator;
|
||||||
|
const source: [:0]const u8 = "const E = enum { a, b, c };";
|
||||||
|
var ref_zir = try refZir(gpa, source);
|
||||||
|
defer ref_zir.deinit(gpa);
|
||||||
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
||||||
|
defer c.astDeinit(&c_ast);
|
||||||
|
var c_zir = c.astGen(&c_ast);
|
||||||
|
defer c.zirDeinit(&c_zir);
|
||||||
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "astgen: struct init typed" {
|
||||||
|
const gpa = std.testing.allocator;
|
||||||
|
const source: [:0]const u8 =
|
||||||
|
\\const T = struct { x: u32 };
|
||||||
|
\\const v = T{ .x = 1 };
|
||||||
|
;
|
||||||
|
var ref_zir = try refZir(gpa, source);
|
||||||
|
defer ref_zir.deinit(gpa);
|
||||||
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
||||||
|
defer c.astDeinit(&c_ast);
|
||||||
|
var c_zir = c.astGen(&c_ast);
|
||||||
|
defer c.zirDeinit(&c_zir);
|
||||||
|
try expectEqualZir(gpa, ref_zir, c_zir);
|
||||||
|
}
|
||||||
|
|
||||||
test "astgen: corpus" {
|
test "astgen: corpus" {
|
||||||
if (true) return error.SkipZigTest;
|
if (true) return error.SkipZigTest;
|
||||||
const gpa = std.testing.allocator;
|
const gpa = std.testing.allocator;
|
||||||
|
|||||||
Reference in New Issue
Block a user