add member functions

This commit is contained in:
Andrew Kelley 2016-01-04 16:57:22 -07:00
parent fcacc85b4e
commit 4514661cfe
7 changed files with 334 additions and 209 deletions

View File

@ -36,7 +36,9 @@ TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl | Variabl
VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace)
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructMember) token(RBrace)
StructMember: StructField | FnDecl
StructField : token(Symbol) token(Colon) Type token(Comma)

View File

@ -106,6 +106,11 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->arrays_by_size.init(2);
entry->id = id;
if (id == TypeTableEntryIdStruct) {
entry->data.structure.fn_table.init(8);
}
return entry;
}
@ -461,6 +466,68 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
struct_type->di_type = replacement_di_type;
}
static void preview_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node, TypeTableEntry *struct_type) {
assert(node->type == NodeTypeFnDef);
AstNode *proto_node = node->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
Buf *proto_name = &proto_node->data.fn_proto.name;
auto fn_table = struct_type ? &struct_type->data.structure.fn_table : &import->fn_table;
auto entry = fn_table->maybe_get(proto_name);
bool skip = false;
bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport);
bool is_pub = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModPrivate);
if (entry) {
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
skip = true;
} else if (is_pub) {
auto entry = fn_table->maybe_get(proto_name);
if (entry) {
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
skip = true;
}
}
if (proto_node->data.fn_proto.is_var_args) {
add_node_error(g, node,
buf_sprintf("variadic arguments only allowed in extern functions"));
}
if (!skip) {
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
fn_table_entry->import_entry = import;
fn_table_entry->proto_node = proto_node;
fn_table_entry->fn_def_node = node;
fn_table_entry->internal_linkage = is_internal;
fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
fn_table_entry->label_table.init(8);
g->fn_protos.append(fn_table_entry);
g->fn_defs.append(fn_table_entry);
fn_table->put(proto_name, fn_table_entry);
if (!struct_type &&
g->bootstrap_import &&
import == g->root_import && buf_eql_str(proto_name, "main"))
{
g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
}
resolve_function_proto(g, proto_node, fn_table_entry, import);
alloc_codegen_node(proto_node);
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
preview_function_labels(g, node->data.fn_def.body, fn_table_entry);
}
}
static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
@ -500,61 +567,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
}
break;
case NodeTypeFnDef:
{
AstNode *proto_node = node->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
Buf *proto_name = &proto_node->data.fn_proto.name;
auto entry = import->fn_table.maybe_get(proto_name);
bool skip = false;
bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport);
bool is_pub = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModPrivate);
if (entry) {
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
skip = true;
} else if (is_pub) {
auto entry = import->fn_table.maybe_get(proto_name);
if (entry) {
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
skip = true;
}
}
if (proto_node->data.fn_proto.is_var_args) {
add_node_error(g, node,
buf_sprintf("variadic arguments only allowed in extern functions"));
}
if (!skip) {
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
fn_table_entry->import_entry = import;
fn_table_entry->proto_node = proto_node;
fn_table_entry->fn_def_node = node;
fn_table_entry->internal_linkage = is_internal;
fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
fn_table_entry->label_table.init(8);
g->fn_protos.append(fn_table_entry);
g->fn_defs.append(fn_table_entry);
import->fn_table.put(proto_name, fn_table_entry);
if (g->bootstrap_import && import == g->root_import && buf_eql_str(proto_name, "main")) {
g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
}
resolve_function_proto(g, proto_node, fn_table_entry, import);
alloc_codegen_node(proto_node);
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
preview_function_labels(g, node->data.fn_def.body, fn_table_entry);
}
}
preview_fn_def(g, import, node, nullptr);
break;
case NodeTypeRootExportDecl:
if (import == g->root_import) {
@ -605,6 +618,11 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
TypeTableEntry *type_entry = struct_codegen->type_entry;
resolve_struct_type(g, import, type_entry);
for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
preview_fn_def(g, import, fn_def_node, type_entry);
}
break;
}
case NodeTypeUse:
@ -1624,6 +1642,94 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im
}
}
static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
TypeTableEntry *struct_type = nullptr;
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> *fn_table = &import->fn_table;
AstNode *first_param_expr = nullptr;
Buf *name;
if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
struct_type = analyze_expression(g, import, context, nullptr, first_param_expr);
name = &fn_ref_expr->data.field_access_expr.field_name;
if (struct_type->id == TypeTableEntryIdStruct) {
fn_table = &struct_type->data.structure.fn_table;
} else if (struct_type->id == TypeTableEntryIdInvalid) {
return struct_type;
} else {
add_node_error(g, fn_ref_expr->data.field_access_expr.struct_expr,
buf_sprintf("member reference base type not struct or enum"));
return g->builtin_types.entry_invalid;
}
} else if (fn_ref_expr->type == NodeTypeSymbol) {
name = &fn_ref_expr->data.symbol;
} else {
add_node_error(g, node,
buf_sprintf("function pointers not yet supported"));
return g->builtin_types.entry_invalid;
}
auto entry = fn_table->maybe_get(name);
if (!entry) {
add_node_error(g, fn_ref_expr,
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
// still analyze the parameters, even though we don't know what to expect
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
AstNode *child = node->data.fn_call_expr.params.at(i);
analyze_expression(g, import, context, nullptr, child);
}
return g->builtin_types.entry_invalid;
} else {
FnTableEntry *fn_table_entry = entry->value;
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
// count parameters
int expected_param_count = fn_proto->params.length;
int actual_param_count = node->data.fn_call_expr.params.length;
if (struct_type) {
actual_param_count += 1;
}
if (fn_proto->is_var_args) {
if (actual_param_count < expected_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected at least %d, got %d.",
expected_param_count, actual_param_count));
}
} else if (expected_param_count != actual_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected %d, got %d.",
expected_param_count, actual_param_count));
}
// analyze each parameter. in the case of a method, we already analyzed the
// first parameter in order to figure out which struct we were calling a method on.
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
AstNode *child = node->data.fn_call_expr.params.at(i);
// determine the expected type for each parameter
TypeTableEntry *expected_param_type = nullptr;
int fn_proto_i = i + (struct_type ? 1 : 0);
if (fn_proto_i < fn_proto->params.length) {
AstNode *param_decl_node = fn_proto->params.at(fn_proto_i);
assert(param_decl_node->type == NodeTypeParamDecl);
AstNode *param_type_node = param_decl_node->data.param_decl.type;
if (param_type_node->codegen_node)
expected_param_type = param_type_node->codegen_node->data.type_node.entry;
}
analyze_expression(g, import, context, expected_param_type, child);
}
return fn_proto->return_type->codegen_node->data.type_node.entry;
}
}
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@ -1739,67 +1845,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
case NodeTypeFnCallExpr:
{
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
if (fn_ref_expr->type != NodeTypeSymbol) {
add_node_error(g, node,
buf_sprintf("function pointers not allowed"));
break;
}
Buf *name = &fn_ref_expr->data.symbol;
auto entry = import->fn_table.maybe_get(name);
if (!entry) {
add_node_error(g, fn_ref_expr,
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
// still analyze the parameters, even though we don't know what to expect
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
AstNode *child = node->data.fn_call_expr.params.at(i);
analyze_expression(g, import, context, nullptr, child);
}
return_type = g->builtin_types.entry_invalid;
} else {
FnTableEntry *fn_table_entry = entry->value;
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
// count parameters
int expected_param_count = fn_proto->params.length;
int actual_param_count = node->data.fn_call_expr.params.length;
if (fn_proto->is_var_args) {
if (actual_param_count < expected_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected at least %d, got %d.",
expected_param_count, actual_param_count));
}
} else if (expected_param_count != actual_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected %d, got %d.",
expected_param_count, actual_param_count));
}
// analyze each parameter
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
AstNode *child = node->data.fn_call_expr.params.at(i);
// determine the expected type for each parameter
TypeTableEntry *expected_param_type = nullptr;
if (i < fn_proto->params.length) {
AstNode *param_decl_node = fn_proto->params.at(i);
assert(param_decl_node->type == NodeTypeParamDecl);
AstNode *param_type_node = param_decl_node->data.param_decl.type;
if (param_type_node->codegen_node)
expected_param_type = param_type_node->codegen_node->data.type_node.entry;
}
analyze_expression(g, import, context, expected_param_type, child);
}
return_type = fn_proto->return_type->codegen_node->data.type_node.entry;
}
break;
}
return_type = analyze_fn_call_expr(g, import, context, expected_type, node);
break;
case NodeTypeArrayAccessExpr:
// for reading array access; assignment handled elsewhere
@ -1924,89 +1971,92 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
return return_type;
}
static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
assert(node->type == NodeTypeFnDef);
if (node->codegen_node && node->codegen_node->data.fn_def_node.skip) {
// we detected an error with this function definition which prevents us
// from further analyzing it.
return;
}
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
assert(fn_proto_node->type == NodeTypeFnProto);
alloc_codegen_node(node);
BlockContext *context = new_block_context(node, import->block_context);
node->codegen_node->data.fn_def_node.block_context = context;
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
bool is_exported = (fn_proto->visib_mod == FnProtoVisibModExport);
for (int i = 0; i < fn_proto->params.length; i += 1) {
AstNode *param_decl_node = fn_proto->params.at(i);
assert(param_decl_node->type == NodeTypeParamDecl);
// define local variables for parameters
AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
assert(param_decl->type->type == NodeTypeType);
TypeTableEntry *type = param_decl->type->codegen_node->data.type_node.entry;
if (is_exported && type->id == TypeTableEntryIdStruct) {
add_node_error(g, param_decl_node,
buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
}
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
buf_init_from_buf(&variable_entry->name, &param_decl->name);
variable_entry->type = type;
variable_entry->is_const = true;
variable_entry->decl_node = param_decl_node;
variable_entry->arg_index = i;
alloc_codegen_node(param_decl_node);
param_decl_node->codegen_node->data.param_decl_node.variable = variable_entry;
VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
if (!existing_entry) {
// unique definition
context->variable_table.put(&variable_entry->name, variable_entry);
} else {
add_node_error(g, node,
buf_sprintf("redeclaration of parameter '%s'.", buf_ptr(&existing_entry->name)));
if (existing_entry->type == variable_entry->type) {
// types agree, so the type is probably good enough for the rest of analysis
} else {
// types disagree. don't trust either one of them.
existing_entry->type = g->builtin_types.entry_invalid;;
}
}
}
TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
{
FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
auto it = fn_table_entry->label_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
LabelTableEntry *label_entry = entry->value;
if (!label_entry->used) {
add_node_error(g, label_entry->label_node,
buf_sprintf("label '%s' defined but not used",
buf_ptr(&label_entry->label_node->data.label.name)));
}
}
}
}
static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
case NodeTypeFnDef:
{
if (node->codegen_node && node->codegen_node->data.fn_def_node.skip) {
// we detected an error with this function definition which prevents us
// from further analyzing it.
break;
}
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
assert(fn_proto_node->type == NodeTypeFnProto);
alloc_codegen_node(node);
BlockContext *context = new_block_context(node, import->block_context);
node->codegen_node->data.fn_def_node.block_context = context;
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
bool is_exported = (fn_proto->visib_mod == FnProtoVisibModExport);
for (int i = 0; i < fn_proto->params.length; i += 1) {
AstNode *param_decl_node = fn_proto->params.at(i);
assert(param_decl_node->type == NodeTypeParamDecl);
// define local variables for parameters
AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
assert(param_decl->type->type == NodeTypeType);
TypeTableEntry *type = param_decl->type->codegen_node->data.type_node.entry;
if (is_exported && type->id == TypeTableEntryIdStruct) {
add_node_error(g, param_decl_node,
buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
}
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
buf_init_from_buf(&variable_entry->name, &param_decl->name);
variable_entry->type = type;
variable_entry->is_const = true;
variable_entry->decl_node = param_decl_node;
variable_entry->arg_index = i;
alloc_codegen_node(param_decl_node);
param_decl_node->codegen_node->data.param_decl_node.variable = variable_entry;
VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
if (!existing_entry) {
// unique definition
context->variable_table.put(&variable_entry->name, variable_entry);
} else {
add_node_error(g, node,
buf_sprintf("redeclaration of parameter '%s'.", buf_ptr(&existing_entry->name)));
if (existing_entry->type == variable_entry->type) {
// types agree, so the type is probably good enough for the rest of analysis
} else {
// types disagree. don't trust either one of them.
existing_entry->type = g->builtin_types.entry_invalid;;
}
}
}
TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
{
FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
auto it = fn_table_entry->label_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
LabelTableEntry *label_entry = entry->value;
if (!label_entry->used) {
add_node_error(g, label_entry->label_node,
buf_sprintf("label '%s' defined but not used",
buf_ptr(&label_entry->label_node->data.label.name)));
}
}
}
}
analyze_top_level_fn_def(g, import, node);
break;
case NodeTypeRootExportDecl:
case NodeTypeExternBlock:
// already looked at these in the preview pass
@ -2048,8 +2098,13 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeStructDecl:
// nothing to do
break;
{
for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
analyze_top_level_fn_def(g, import, fn_def_node);
}
break;
}
case NodeTypeVariableDeclaration:
{
VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context,

View File

@ -45,6 +45,8 @@ struct TypeTableEntryStruct {
int field_count;
TypeStructField *fields;
uint64_t size_bytes;
// reminder: hash tables must be initialized before use
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
// set this flag temporarily to detect infinite loops
bool embedded_in_current;

View File

@ -142,15 +142,31 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
FnTableEntry *fn_table_entry;
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
TypeTableEntry *struct_type;
AstNode *first_param_expr;
if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
Buf *name = &fn_ref_expr->data.field_access_expr.field_name;
first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
struct_type = get_expr_type(first_param_expr);
fn_table_entry = struct_type->data.structure.fn_table.get(name);
} else if (fn_ref_expr->type == NodeTypeSymbol) {
Buf *name = hack_get_fn_call_name(g, fn_ref_expr);
struct_type = nullptr;
first_param_expr = nullptr;
fn_table_entry = g->cur_fn->import_entry->fn_table.get(name);
} else {
zig_unreachable();
}
FnTableEntry *fn_table_entry = g->cur_fn->import_entry->fn_table.get(name);
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto_data = &fn_table_entry->proto_node->data.fn_proto;
int expected_param_count = fn_proto_data->params.length;
int actual_param_count = node->data.fn_call_expr.params.length;
int fn_call_param_count = node->data.fn_call_expr.params.length;
int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0);
bool is_var_args = fn_proto_data->is_var_args;
assert((is_var_args && actual_param_count >= expected_param_count) ||
actual_param_count == expected_param_count);
@ -164,10 +180,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
}
LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count);
int loop_end = max(gen_param_count, actual_param_count);
int gen_param_index = 0;
for (int i = 0; i < loop_end; i += 1) {
if (struct_type) {
gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
gen_param_index += 1;
}
for (int i = 0; i < fn_call_param_count; i += 1) {
AstNode *expr_node = node->data.fn_call_expr.params.at(i);
LLVMValueRef param_value = gen_expr(g, expr_node);
if (is_var_args ||

View File

@ -395,6 +395,14 @@ void ast_print(AstNode *node, int indent) {
case NodeTypeStructDecl:
fprintf(stderr, "%s '%s'\n",
node_type_str(node->type), buf_ptr(&node->data.struct_decl.name));
for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) {
AstNode *child = node->data.struct_decl.fields.at(i);
ast_print(child, indent + 2);
}
for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
AstNode *child = node->data.struct_decl.fns.at(i);
ast_print(child, indent + 2);
}
break;
case NodeTypeStructField:
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.struct_field.name));
@ -2572,7 +2580,8 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) {
}
/*
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace)
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructMember) token(RBrace)
StructMember: StructField | FnDecl
StructField : token(Symbol) token(Colon) Type token(Comma)
*/
static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
@ -2590,16 +2599,38 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
ast_eat_token(pc, token_index, TokenIdLBrace);
node->data.struct_decl.directives = pc->directive_list;
pc->directive_list = nullptr;
for (;;) {
assert(!pc->directive_list);
pc->directive_list = allocate<ZigList<AstNode*>>(1);
Token *directive_token = &pc->tokens->at(*token_index);
ast_parse_directives(pc, token_index, pc->directive_list);
AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false);
if (fn_def_node) {
node->data.struct_decl.fns.append(fn_def_node);
continue;
}
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdRBrace) {
if (pc->directive_list->length > 0) {
ast_error(pc, directive_token, "invalid directive");
}
pc->directive_list = nullptr;
*token_index += 1;
break;
} else if (token->id == TokenIdSymbol) {
AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token);
*token_index += 1;
field_node->data.struct_field.directives = pc->directive_list;
pc->directive_list = nullptr;
ast_buf_from_token(pc, token, &field_node->data.struct_field.name);
ast_eat_token(pc, token_index, TokenIdColon);
@ -2615,9 +2646,6 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
}
node->data.struct_decl.directives = pc->directive_list;
pc->directive_list = nullptr;
return node;
}

