Commit 4514661cfe
Changed files (7)
doc/langref.md
@@ -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)
src/analyze.cpp
@@ -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_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;
- }
+static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+ assert(node->type == NodeTypeFnDef);
- AstNode *fn_proto_node = node->data.fn_def.fn_proto;
- assert(fn_proto_node->type == NodeTypeFnProto);
+ 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;
+ }
- 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 = ¶m_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"));
- }
+ AstNode *fn_proto_node = node->data.fn_def.fn_proto;
+ assert(fn_proto_node->type == NodeTypeFnProto);
- VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
- buf_init_from_buf(&variable_entry->name, ¶m_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(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 = ¶m_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"));
+ }
- alloc_codegen_node(param_decl_node);
- param_decl_node->codegen_node->data.param_decl_node.variable = variable_entry;
+ VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
+ buf_init_from_buf(&variable_entry->name, ¶m_decl->name);
+ variable_entry->type = type;
+ variable_entry->is_const = true;
+ variable_entry->decl_node = param_decl_node;
+ variable_entry->arg_index = i;
- 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;;
- }
- }
- }
+ alloc_codegen_node(param_decl_node);
+ param_decl_node->codegen_node->data.param_decl_node.variable = variable_entry;
- 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);
+ 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;;
+ }
+ }
+ }
- node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
+ 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);
- {
- 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;
+ node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
- 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)));
- }
- }
- }
+ {
+ 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)));
}
- break;
+ }
+ }
+}
+static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+ switch (node->type) {
+ case NodeTypeFnDef:
+ 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,
src/analyze.hpp
@@ -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;
src/codegen.cpp
@@ -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 ||
src/parser.cpp
@@ -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;
}
src/parser.hpp
@@ -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 {
test/run_tests.cpp
@@ -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");
}
////////////////////////////////////////////////////////////////////////////////////