View File

@ -281,12 +281,14 @@ struct AstNodeAsmExpr {
struct AstNodeStructDecl {
Buf name;
ZigList<AstNode *> fields;
ZigList<AstNode *> fns;
ZigList<AstNode *> *directives;
};
struct AstNodeStructField {
Buf name;
AstNode *type;
ZigList<AstNode *> *directives;
};
struct AstNodeStringLiteral {

View File

@ -56,7 +56,6 @@ static TestCase *add_simple_case(const char *case_name, const char *source, cons
test_case->compiler_args.append(tmp_exe_path);
test_case->compiler_args.append("--release");
test_case->compiler_args.append("--strip");
//test_case->compiler_args.append("--verbose");
test_case->compiler_args.append("--color");
test_case->compiler_args.append("on");
@ -740,6 +739,24 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
return 0;
}
)SOURCE", "2\n");
add_simple_case("member functions", R"SOURCE(
use "std.zig";
struct Rand {
seed: u32,
pub fn get_seed(r: Rand) -> u32 {
r.seed
}
}
pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
const r = Rand {.seed = 1234};
if (r.get_seed() != 1234) {
print_str("BAD seed\n");
}
print_str("OK\n");
return 0;
}
)SOURCE", "OK\n");
}
////////////////////////////////////////////////////////////////////////////////////