inline parameters
This replaces the current generic syntax for functions and replaces it with the concept of inline parameters. This paves the way for the "all structs anonymous" proposal. Closes #151.
This commit is contained in:
parent
425c0ffa01
commit
78d4fb20c4
@ -25,7 +25,7 @@ UseDecl = "use" Expression ";"
|
|||||||
|
|
||||||
ExternDecl = "extern" (FnProto | VariableDeclaration) ";"
|
ExternDecl = "extern" (FnProto | VariableDeclaration) ";"
|
||||||
|
|
||||||
FnProto = "fn" option("Symbol") option(ParamDeclList) ParamDeclList option("->" TypeExpr)
|
FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr)
|
||||||
|
|
||||||
Directive = "#" "Symbol" "(" Expression ")"
|
Directive = "#" "Symbol" "(" Expression ")"
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ FnDef = option("inline" | "extern") FnProto Block
|
|||||||
|
|
||||||
ParamDeclList = "(" list(ParamDecl, ",") ")"
|
ParamDeclList = "(" list(ParamDecl, ",") ")"
|
||||||
|
|
||||||
ParamDecl = option("noalias") option("Symbol" ":") TypeExpr | "..."
|
ParamDecl = option("noalias" | "inline") option("Symbol" ":") TypeExpr | "..."
|
||||||
|
|
||||||
Block = "{" list(option(Statement), ";") "}"
|
Block = "{" list(option(Statement), ";") "}"
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ pub fn main(args: [][]u8) -> %void {
|
|||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
||||||
const guess = io.parse_unsigned(u8)(line_buf[0...line_len - 1], 10) %% {
|
const guess = io.parse_unsigned(u8, line_buf[0...line_len - 1], 10) %% {
|
||||||
%%io.stdout.printf("Invalid number.\n");
|
%%io.stdout.printf("Invalid number.\n");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -195,10 +195,8 @@ struct AstNodeRoot {
|
|||||||
struct AstNodeFnProto {
|
struct AstNodeFnProto {
|
||||||
TopLevelDecl top_level_decl;
|
TopLevelDecl top_level_decl;
|
||||||
Buf name;
|
Buf name;
|
||||||
ZigList<AstNode *> generic_params;
|
|
||||||
ZigList<AstNode *> params;
|
ZigList<AstNode *> params;
|
||||||
AstNode *return_type;
|
AstNode *return_type;
|
||||||
bool generic_params_is_var_args;
|
|
||||||
bool is_var_args;
|
bool is_var_args;
|
||||||
bool is_extern;
|
bool is_extern;
|
||||||
bool is_inline;
|
bool is_inline;
|
||||||
@ -210,7 +208,10 @@ struct AstNodeFnProto {
|
|||||||
FnTableEntry *fn_table_entry;
|
FnTableEntry *fn_table_entry;
|
||||||
bool skip;
|
bool skip;
|
||||||
Expr resolved_expr;
|
Expr resolved_expr;
|
||||||
TypeTableEntry *generic_fn_type;
|
// computed from params field
|
||||||
|
int inline_arg_count;
|
||||||
|
// if this is a generic function implementation, this points to the generic node
|
||||||
|
AstNode *generic_proto_node;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeFnDef {
|
struct AstNodeFnDef {
|
||||||
@ -219,6 +220,7 @@ struct AstNodeFnDef {
|
|||||||
|
|
||||||
// populated by semantic analyzer
|
// populated by semantic analyzer
|
||||||
TypeTableEntry *implicit_return_type;
|
TypeTableEntry *implicit_return_type;
|
||||||
|
// the first child block context
|
||||||
BlockContext *block_context;
|
BlockContext *block_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -230,6 +232,7 @@ struct AstNodeParamDecl {
|
|||||||
Buf name;
|
Buf name;
|
||||||
AstNode *type;
|
AstNode *type;
|
||||||
bool is_noalias;
|
bool is_noalias;
|
||||||
|
bool is_inline;
|
||||||
|
|
||||||
// populated by semantic analyzer
|
// populated by semantic analyzer
|
||||||
VariableTableEntry *variable;
|
VariableTableEntry *variable;
|
||||||
@ -841,6 +844,7 @@ struct FnTypeId {
|
|||||||
bool is_naked;
|
bool is_naked;
|
||||||
bool is_cold;
|
bool is_cold;
|
||||||
bool is_extern;
|
bool is_extern;
|
||||||
|
bool is_inline;
|
||||||
FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count];
|
FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1063,7 +1067,6 @@ struct FnTableEntry {
|
|||||||
ZigList<LabelTableEntry *> all_labels;
|
ZigList<LabelTableEntry *> all_labels;
|
||||||
Buf symbol_name;
|
Buf symbol_name;
|
||||||
TypeTableEntry *type_entry; // function type
|
TypeTableEntry *type_entry; // function type
|
||||||
bool is_inline;
|
|
||||||
bool internal_linkage;
|
bool internal_linkage;
|
||||||
bool is_extern;
|
bool is_extern;
|
||||||
bool is_test;
|
bool is_test;
|
||||||
@ -1172,8 +1175,8 @@ struct CodeGen {
|
|||||||
|
|
||||||
ZigList<ImportTableEntry *> import_queue;
|
ZigList<ImportTableEntry *> import_queue;
|
||||||
int import_queue_index;
|
int import_queue_index;
|
||||||
ZigList<AstNode *> export_queue;
|
ZigList<AstNode *> resolve_queue;
|
||||||
int export_queue_index;
|
int resolve_queue_index;
|
||||||
ZigList<AstNode *> use_queue;
|
ZigList<AstNode *> use_queue;
|
||||||
int use_queue_index;
|
int use_queue_index;
|
||||||
|
|
||||||
|
476
src/analyze.cpp
476
src/analyze.cpp
@ -32,6 +32,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
|
|||||||
static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
|
static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
|
||||||
static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn,
|
static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn,
|
||||||
bool depends_on_compile_var);
|
bool depends_on_compile_var);
|
||||||
|
static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node,
|
||||||
|
TypeTableEntry *type_entry, bool depends_on_compile_var);
|
||||||
static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type,
|
static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type,
|
||||||
bool depends_on_compile_var);
|
bool depends_on_compile_var);
|
||||||
static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
|
static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
|
||||||
@ -874,7 +876,8 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
|
|||||||
fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);
|
fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);
|
||||||
fn_type_id.is_naked = is_naked;
|
fn_type_id.is_naked = is_naked;
|
||||||
fn_type_id.is_cold = is_cold;
|
fn_type_id.is_cold = is_cold;
|
||||||
fn_type_id.param_count = node->data.fn_proto.params.length;
|
fn_type_id.is_inline = fn_proto->is_inline;
|
||||||
|
fn_type_id.param_count = fn_proto->params.length;
|
||||||
|
|
||||||
if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) {
|
if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) {
|
||||||
fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count);
|
fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count);
|
||||||
@ -883,15 +886,52 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn_type_id.is_var_args = fn_proto->is_var_args;
|
fn_type_id.is_var_args = fn_proto->is_var_args;
|
||||||
fn_type_id.return_type = analyze_type_expr(g, import, context, node->data.fn_proto.return_type);
|
fn_type_id.return_type = analyze_type_expr(g, import, context, fn_proto->return_type);
|
||||||
|
|
||||||
if (fn_type_id.return_type->id == TypeTableEntryIdInvalid) {
|
switch (fn_type_id.return_type->id) {
|
||||||
fn_proto->skip = true;
|
case TypeTableEntryIdInvalid:
|
||||||
|
fn_proto->skip = true;
|
||||||
|
break;
|
||||||
|
case TypeTableEntryIdNumLitFloat:
|
||||||
|
case TypeTableEntryIdNumLitInt:
|
||||||
|
case TypeTableEntryIdUndefLit:
|
||||||
|
case TypeTableEntryIdNamespace:
|
||||||
|
case TypeTableEntryIdGenericFn:
|
||||||
|
fn_proto->skip = true;
|
||||||
|
add_node_error(g, fn_proto->return_type,
|
||||||
|
buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name)));
|
||||||
|
break;
|
||||||
|
case TypeTableEntryIdMetaType:
|
||||||
|
if (!fn_proto->is_inline) {
|
||||||
|
fn_proto->skip = true;
|
||||||
|
add_node_error(g, fn_proto->return_type,
|
||||||
|
buf_sprintf("function with return type '%s' must be declared inline",
|
||||||
|
buf_ptr(&fn_type_id.return_type->name)));
|
||||||
|
return g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TypeTableEntryIdUnreachable:
|
||||||
|
case TypeTableEntryIdVoid:
|
||||||
|
case TypeTableEntryIdBool:
|
||||||
|
case TypeTableEntryIdInt:
|
||||||
|
case TypeTableEntryIdFloat:
|
||||||
|
case TypeTableEntryIdPointer:
|
||||||
|
case TypeTableEntryIdArray:
|
||||||
|
case TypeTableEntryIdStruct:
|
||||||
|
case TypeTableEntryIdMaybe:
|
||||||
|
case TypeTableEntryIdErrorUnion:
|
||||||
|
case TypeTableEntryIdPureError:
|
||||||
|
case TypeTableEntryIdEnum:
|
||||||
|
case TypeTableEntryIdUnion:
|
||||||
|
case TypeTableEntryIdFn:
|
||||||
|
case TypeTableEntryIdTypeDecl:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fn_type_id.param_count; i += 1) {
|
for (int i = 0; i < fn_type_id.param_count; i += 1) {
|
||||||
AstNode *child = node->data.fn_proto.params.at(i);
|
AstNode *child = fn_proto->params.at(i);
|
||||||
assert(child->type == NodeTypeParamDecl);
|
assert(child->type == NodeTypeParamDecl);
|
||||||
|
|
||||||
TypeTableEntry *type_entry = analyze_type_expr(g, import, context,
|
TypeTableEntry *type_entry = analyze_type_expr(g, import, context,
|
||||||
child->data.param_decl.type);
|
child->data.param_decl.type);
|
||||||
switch (type_entry->id) {
|
switch (type_entry->id) {
|
||||||
@ -901,13 +941,20 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
|
|||||||
case TypeTableEntryIdNumLitFloat:
|
case TypeTableEntryIdNumLitFloat:
|
||||||
case TypeTableEntryIdNumLitInt:
|
case TypeTableEntryIdNumLitInt:
|
||||||
case TypeTableEntryIdUndefLit:
|
case TypeTableEntryIdUndefLit:
|
||||||
case TypeTableEntryIdMetaType:
|
|
||||||
case TypeTableEntryIdUnreachable:
|
case TypeTableEntryIdUnreachable:
|
||||||
case TypeTableEntryIdNamespace:
|
case TypeTableEntryIdNamespace:
|
||||||
case TypeTableEntryIdGenericFn:
|
case TypeTableEntryIdGenericFn:
|
||||||
fn_proto->skip = true;
|
fn_proto->skip = true;
|
||||||
add_node_error(g, child->data.param_decl.type,
|
add_node_error(g, child->data.param_decl.type,
|
||||||
buf_sprintf("parameter of type '%s' not allowed'", buf_ptr(&type_entry->name)));
|
buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
|
||||||
|
break;
|
||||||
|
case TypeTableEntryIdMetaType:
|
||||||
|
if (!child->data.param_decl.is_inline) {
|
||||||
|
fn_proto->skip = true;
|
||||||
|
add_node_error(g, child->data.param_decl.type,
|
||||||
|
buf_sprintf("parameter of type '%s' must be declared inline",
|
||||||
|
buf_ptr(&type_entry->name)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TypeTableEntryIdVoid:
|
case TypeTableEntryIdVoid:
|
||||||
case TypeTableEntryIdBool:
|
case TypeTableEntryIdBool:
|
||||||
@ -998,8 +1045,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_table_entry->is_inline = fn_proto->is_inline;
|
|
||||||
|
|
||||||
bool is_cold = false;
|
bool is_cold = false;
|
||||||
bool is_naked = false;
|
bool is_naked = false;
|
||||||
bool is_test = false;
|
bool is_test = false;
|
||||||
@ -1095,7 +1140,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fn_table_entry->is_inline && fn_table_entry->is_noinline) {
|
if (fn_proto->is_inline && fn_table_entry->is_noinline) {
|
||||||
add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
|
add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
|
||||||
fn_proto->skip = true;
|
fn_proto->skip = true;
|
||||||
return;
|
return;
|
||||||
@ -1109,10 +1154,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
|||||||
symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name));
|
symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name),
|
if (fn_table_entry->fn_def_node) {
|
||||||
fn_type->data.fn.raw_type_ref);
|
BlockContext *context = new_block_context(fn_table_entry->fn_def_node, containing_context);
|
||||||
|
fn_table_entry->fn_def_node->data.fn_def.block_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
if (fn_table_entry->is_inline) {
|
fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref);
|
||||||
|
|
||||||
|
if (fn_proto->is_inline) {
|
||||||
LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
|
LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
|
||||||
}
|
}
|
||||||
if (fn_table_entry->is_noinline) {
|
if (fn_table_entry->is_noinline) {
|
||||||
@ -1150,9 +1199,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
|||||||
fn_type->di_type, fn_table_entry->internal_linkage,
|
fn_type->di_type, fn_table_entry->internal_linkage,
|
||||||
is_definition, scope_line, flags, is_optimized, nullptr);
|
is_definition, scope_line, flags, is_optimized, nullptr);
|
||||||
|
|
||||||
BlockContext *context = new_block_context(fn_table_entry->fn_def_node, containing_context);
|
fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
|
||||||
fn_table_entry->fn_def_node->data.fn_def.block_context = context;
|
|
||||||
context->di_scope = LLVMZigSubprogramToScope(subprogram);
|
|
||||||
ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram);
|
ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1176,6 +1223,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
assert(enum_type->di_type);
|
assert(enum_type->di_type);
|
||||||
|
|
||||||
enum_type->deep_const = true;
|
enum_type->deep_const = true;
|
||||||
@ -1370,7 +1418,7 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
assert(struct_type->di_type);
|
assert(struct_type->di_type);
|
||||||
|
|
||||||
struct_type->deep_const = true;
|
struct_type->deep_const = true;
|
||||||
@ -1496,38 +1544,30 @@ static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||||
if (node->type == NodeTypeFnProto) {
|
assert(node->type == NodeTypeContainerDecl);
|
||||||
if (node->data.fn_proto.generic_params_is_var_args) {
|
|
||||||
add_node_error(g, node, buf_sprintf("generic parameters cannot be var args"));
|
|
||||||
node->data.fn_proto.skip = true;
|
|
||||||
node->data.fn_proto.generic_fn_type = g->builtin_types.entry_invalid;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->data.fn_proto.generic_fn_type = get_generic_fn_type(g, node);
|
if (node->data.struct_decl.generic_params_is_var_args) {
|
||||||
} else if (node->type == NodeTypeContainerDecl) {
|
add_node_error(g, node, buf_sprintf("generic parameters cannot be var args"));
|
||||||
if (node->data.struct_decl.generic_params_is_var_args) {
|
node->data.struct_decl.skip = true;
|
||||||
add_node_error(g, node, buf_sprintf("generic parameters cannot be var args"));
|
node->data.struct_decl.generic_fn_type = g->builtin_types.entry_invalid;
|
||||||
node->data.struct_decl.skip = true;
|
return;
|
||||||
node->data.struct_decl.generic_fn_type = g->builtin_types.entry_invalid;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node);
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node,
|
static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node,
|
||||||
BlockContext *containing_context)
|
BlockContext *containing_context)
|
||||||
{
|
{
|
||||||
|
assert(proto_node->type == NodeTypeFnProto);
|
||||||
|
|
||||||
if (proto_node->data.fn_proto.skip) {
|
if (proto_node->data.fn_proto.skip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_generic_instance = (proto_node->data.fn_proto.generic_params.length > 0);
|
bool is_generic_instance = proto_node->data.fn_proto.generic_proto_node;
|
||||||
|
bool is_generic_fn = proto_node->data.fn_proto.inline_arg_count > 0;
|
||||||
|
assert(!is_generic_instance || !is_generic_fn);
|
||||||
|
|
||||||
AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl;
|
AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl;
|
||||||
Buf *proto_name = &proto_node->data.fn_proto.name;
|
Buf *proto_name = &proto_node->data.fn_proto.name;
|
||||||
@ -1551,43 +1591,52 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
|
|||||||
|
|
||||||
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_');
|
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_');
|
||||||
|
|
||||||
g->fn_protos.append(fn_table_entry);
|
|
||||||
|
|
||||||
if (fn_def_node) {
|
|
||||||
g->fn_defs.append(fn_table_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_main_fn = !is_generic_instance &&
|
|
||||||
!parent_decl && (import == g->root_import) &&
|
|
||||||
buf_eql_str(proto_name, "main");
|
|
||||||
if (is_main_fn) {
|
|
||||||
g->main_fn = fn_table_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
|
proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
|
||||||
resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context);
|
|
||||||
|
|
||||||
if (is_main_fn && !g->link_libc) {
|
if (is_generic_fn) {
|
||||||
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
|
fn_table_entry->type_entry = get_generic_fn_type(g, proto_node);
|
||||||
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
|
|
||||||
if (actual_return_type != err_void) {
|
if (is_extern || proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModExport) {
|
||||||
AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
|
for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
|
||||||
add_node_error(g, return_type_node,
|
AstNode *param_decl_node = proto_node->data.fn_proto.params.at(i);
|
||||||
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
|
if (param_decl_node->data.param_decl.is_inline) {
|
||||||
buf_ptr(&actual_return_type->name)));
|
proto_node->data.fn_proto.skip = true;
|
||||||
|
add_node_error(g, param_decl_node,
|
||||||
|
buf_sprintf("inline parameter not allowed in extern function"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
g->fn_protos.append(fn_table_entry);
|
||||||
|
|
||||||
|
if (fn_def_node) {
|
||||||
|
g->fn_defs.append(fn_table_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_main_fn = !is_generic_instance &&
|
||||||
|
!parent_decl && (import == g->root_import) &&
|
||||||
|
buf_eql_str(proto_name, "main");
|
||||||
|
if (is_main_fn) {
|
||||||
|
g->main_fn = fn_table_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context);
|
||||||
|
|
||||||
|
if (is_main_fn && !g->link_libc) {
|
||||||
|
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
|
||||||
|
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
|
||||||
|
if (actual_return_type != err_void) {
|
||||||
|
AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
|
||||||
|
add_node_error(g, return_type_node,
|
||||||
|
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
|
||||||
|
buf_ptr(&actual_return_type->name)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) {
|
|
||||||
if (proto_node->data.fn_proto.generic_params.length > 0) {
|
|
||||||
return preview_generic_fn_proto(g, import, proto_node);
|
|
||||||
} else {
|
|
||||||
return preview_fn_proto_instance(g, import, proto_node, proto_node->block_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
|
static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
|
||||||
assert(node->type == NodeTypeContainerDecl);
|
assert(node->type == NodeTypeContainerDecl);
|
||||||
|
|
||||||
@ -1683,7 +1732,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only)
|
|||||||
|
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeFnProto:
|
case NodeTypeFnProto:
|
||||||
preview_fn_proto(g, import, node);
|
preview_fn_proto_instance(g, import, node, node->block_context);
|
||||||
break;
|
break;
|
||||||
case NodeTypeContainerDecl:
|
case NodeTypeContainerDecl:
|
||||||
resolve_struct_decl(g, import, node);
|
resolve_struct_decl(g, import, node);
|
||||||
@ -2600,7 +2649,11 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
|
|||||||
|
|
||||||
node->data.field_access_expr.is_member_fn = true;
|
node->data.field_access_expr.is_member_fn = true;
|
||||||
FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
|
FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
|
||||||
return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
|
if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
|
||||||
|
return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false);
|
||||||
|
} else {
|
||||||
|
return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'",
|
add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'",
|
||||||
buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
|
buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
|
||||||
@ -3004,13 +3057,11 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod
|
|||||||
VariableTableEntry *var = decl_node->data.variable_declaration.variable;
|
VariableTableEntry *var = decl_node->data.variable_declaration.variable;
|
||||||
return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var);
|
return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var);
|
||||||
} else if (decl_node->type == NodeTypeFnProto) {
|
} else if (decl_node->type == NodeTypeFnProto) {
|
||||||
if (decl_node->data.fn_proto.generic_params.length > 0) {
|
FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
|
||||||
TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type;
|
assert(fn_entry->type_entry);
|
||||||
assert(type_entry);
|
if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
|
||||||
return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var);
|
return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var);
|
||||||
} else {
|
} else {
|
||||||
FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
|
|
||||||
assert(fn_entry->type_entry);
|
|
||||||
return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var);
|
return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var);
|
||||||
}
|
}
|
||||||
} else if (decl_node->type == NodeTypeContainerDecl) {
|
} else if (decl_node->type == NodeTypeContainerDecl) {
|
||||||
@ -5238,6 +5289,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
|
|||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known
|
||||||
|
// at compile time. Otherwise this is a function pointer call.
|
||||||
static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
|
TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
|
||||||
AstNode *struct_node)
|
AstNode *struct_node)
|
||||||
@ -5248,26 +5301,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||||||
return fn_type;
|
return fn_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// count parameters
|
// The function call might include inline parameters which we need to ignore according to the
|
||||||
int src_param_count = fn_type->data.fn.fn_type_id.param_count;
|
// fn_type.
|
||||||
int actual_param_count = node->data.fn_call_expr.params.length;
|
FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
|
||||||
|
AstNode *generic_proto_node = fn_table_entry ?
|
||||||
|
fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr;
|
||||||
|
|
||||||
if (struct_node) {
|
// count parameters
|
||||||
actual_param_count += 1;
|
int struct_node_1_or_0 = struct_node ? 1 : 0;
|
||||||
}
|
int src_param_count = fn_type->data.fn.fn_type_id.param_count +
|
||||||
|
(generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0);
|
||||||
|
int call_param_count = node->data.fn_call_expr.params.length;
|
||||||
|
|
||||||
bool ok_invocation = true;
|
bool ok_invocation = true;
|
||||||
|
|
||||||
if (fn_type->data.fn.fn_type_id.is_var_args) {
|
if (fn_type->data.fn.fn_type_id.is_var_args) {
|
||||||
if (actual_param_count < src_param_count) {
|
if (call_param_count < src_param_count - struct_node_1_or_0) {
|
||||||
ok_invocation = false;
|
ok_invocation = false;
|
||||||
add_node_error(g, node,
|
add_node_error(g, node,
|
||||||
buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count));
|
buf_sprintf("expected at least %d arguments, got %d", src_param_count, call_param_count));
|
||||||
}
|
}
|
||||||
} else if (src_param_count != actual_param_count) {
|
} else if (src_param_count - struct_node_1_or_0 != call_param_count) {
|
||||||
ok_invocation = false;
|
ok_invocation = false;
|
||||||
add_node_error(g, node,
|
add_node_error(g, node,
|
||||||
buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count));
|
buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool all_args_const_expr = true;
|
bool all_args_const_expr = true;
|
||||||
@ -5281,17 +5338,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||||||
|
|
||||||
// analyze each parameter. in the case of a method, we already analyzed the
|
// 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.
|
// 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) {
|
int next_type_i = struct_node_1_or_0;
|
||||||
AstNode **child = &node->data.fn_call_expr.params.at(i);
|
for (int call_i = 0; call_i < call_param_count; call_i += 1) {
|
||||||
|
int proto_i = call_i + struct_node_1_or_0;
|
||||||
|
AstNode **param_node = &node->data.fn_call_expr.params.at(call_i);
|
||||||
// determine the expected type for each parameter
|
// determine the expected type for each parameter
|
||||||
TypeTableEntry *expected_param_type = nullptr;
|
TypeTableEntry *expected_param_type = nullptr;
|
||||||
int fn_proto_i = i + (struct_node ? 1 : 0);
|
if (proto_i < src_param_count) {
|
||||||
if (fn_proto_i < src_param_count) {
|
if (generic_proto_node &&
|
||||||
expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type;
|
generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
|
||||||
}
|
{
|
||||||
analyze_expression(g, import, context, expected_param_type, *child);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ConstExprValue *const_arg_val = &get_resolved_expr(*child)->const_val;
|
FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i];
|
||||||
|
next_type_i += 1;
|
||||||
|
|
||||||
|
expected_param_type = param_info->type;
|
||||||
|
}
|
||||||
|
TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node);
|
||||||
|
if (param_type->id == TypeTableEntryIdInvalid) {
|
||||||
|
return param_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val;
|
||||||
if (!const_arg_val->ok) {
|
if (!const_arg_val->ok) {
|
||||||
all_args_const_expr = false;
|
all_args_const_expr = false;
|
||||||
}
|
}
|
||||||
@ -5303,7 +5373,6 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
|
|
||||||
ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
|
ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
|
||||||
if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
|
if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
|
||||||
if (fn_table_entry->anal_state == FnAnalStateReady) {
|
if (fn_table_entry->anal_state == FnAnalStateReady) {
|
||||||
@ -5335,14 +5404,103 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import,
|
||||||
TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node)
|
BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node,
|
||||||
|
FnTableEntry *fn_table_entry, AstNode *struct_node)
|
||||||
{
|
{
|
||||||
assert(node->type == NodeTypeFnCallExpr);
|
assert(call_node->type == NodeTypeFnCallExpr);
|
||||||
|
assert(fn_table_entry);
|
||||||
|
|
||||||
node->data.fn_call_expr.fn_entry = fn_table_entry;
|
AstNode *decl_node = fn_table_entry->proto_node;
|
||||||
|
|
||||||
return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node);
|
// count parameters
|
||||||
|
int struct_node_1_or_0 = (struct_node ? 1 : 0);
|
||||||
|
int src_param_count = decl_node->data.fn_proto.params.length;
|
||||||
|
int call_param_count = call_node->data.fn_call_expr.params.length;
|
||||||
|
|
||||||
|
if (src_param_count != call_param_count + struct_node_1_or_0) {
|
||||||
|
add_node_error(g, call_node,
|
||||||
|
buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count));
|
||||||
|
return g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inline_arg_count = decl_node->data.fn_proto.inline_arg_count;
|
||||||
|
assert(inline_arg_count > 0);
|
||||||
|
|
||||||
|
BlockContext *child_context = decl_node->owner->block_context;
|
||||||
|
int next_generic_param_index = 0;
|
||||||
|
|
||||||
|
GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
|
||||||
|
generic_fn_type_id->decl_node = decl_node;
|
||||||
|
generic_fn_type_id->generic_param_count = inline_arg_count;
|
||||||
|
generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_arg_count);
|
||||||
|
|
||||||
|
for (int call_i = 0; call_i < call_param_count; call_i += 1) {
|
||||||
|
int proto_i = call_i + struct_node_1_or_0;
|
||||||
|
AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i);
|
||||||
|
assert(generic_param_decl_node->type == NodeTypeParamDecl);
|
||||||
|
bool is_inline = generic_param_decl_node->data.param_decl.is_inline;
|
||||||
|
if (!is_inline) continue;
|
||||||
|
|
||||||
|
AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
|
||||||
|
TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context,
|
||||||
|
*generic_param_type_node);
|
||||||
|
if (expected_param_type->id == TypeTableEntryIdInvalid) {
|
||||||
|
return expected_param_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i);
|
||||||
|
TypeTableEntry *param_type = analyze_expression(g, import, parent_context,
|
||||||
|
expected_param_type, *param_node);
|
||||||
|
if (param_type->id == TypeTableEntryIdInvalid) {
|
||||||
|
return param_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set child_context so that the previous param is in scope
|
||||||
|
child_context = new_block_context(generic_param_decl_node, child_context);
|
||||||
|
|
||||||
|
ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
|
||||||
|
if (const_val->ok) {
|
||||||
|
add_local_var(g, generic_param_decl_node, decl_node->owner, child_context,
|
||||||
|
&generic_param_decl_node->data.param_decl.name, param_type, true, *param_node);
|
||||||
|
} else {
|
||||||
|
add_node_error(g, *param_node,
|
||||||
|
buf_sprintf("unable to evaluate constant expression for inline parameter"));
|
||||||
|
|
||||||
|
return g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericParamValue *generic_param_value =
|
||||||
|
&generic_fn_type_id->generic_params[next_generic_param_index];
|
||||||
|
generic_param_value->type = param_type;
|
||||||
|
generic_param_value->node = *param_node;
|
||||||
|
next_generic_param_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next_generic_param_index == inline_arg_count);
|
||||||
|
|
||||||
|
auto entry = g->generic_table.maybe_get(generic_fn_type_id);
|
||||||
|
FnTableEntry *impl_fn;
|
||||||
|
if (entry) {
|
||||||
|
AstNode *impl_decl_node = entry->value;
|
||||||
|
assert(impl_decl_node->type == NodeTypeFnProto);
|
||||||
|
impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
|
||||||
|
} else {
|
||||||
|
AstNode *decl_node = generic_fn_type_id->decl_node;
|
||||||
|
AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node,
|
||||||
|
&g->next_node_index, AstCloneSpecialOmitInlineParams);
|
||||||
|
AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
|
||||||
|
impl_decl_node->data.fn_proto.inline_arg_count = 0;
|
||||||
|
impl_decl_node->data.fn_proto.generic_proto_node = decl_node;
|
||||||
|
|
||||||
|
preview_fn_proto_instance(g, import, impl_decl_node, child_context);
|
||||||
|
g->generic_table.put(generic_fn_type_id, impl_decl_node);
|
||||||
|
impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
call_node->data.fn_call_expr.fn_entry = impl_fn;
|
||||||
|
return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node,
|
||||||
|
impl_fn->type_entry, struct_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
|
static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
|
||||||
@ -5352,14 +5510,8 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
|
|||||||
assert(generic_fn_type->id == TypeTableEntryIdGenericFn);
|
assert(generic_fn_type->id == TypeTableEntryIdGenericFn);
|
||||||
|
|
||||||
AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
|
AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
|
||||||
ZigList<AstNode *> *generic_params;
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
if (decl_node->type == NodeTypeFnProto) {
|
ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params;
|
||||||
generic_params = &decl_node->data.fn_proto.generic_params;
|
|
||||||
} else if (decl_node->type == NodeTypeContainerDecl) {
|
|
||||||
generic_params = &decl_node->data.struct_decl.generic_params;
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
int expected_param_count = generic_params->length;
|
int expected_param_count = generic_params->length;
|
||||||
int actual_param_count = node->data.fn_call_expr.params.length;
|
int actual_param_count = node->data.fn_call_expr.params.length;
|
||||||
@ -5405,10 +5557,6 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
|
|||||||
} else {
|
} else {
|
||||||
add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression"));
|
add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression"));
|
||||||
|
|
||||||
add_local_var(g, generic_param_decl_node, decl_node->owner, child_context,
|
|
||||||
&generic_param_decl_node->data.param_decl.name, g->builtin_types.entry_invalid,
|
|
||||||
true, nullptr);
|
|
||||||
|
|
||||||
return g->builtin_types.entry_invalid;
|
return g->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5420,36 +5568,19 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
|
|||||||
auto entry = g->generic_table.maybe_get(generic_fn_type_id);
|
auto entry = g->generic_table.maybe_get(generic_fn_type_id);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
AstNode *impl_decl_node = entry->value;
|
AstNode *impl_decl_node = entry->value;
|
||||||
if (impl_decl_node->type == NodeTypeFnProto) {
|
assert(impl_decl_node->type == NodeTypeContainerDecl);
|
||||||
FnTableEntry *fn_table_entry = impl_decl_node->data.fn_proto.fn_table_entry;
|
TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
|
||||||
return resolve_expr_const_val_as_fn(g, node, fn_table_entry, false);
|
return resolve_expr_const_val_as_type(g, node, type_entry, false);
|
||||||
} else if (impl_decl_node->type == NodeTypeContainerDecl) {
|
|
||||||
TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
|
|
||||||
return resolve_expr_const_val_as_type(g, node, type_entry, false);
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a type from the generic parameters supplied
|
// make a type from the generic parameters supplied
|
||||||
if (decl_node->type == NodeTypeFnProto) {
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
AstNode *impl_fn_def_node = ast_clone_subtree(decl_node->data.fn_proto.fn_def_node, &g->next_node_index);
|
AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index);
|
||||||
AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
|
g->generic_table.put(generic_fn_type_id, impl_decl_node);
|
||||||
|
scan_struct_decl(g, import, child_context, impl_decl_node);
|
||||||
preview_fn_proto_instance(g, import, impl_decl_node, child_context);
|
TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
|
||||||
g->generic_table.put(generic_fn_type_id, impl_decl_node);
|
resolve_struct_type(g, import, type_entry);
|
||||||
FnTableEntry *fn_table_entry = impl_decl_node->data.fn_proto.fn_table_entry;
|
return resolve_expr_const_val_as_type(g, node, type_entry, false);
|
||||||
return resolve_expr_const_val_as_fn(g, node, fn_table_entry, false);
|
|
||||||
} else if (decl_node->type == NodeTypeContainerDecl) {
|
|
||||||
AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index);
|
|
||||||
g->generic_table.put(generic_fn_type_id, impl_decl_node);
|
|
||||||
scan_struct_decl(g, import, child_context, impl_decl_node);
|
|
||||||
TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
|
|
||||||
resolve_struct_type(g, import, type_entry);
|
|
||||||
return resolve_expr_const_val_as_type(g, node, type_entry, false);
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
@ -5487,10 +5618,32 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
|
|||||||
struct_node = nullptr;
|
struct_node = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return analyze_fn_call_raw(g, import, context, expected_type, node,
|
FnTableEntry *fn_table_entry = const_val->data.x_fn;
|
||||||
const_val->data.x_fn, struct_node);
|
node->data.fn_call_expr.fn_entry = fn_table_entry;
|
||||||
|
return analyze_fn_call_ptr(g, import, context, expected_type, node,
|
||||||
|
fn_table_entry->type_entry, struct_node);
|
||||||
} else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
|
} else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
|
||||||
return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
|
TypeTableEntry *generic_fn_type = const_val->data.x_type;
|
||||||
|
AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
|
||||||
|
if (decl_node->type == NodeTypeFnProto) {
|
||||||
|
AstNode *struct_node;
|
||||||
|
if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
|
||||||
|
fn_ref_expr->data.field_access_expr.is_member_fn)
|
||||||
|
{
|
||||||
|
struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
|
||||||
|
} else {
|
||||||
|
struct_node = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry;
|
||||||
|
if (fn_table_entry->proto_node->data.fn_proto.skip) {
|
||||||
|
return g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
return analyze_fn_call_with_inline_args(g, import, context, expected_type, node,
|
||||||
|
fn_table_entry, struct_node);
|
||||||
|
} else {
|
||||||
|
return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
add_node_error(g, fn_ref_expr,
|
add_node_error(g, fn_ref_expr,
|
||||||
buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
|
buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
|
||||||
@ -6367,7 +6520,9 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
|
|||||||
var->src_arg_index = i;
|
var->src_arg_index = i;
|
||||||
param_decl_node->data.param_decl.variable = var;
|
param_decl_node->data.param_decl.variable = var;
|
||||||
|
|
||||||
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
|
if (fn_type->data.fn.gen_param_info) {
|
||||||
|
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
|
||||||
|
}
|
||||||
|
|
||||||
if (!type->deep_const) {
|
if (!type->deep_const) {
|
||||||
fn_table_entry->is_pure = false;
|
fn_table_entry->is_pure = false;
|
||||||
@ -6406,11 +6561,11 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex
|
|||||||
tld->import = import;
|
tld->import = import;
|
||||||
tld->name = name;
|
tld->name = name;
|
||||||
|
|
||||||
bool want_as_export = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport);
|
bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport);
|
||||||
bool is_generic = (node->type == NodeTypeFnProto && node->data.fn_proto.generic_params.length > 0) ||
|
bool is_generic_container = (node->type == NodeTypeContainerDecl &&
|
||||||
(node->type == NodeTypeContainerDecl && node->data.struct_decl.generic_params.length > 0);
|
node->data.struct_decl.generic_params.length > 0);
|
||||||
if (!is_generic && want_as_export) {
|
if (want_to_resolve && !is_generic_container) {
|
||||||
g->export_queue.append(node);
|
g->resolve_queue.append(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
node->block_context = block_context;
|
node->block_context = block_context;
|
||||||
@ -6425,6 +6580,18 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fn_proto_inline_arg_count(AstNode *proto_node) {
|
||||||
|
assert(proto_node->type == NodeTypeFnProto);
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
|
||||||
|
AstNode *param_node = proto_node->data.fn_proto.params.at(i);
|
||||||
|
assert(param_node->type == NodeTypeParamDecl);
|
||||||
|
result += param_node->data.param_decl.is_inline ? 1 : 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
|
static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeRoot:
|
case NodeTypeRoot:
|
||||||
@ -6467,6 +6634,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte
|
|||||||
add_node_error(g, node, buf_sprintf("missing function name"));
|
add_node_error(g, node, buf_sprintf("missing function name"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
node->data.fn_proto.inline_arg_count = fn_proto_inline_arg_count(node);
|
||||||
|
|
||||||
add_top_level_decl(g, import, context, node, fn_name);
|
add_top_level_decl(g, import, context, node, fn_name);
|
||||||
break;
|
break;
|
||||||
@ -6692,8 +6860,8 @@ void semantic_analyze(CodeGen *g) {
|
|||||||
resolve_use_decl(g, use_decl_node);
|
resolve_use_decl(g, use_decl_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; g->export_queue_index < g->export_queue.length; g->export_queue_index += 1) {
|
for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
|
||||||
AstNode *decl_node = g->export_queue.at(g->export_queue_index);
|
AstNode *decl_node = g->resolve_queue.at(g->resolve_queue_index);
|
||||||
bool pointer_only = false;
|
bool pointer_only = false;
|
||||||
resolve_top_level_decl(g, decl_node, pointer_only);
|
resolve_top_level_decl(g, decl_node, pointer_only);
|
||||||
}
|
}
|
||||||
@ -6983,11 +7151,9 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
|
|||||||
FnTypeParamInfo *a_param_info = &a->param_info[i];
|
FnTypeParamInfo *a_param_info = &a->param_info[i];
|
||||||
FnTypeParamInfo *b_param_info = &b->param_info[i];
|
FnTypeParamInfo *b_param_info = &b->param_info[i];
|
||||||
|
|
||||||
if (a_param_info->type != b_param_info->type) {
|
if (a_param_info->type != b_param_info->type ||
|
||||||
return false;
|
a_param_info->is_noalias != b_param_info->is_noalias)
|
||||||
}
|
{
|
||||||
|
|
||||||
if (a_param_info->is_noalias != b_param_info->is_noalias) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,7 +353,8 @@ static void render_node(AstRender *ar, AstNode *node) {
|
|||||||
assert(param_decl->type == NodeTypeParamDecl);
|
assert(param_decl->type == NodeTypeParamDecl);
|
||||||
if (buf_len(¶m_decl->data.param_decl.name) > 0) {
|
if (buf_len(¶m_decl->data.param_decl.name) > 0) {
|
||||||
const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
|
const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
|
||||||
fprintf(ar->f, "%s", noalias_str);
|
const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : "";
|
||||||
|
fprintf(ar->f, "%s%s", noalias_str, inline_str);
|
||||||
print_symbol(ar, ¶m_decl->data.param_decl.name);
|
print_symbol(ar, ¶m_decl->data.param_decl.name);
|
||||||
fprintf(ar->f, ": ");
|
fprintf(ar->f, ": ");
|
||||||
}
|
}
|
||||||
|
@ -1062,12 +1062,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
|||||||
|
|
||||||
TypeTableEntry *fn_type;
|
TypeTableEntry *fn_type;
|
||||||
LLVMValueRef fn_val;
|
LLVMValueRef fn_val;
|
||||||
|
AstNode *generic_proto_node;
|
||||||
if (fn_table_entry) {
|
if (fn_table_entry) {
|
||||||
fn_val = fn_table_entry->fn_value;
|
fn_val = fn_table_entry->fn_value;
|
||||||
fn_type = fn_table_entry->type_entry;
|
fn_type = fn_table_entry->type_entry;
|
||||||
|
generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node;
|
||||||
} else {
|
} else {
|
||||||
fn_val = gen_expr(g, fn_ref_expr);
|
fn_val = gen_expr(g, fn_ref_expr);
|
||||||
fn_type = get_expr_type(fn_ref_expr);
|
fn_type = get_expr_type(fn_ref_expr);
|
||||||
|
generic_proto_node = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
|
TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
|
||||||
@ -1093,8 +1096,14 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
|||||||
gen_param_index += 1;
|
gen_param_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fn_call_param_count; i += 1) {
|
for (int call_i = 0; call_i < fn_call_param_count; call_i += 1) {
|
||||||
AstNode *expr_node = node->data.fn_call_expr.params.at(i);
|
int proto_i = call_i + (struct_type ? 1 : 0);
|
||||||
|
if (generic_proto_node &&
|
||||||
|
generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AstNode *expr_node = node->data.fn_call_expr.params.at(call_i);
|
||||||
LLVMValueRef param_value = gen_expr(g, expr_node);
|
LLVMValueRef param_value = gen_expr(g, expr_node);
|
||||||
assert(param_value);
|
assert(param_value);
|
||||||
TypeTableEntry *param_type = get_expr_type(expr_node);
|
TypeTableEntry *param_type = get_expr_type(expr_node);
|
||||||
@ -3734,7 +3743,7 @@ static void delete_unused_builtin_fns(CodeGen *g) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) {
|
static bool should_skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) {
|
||||||
if (g->is_test_build) {
|
if (g->is_test_build) {
|
||||||
if (fn_entry->is_test) {
|
if (fn_entry->is_test) {
|
||||||
return false;
|
return false;
|
||||||
@ -3889,7 +3898,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
// Generate function prototypes
|
// Generate function prototypes
|
||||||
for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
|
for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
|
||||||
FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
|
FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
|
||||||
if (skip_fn_codegen(g, fn_table_entry)) {
|
if (should_skip_fn_codegen(g, fn_table_entry)) {
|
||||||
// huge time saver
|
// huge time saver
|
||||||
LLVMDeleteFunction(fn_table_entry->fn_value);
|
LLVMDeleteFunction(fn_table_entry->fn_value);
|
||||||
fn_table_entry->fn_value = nullptr;
|
fn_table_entry->fn_value = nullptr;
|
||||||
@ -3995,7 +4004,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
// Generate function definitions.
|
// Generate function definitions.
|
||||||
for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
|
for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
|
||||||
FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i);
|
FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i);
|
||||||
if (skip_fn_codegen(g, fn_table_entry)) {
|
if (should_skip_fn_codegen(g, fn_table_entry)) {
|
||||||
// huge time saver
|
// huge time saver
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
33
src/eval.cpp
33
src/eval.cpp
@ -884,9 +884,9 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val
|
|||||||
|
|
||||||
int param_count = node->data.fn_call_expr.params.length;
|
int param_count = node->data.fn_call_expr.params.length;
|
||||||
ConstExprValue *args = allocate<ConstExprValue>(param_count);
|
ConstExprValue *args = allocate<ConstExprValue>(param_count);
|
||||||
for (int i = 0; i < param_count; i += 1) {
|
for (int call_i = 0; call_i < param_count; call_i += 1) {
|
||||||
AstNode *param_expr_node = node->data.fn_call_expr.params.at(i);
|
AstNode *param_expr_node = node->data.fn_call_expr.params.at(call_i);
|
||||||
ConstExprValue *param_val = &args[i];
|
ConstExprValue *param_val = &args[call_i];
|
||||||
if (eval_expr(ef, param_expr_node, param_val)) return true;
|
if (eval_expr(ef, param_expr_node, param_val)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1291,6 +1291,13 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) {
|
static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) {
|
||||||
|
AstNode *acting_proto_node;
|
||||||
|
if (fn->proto_node->data.fn_proto.generic_proto_node) {
|
||||||
|
acting_proto_node = fn->proto_node->data.fn_proto.generic_proto_node;
|
||||||
|
} else {
|
||||||
|
acting_proto_node = fn->proto_node;
|
||||||
|
}
|
||||||
|
|
||||||
EvalFn ef = {0};
|
EvalFn ef = {0};
|
||||||
ef.root = efr;
|
ef.root = efr;
|
||||||
ef.fn = fn;
|
ef.fn = fn;
|
||||||
@ -1300,12 +1307,12 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args
|
|||||||
root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context;
|
root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context;
|
||||||
ef.scope_stack.append(root_scope);
|
ef.scope_stack.append(root_scope);
|
||||||
|
|
||||||
int param_count = fn->type_entry->data.fn.fn_type_id.param_count;
|
int param_count = acting_proto_node->data.fn_proto.params.length;
|
||||||
for (int i = 0; i < param_count; i += 1) {
|
for (int proto_i = 0; proto_i < param_count; proto_i += 1) {
|
||||||
AstNode *decl_param_node = fn->proto_node->data.fn_proto.params.at(i);
|
AstNode *decl_param_node = acting_proto_node->data.fn_proto.params.at(proto_i);
|
||||||
assert(decl_param_node->type == NodeTypeParamDecl);
|
assert(decl_param_node->type == NodeTypeParamDecl);
|
||||||
|
|
||||||
ConstExprValue *src_const_val = &args[i];
|
ConstExprValue *src_const_val = &args[proto_i];
|
||||||
assert(src_const_val->ok);
|
assert(src_const_val->ok);
|
||||||
|
|
||||||
root_scope->vars.add_one();
|
root_scope->vars.add_one();
|
||||||
@ -1315,7 +1322,6 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args
|
|||||||
}
|
}
|
||||||
|
|
||||||
return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val);
|
return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val,
|
bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val,
|
||||||
@ -1329,9 +1335,16 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va
|
|||||||
efr.call_node = node;
|
efr.call_node = node;
|
||||||
efr.branch_quota = branch_quota;
|
efr.branch_quota = branch_quota;
|
||||||
|
|
||||||
|
AstNode *acting_proto_node;
|
||||||
|
if (fn->proto_node->data.fn_proto.generic_proto_node) {
|
||||||
|
acting_proto_node = fn->proto_node->data.fn_proto.generic_proto_node;
|
||||||
|
} else {
|
||||||
|
acting_proto_node = fn->proto_node;
|
||||||
|
}
|
||||||
|
|
||||||
int call_param_count = node->data.fn_call_expr.params.length;
|
int call_param_count = node->data.fn_call_expr.params.length;
|
||||||
int type_param_count = fn->type_entry->data.fn.fn_type_id.param_count;
|
int proto_param_count = acting_proto_node->data.fn_proto.params.length;
|
||||||
ConstExprValue *args = allocate<ConstExprValue>(type_param_count);
|
ConstExprValue *args = allocate<ConstExprValue>(proto_param_count);
|
||||||
int next_arg_index = 0;
|
int next_arg_index = 0;
|
||||||
if (struct_node) {
|
if (struct_node) {
|
||||||
ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val;
|
ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val;
|
||||||
|
@ -747,7 +747,7 @@ static void ast_parse_directives(ParseContext *pc, int *token_index,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..."
|
ParamDecl = option("noalias" | "inline") option("Symbol" ":") TypeExpr | "..."
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
|
static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
|
||||||
Token *token = &pc->tokens->at(*token_index);
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
@ -763,6 +763,10 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
|
|||||||
node->data.param_decl.is_noalias = true;
|
node->data.param_decl.is_noalias = true;
|
||||||
*token_index += 1;
|
*token_index += 1;
|
||||||
token = &pc->tokens->at(*token_index);
|
token = &pc->tokens->at(*token_index);
|
||||||
|
} else if (token->id == TokenIdKeywordInline) {
|
||||||
|
node->data.param_decl.is_inline = true;
|
||||||
|
*token_index += 1;
|
||||||
|
token = &pc->tokens->at(*token_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf_resize(&node->data.param_decl.name, 0);
|
buf_resize(&node->data.param_decl.name, 0);
|
||||||
@ -2472,7 +2476,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FnProto = "fn" option("Symbol") option(ParamDeclList) ParamDeclList option("->" TypeExpr)
|
FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr)
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory,
|
static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory,
|
||||||
ZigList<AstNode*> *directives, VisibMod visib_mod)
|
ZigList<AstNode*> *directives, VisibMod visib_mod)
|
||||||
@ -2502,17 +2506,6 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
|
|||||||
|
|
||||||
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
||||||
|
|
||||||
Token *maybe_lparen = &pc->tokens->at(*token_index);
|
|
||||||
if (maybe_lparen->id == TokenIdLParen) {
|
|
||||||
for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
|
|
||||||
node->data.fn_proto.generic_params.append(node->data.fn_proto.params.at(i));
|
|
||||||
}
|
|
||||||
node->data.fn_proto.generic_params_is_var_args = node->data.fn_proto.is_var_args;
|
|
||||||
|
|
||||||
node->data.fn_proto.params.resize(0);
|
|
||||||
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token *next_token = &pc->tokens->at(*token_index);
|
Token *next_token = &pc->tokens->at(*token_index);
|
||||||
if (next_token->id == TokenIdArrow) {
|
if (next_token->id == TokenIdArrow) {
|
||||||
*token_index += 1;
|
*token_index += 1;
|
||||||
@ -2931,7 +2924,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
|||||||
case NodeTypeFnProto:
|
case NodeTypeFnProto:
|
||||||
visit_field(&node->data.fn_proto.return_type, visit, context);
|
visit_field(&node->data.fn_proto.return_type, visit, context);
|
||||||
visit_node_list(node->data.fn_proto.top_level_decl.directives, visit, context);
|
visit_node_list(node->data.fn_proto.top_level_decl.directives, visit, context);
|
||||||
visit_node_list(&node->data.fn_proto.generic_params, visit, context);
|
|
||||||
visit_node_list(&node->data.fn_proto.params, visit, context);
|
visit_node_list(&node->data.fn_proto.params, visit, context);
|
||||||
break;
|
break;
|
||||||
case NodeTypeFnDef:
|
case NodeTypeFnDef:
|
||||||
@ -3123,6 +3115,22 @@ static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clone_subtree_list_omit_inline_params(ZigList<AstNode *> *dest, ZigList<AstNode *> *src,
|
||||||
|
uint32_t *next_node_index)
|
||||||
|
{
|
||||||
|
memset(dest, 0, sizeof(ZigList<AstNode *>));
|
||||||
|
dest->ensure_capacity(src->length);
|
||||||
|
for (int i = 0; i < src->length; i += 1) {
|
||||||
|
AstNode *src_node = src->at(i);
|
||||||
|
assert(src_node->type == NodeTypeParamDecl);
|
||||||
|
if (src_node->data.param_decl.is_inline) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dest->append(ast_clone_subtree(src_node, next_node_index));
|
||||||
|
dest->last()->parent_field = &dest->last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src,
|
static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src,
|
||||||
uint32_t *next_node_index)
|
uint32_t *next_node_index)
|
||||||
{
|
{
|
||||||
@ -3133,20 +3141,26 @@ static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_node_index) {
|
static void clone_subtree_field_special(AstNode **dest, AstNode *src, uint32_t *next_node_index,
|
||||||
|
enum AstCloneSpecial special)
|
||||||
|
{
|
||||||
if (src) {
|
if (src) {
|
||||||
*dest = ast_clone_subtree(src, next_node_index);
|
*dest = ast_clone_subtree_special(src, next_node_index, special);
|
||||||
(*dest)->parent_field = dest;
|
(*dest)->parent_field = dest;
|
||||||
} else {
|
} else {
|
||||||
*dest = nullptr;
|
*dest = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_node_index) {
|
||||||
|
return clone_subtree_field_special(dest, src, next_node_index, AstCloneSpecialNone);
|
||||||
|
}
|
||||||
|
|
||||||
static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) {
|
static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) {
|
||||||
clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index);
|
clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
|
AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, enum AstCloneSpecial special) {
|
||||||
AstNode *new_node = allocate_nonzero<AstNode>(1);
|
AstNode *new_node = allocate_nonzero<AstNode>(1);
|
||||||
memcpy(new_node, old_node, sizeof(AstNode));
|
memcpy(new_node, old_node, sizeof(AstNode));
|
||||||
new_node->create_index = *next_node_index;
|
new_node->create_index = *next_node_index;
|
||||||
@ -3163,14 +3177,19 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
|
|||||||
next_node_index);
|
next_node_index);
|
||||||
clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type,
|
clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type,
|
||||||
next_node_index);
|
next_node_index);
|
||||||
clone_subtree_list(&new_node->data.fn_proto.generic_params,
|
|
||||||
&old_node->data.fn_proto.generic_params, next_node_index);
|
if (special == AstCloneSpecialOmitInlineParams) {
|
||||||
clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params,
|
clone_subtree_list_omit_inline_params(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params,
|
||||||
next_node_index);
|
next_node_index);
|
||||||
|
} else {
|
||||||
|
clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params,
|
||||||
|
next_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NodeTypeFnDef:
|
case NodeTypeFnDef:
|
||||||
clone_subtree_field(&new_node->data.fn_def.fn_proto, old_node->data.fn_def.fn_proto, next_node_index);
|
clone_subtree_field_special(&new_node->data.fn_def.fn_proto, old_node->data.fn_def.fn_proto,
|
||||||
|
next_node_index, special);
|
||||||
new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node;
|
new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node;
|
||||||
clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index);
|
clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index);
|
||||||
break;
|
break;
|
||||||
@ -3354,3 +3373,7 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
|
|||||||
|
|
||||||
return new_node;
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
|
||||||
|
return ast_clone_subtree_special(old_node, next_node_index, AstCloneSpecialNone);
|
||||||
|
}
|
||||||
|
@ -25,6 +25,13 @@ void ast_print(AstNode *node, int indent);
|
|||||||
void normalize_parent_ptrs(AstNode *node);
|
void normalize_parent_ptrs(AstNode *node);
|
||||||
|
|
||||||
AstNode *ast_clone_subtree(AstNode *node, uint32_t *next_node_index);
|
AstNode *ast_clone_subtree(AstNode *node, uint32_t *next_node_index);
|
||||||
|
|
||||||
|
enum AstCloneSpecial {
|
||||||
|
AstCloneSpecialNone,
|
||||||
|
AstCloneSpecialOmitInlineParams,
|
||||||
|
};
|
||||||
|
AstNode *ast_clone_subtree_special(AstNode *node, uint32_t *next_node_index, enum AstCloneSpecial special);
|
||||||
|
|
||||||
void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
|
void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@ const want_modification_safety = !@compile_var("is_release");
|
|||||||
const debug_u32 = if (want_modification_safety) u32 else void;
|
const debug_u32 = if (want_modification_safety) u32 else void;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub fn HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool) {
|
pub inline fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool) {
|
||||||
SmallHashMap(K, V, hash, eql, 8);
|
SmallHashMap(K, V, hash, eql, 8);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@ -70,7 +70,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
|
|
||||||
pub fn deinit(hm: &Self) {
|
pub fn deinit(hm: &Self) {
|
||||||
if (hm.entries.ptr != &hm.prealloc_entries[0]) {
|
if (hm.entries.ptr != &hm.prealloc_entries[0]) {
|
||||||
hm.allocator.free(hm.allocator, ([]u8)(hm.entries));
|
hm.allocator.free(Entry, hm.entries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (old_entries.ptr != &hm.prealloc_entries[0]) {
|
if (old_entries.ptr != &hm.prealloc_entries[0]) {
|
||||||
hm.allocator.free(hm.allocator, ([]u8)(old_entries));
|
hm.allocator.free(Entry, old_entries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_capacity(hm: &Self, capacity: isize) -> %void {
|
fn init_capacity(hm: &Self, capacity: isize) -> %void {
|
||||||
hm.entries = ([]Entry)(%return hm.allocator.alloc(hm.allocator, capacity * @sizeof(Entry)));
|
hm.entries = %return hm.allocator.alloc(Entry, capacity);
|
||||||
hm.size = 0;
|
hm.size = 0;
|
||||||
hm.max_distance_from_start_index = 0;
|
hm.max_distance_from_start_index = 0;
|
||||||
for (hm.entries) |*entry| {
|
for (hm.entries) |*entry| {
|
||||||
@ -180,7 +180,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
if (entry.distance_from_start_index < distance_from_start_index) {
|
if (entry.distance_from_start_index < distance_from_start_index) {
|
||||||
// robin hood to the rescue
|
// robin hood to the rescue
|
||||||
const tmp = *entry;
|
const tmp = *entry;
|
||||||
hm.max_distance_from_start_index = math.max(isize)(
|
hm.max_distance_from_start_index = math.max(isize,
|
||||||
hm.max_distance_from_start_index, distance_from_start_index);
|
hm.max_distance_from_start_index, distance_from_start_index);
|
||||||
*entry = Entry {
|
*entry = Entry {
|
||||||
.used = true,
|
.used = true,
|
||||||
@ -201,7 +201,8 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
hm.size += 1;
|
hm.size += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hm.max_distance_from_start_index = math.max(isize)(distance_from_start_index, hm.max_distance_from_start_index);
|
hm.max_distance_from_start_index = math.max(isize, distance_from_start_index,
|
||||||
|
hm.max_distance_from_start_index);
|
||||||
*entry = Entry {
|
*entry = Entry {
|
||||||
.used = true,
|
.used = true,
|
||||||
.distance_from_start_index = distance_from_start_index,
|
.distance_from_start_index = distance_from_start_index,
|
||||||
@ -231,9 +232,9 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
|
|||||||
}
|
}
|
||||||
|
|
||||||
var global_allocator = Allocator {
|
var global_allocator = Allocator {
|
||||||
.alloc = global_alloc,
|
.alloc_fn = global_alloc,
|
||||||
.realloc = global_realloc,
|
.realloc_fn = global_realloc,
|
||||||
.free = global_free,
|
.free_fn = global_free,
|
||||||
.context = null,
|
.context = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
54
std/io.zig
54
std/io.zig
@ -69,7 +69,7 @@ pub struct OutStream {
|
|||||||
const dest_space_left = os.buffer.len - os.index;
|
const dest_space_left = os.buffer.len - os.index;
|
||||||
|
|
||||||
while (src_bytes_left > 0) {
|
while (src_bytes_left > 0) {
|
||||||
const copy_amt = math.min(isize)(dest_space_left, src_bytes_left);
|
const copy_amt = math.min(isize, dest_space_left, src_bytes_left);
|
||||||
@memcpy(&os.buffer[os.index], &bytes[src_index], copy_amt);
|
@memcpy(&os.buffer[os.index], &bytes[src_index], copy_amt);
|
||||||
os.index += copy_amt;
|
os.index += copy_amt;
|
||||||
if (os.index == os.buffer.len) {
|
if (os.index == os.buffer.len) {
|
||||||
@ -208,59 +208,47 @@ pub struct InStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub error InvalidChar;
|
pub fn parse_unsigned(inline T: type, buf: []u8, radix: u8) -> %T {
|
||||||
pub error Overflow;
|
|
||||||
|
|
||||||
pub fn parse_unsigned(T: type)(buf: []u8, radix: u8) -> %T {
|
|
||||||
var x: T = 0;
|
var x: T = 0;
|
||||||
|
|
||||||
for (buf) |c| {
|
for (buf) |c| {
|
||||||
const digit = char_to_digit(c);
|
const digit = %return char_to_digit(c, radix);
|
||||||
|
x = %return math.mul_overflow(T, x, radix);
|
||||||
if (digit >= radix) {
|
x = %return math.add_overflow(T, x, digit);
|
||||||
return error.InvalidChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// x *= radix
|
|
||||||
if (@mul_with_overflow(T, x, radix, &x)) {
|
|
||||||
return error.Overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// x += digit
|
|
||||||
if (@add_with_overflow(T, x, digit, &x)) {
|
|
||||||
return error.Overflow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char_to_digit(c: u8) -> u8 {
|
pub error InvalidChar;
|
||||||
// TODO use switch with range
|
fn char_to_digit(c: u8, radix: u8) -> %u8 {
|
||||||
if ('0' <= c && c <= '9') {
|
const value = if ('0' <= c && c <= '9') {
|
||||||
c - '0'
|
c - '0'
|
||||||
} else if ('A' <= c && c <= 'Z') {
|
} else if ('A' <= c && c <= 'Z') {
|
||||||
c - 'A' + 10
|
c - 'A' + 10
|
||||||
} else if ('a' <= c && c <= 'z') {
|
} else if ('a' <= c && c <= 'z') {
|
||||||
c - 'a' + 10
|
c - 'a' + 10
|
||||||
} else {
|
} else {
|
||||||
@max_value(u8)
|
return error.InvalidChar;
|
||||||
}
|
};
|
||||||
|
return if (value >= radix) error.InvalidChar else value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buf_print_signed(T: type)(out_buf: []u8, x: T) -> isize {
|
pub fn buf_print_signed(inline T: type, out_buf: []u8, x: T) -> isize {
|
||||||
const uint = @int_type(false, T.bit_count, false);
|
const uint = @int_type(false, T.bit_count, false);
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
out_buf[0] = '-';
|
out_buf[0] = '-';
|
||||||
return 1 + buf_print_unsigned(uint)(out_buf[1...], uint(-(x + 1)) + 1);
|
return 1 + buf_print_unsigned(uint, out_buf[1...], uint(-(x + 1)) + 1);
|
||||||
} else {
|
} else {
|
||||||
return buf_print_unsigned(uint)(out_buf, uint(x));
|
return buf_print_unsigned(uint, out_buf, uint(x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const buf_print_i64 = buf_print_signed(i64);
|
pub fn buf_print_i64(out_buf: []u8, x: i64) -> isize {
|
||||||
|
buf_print_signed(i64, out_buf, x)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize {
|
pub fn buf_print_unsigned(inline T: type, out_buf: []u8, x: T) -> isize {
|
||||||
var buf: [max_u64_base10_digits]u8 = undefined;
|
var buf: [max_u64_base10_digits]u8 = undefined;
|
||||||
var a = x;
|
var a = x;
|
||||||
var index: isize = buf.len;
|
var index: isize = buf.len;
|
||||||
@ -281,7 +269,9 @@ pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const buf_print_u64 = buf_print_unsigned(u64);
|
pub fn buf_print_u64(out_buf: []u8, x: u64) -> isize {
|
||||||
|
buf_print_unsigned(u64, out_buf, x)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
|
pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
|
||||||
const numExpBits = 11;
|
const numExpBits = 11;
|
||||||
@ -409,7 +399,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
|
|||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn parse_u64_digit_too_big() {
|
fn parse_u64_digit_too_big() {
|
||||||
parse_unsigned(u64)("123a", 10) %% |err| {
|
parse_unsigned(u64, "123a", 10) %% |err| {
|
||||||
if (err == error.InvalidChar) return;
|
if (err == error.InvalidChar) return;
|
||||||
unreachable{};
|
unreachable{};
|
||||||
};
|
};
|
||||||
|
29
std/list.zig
29
std/list.zig
@ -2,59 +2,58 @@ const assert = @import("debug.zig").assert;
|
|||||||
const mem = @import("mem.zig");
|
const mem = @import("mem.zig");
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
/*
|
pub inline fn List(inline T: type) -> type {
|
||||||
pub fn List(T: type) -> type {
|
|
||||||
SmallList(T, 8)
|
SmallList(T, 8)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct SmallList(T: type, STATIC_SIZE: isize) {
|
pub struct SmallList(T: type, STATIC_SIZE: isize) {
|
||||||
|
const Self = SmallList(T, STATIC_SIZE);
|
||||||
|
|
||||||
items: []T,
|
items: []T,
|
||||||
length: isize,
|
length: isize,
|
||||||
prealloc_items: [STATIC_SIZE]T,
|
prealloc_items: [STATIC_SIZE]T,
|
||||||
allocator: &Allocator,
|
allocator: &Allocator,
|
||||||
|
|
||||||
pub fn init(l: &SmallList(T, STATIC_SIZE), allocator: &Allocator) {
|
pub fn init(l: &Self, allocator: &Allocator) {
|
||||||
l.items = l.prealloc_items[0...];
|
l.items = l.prealloc_items[0...];
|
||||||
l.length = 0;
|
l.length = 0;
|
||||||
l.allocator = allocator;
|
l.allocator = allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(l: &SmallList(T, STATIC_SIZE)) {
|
pub fn deinit(l: &Self) {
|
||||||
if (l.items.ptr != &l.prealloc_items[0]) {
|
if (l.items.ptr != &l.prealloc_items[0]) {
|
||||||
l.allocator.free(l.allocator, ([]u8)(l.items));
|
l.allocator.free(T, l.items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append(l: &SmallList(T, STATIC_SIZE), item: T) -> %void {
|
pub fn append(l: &Self, item: T) -> %void {
|
||||||
const new_length = l.length + 1;
|
const new_length = l.length + 1;
|
||||||
%return l.ensure_capacity(new_length);
|
%return l.ensure_capacity(new_length);
|
||||||
l.items[l.length] = item;
|
l.items[l.length] = item;
|
||||||
l.length = new_length;
|
l.length = new_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_capacity(l: &SmallList(T, STATIC_SIZE), new_capacity: isize) -> %void {
|
pub fn ensure_capacity(l: &Self, new_capacity: isize) -> %void {
|
||||||
const old_capacity = l.items.len;
|
const old_capacity = l.items.len;
|
||||||
var better_capacity = old_capacity;
|
var better_capacity = old_capacity;
|
||||||
while (better_capacity < new_capacity) {
|
while (better_capacity < new_capacity) {
|
||||||
better_capacity *= 2;
|
better_capacity *= 2;
|
||||||
}
|
}
|
||||||
if (better_capacity != old_capacity) {
|
if (better_capacity != old_capacity) {
|
||||||
const alloc_bytes = better_capacity * @sizeof(T);
|
|
||||||
if (l.items.ptr == &l.prealloc_items[0]) {
|
if (l.items.ptr == &l.prealloc_items[0]) {
|
||||||
l.items = ([]T)(%return l.allocator.alloc(l.allocator, alloc_bytes));
|
l.items = %return l.allocator.alloc(T, better_capacity);
|
||||||
@memcpy(l.items.ptr, &l.prealloc_items[0], old_capacity * @sizeof(T));
|
mem.copy(T, l.items, l.prealloc_items[0...old_capacity]);
|
||||||
} else {
|
} else {
|
||||||
l.items = ([]T)(%return l.allocator.realloc(l.allocator, ([]u8)(l.items), alloc_bytes));
|
l.items = %return l.allocator.realloc(T, l.items, better_capacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var global_allocator = Allocator {
|
var global_allocator = Allocator {
|
||||||
.alloc = global_alloc,
|
.alloc_fn = global_alloc,
|
||||||
.realloc = global_realloc,
|
.realloc_fn = global_realloc,
|
||||||
.free = global_free,
|
.free_fn = global_free,
|
||||||
.context = null,
|
.context = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
18
std/math.zig
18
std/math.zig
@ -26,10 +26,24 @@ pub fn f64_is_inf(f: f64) -> bool {
|
|||||||
f == f64_get_neg_inf() || f == f64_get_pos_inf()
|
f == f64_get_neg_inf() || f == f64_get_pos_inf()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min(T: type)(x: T, y: T) -> T {
|
pub fn min(inline T: type, x: T, y: T) -> T {
|
||||||
if (x < y) x else y
|
if (x < y) x else y
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max(T: type)(x: T, y: T) -> T {
|
pub fn max(inline T: type, x: T, y: T) -> T {
|
||||||
if (x > y) x else y
|
if (x > y) x else y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub error Overflow;
|
||||||
|
pub fn mul_overflow(inline T: type, a: T, b: T) -> %T {
|
||||||
|
var answer: T = undefined;
|
||||||
|
if (@mul_with_overflow(T, a, b, &answer)) error.Overflow else answer
|
||||||
|
}
|
||||||
|
pub fn add_overflow(inline T: type, a: T, b: T) -> %T {
|
||||||
|
var answer: T = undefined;
|
||||||
|
if (@add_with_overflow(T, a, b, &answer)) error.Overflow else answer
|
||||||
|
}
|
||||||
|
pub fn sub_overflow(inline T: type, a: T, b: T) -> %T {
|
||||||
|
var answer: T = undefined;
|
||||||
|
if (@sub_with_overflow(T, a, b, &answer)) error.Overflow else answer
|
||||||
|
}
|
||||||
|
36
std/mem.zig
36
std/mem.zig
@ -1,18 +1,46 @@
|
|||||||
const assert = @import("debug.zig").assert;
|
const assert = @import("debug.zig").assert;
|
||||||
|
const math = @import("math.zig");
|
||||||
|
const os = @import("os.zig");
|
||||||
|
const io = @import("io.zig");
|
||||||
|
|
||||||
pub error NoMem;
|
pub error NoMem;
|
||||||
|
|
||||||
pub type Context = u8;
|
pub type Context = u8;
|
||||||
pub struct Allocator {
|
pub struct Allocator {
|
||||||
alloc: fn (self: &Allocator, n: isize) -> %[]u8,
|
alloc_fn: fn (self: &Allocator, n: isize) -> %[]u8,
|
||||||
realloc: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8,
|
realloc_fn: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8,
|
||||||
free: fn (self: &Allocator, mem: []u8),
|
free_fn: fn (self: &Allocator, mem: []u8),
|
||||||
context: ?&Context,
|
context: ?&Context,
|
||||||
|
|
||||||
|
/// Aborts the program if an allocation fails.
|
||||||
|
fn checked_alloc(self: &Allocator, inline T: type, n: isize) -> []T {
|
||||||
|
alloc(self, T, n) %% |err| {
|
||||||
|
// TODO var args printf
|
||||||
|
%%io.stderr.write("allocation failure: ");
|
||||||
|
%%io.stderr.write(@err_name(err));
|
||||||
|
%%io.stderr.printf("\n");
|
||||||
|
os.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc(self: &Allocator, inline T: type, n: isize) -> %[]T {
|
||||||
|
const byte_count = %return math.mul_overflow(isize, @sizeof(T), n);
|
||||||
|
([]T)(%return self.alloc_fn(self, byte_count))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn realloc(self: &Allocator, inline T: type, old_mem: []T, n: isize) -> %[]T {
|
||||||
|
const byte_count = %return math.mul_overflow(isize, @sizeof(T), n);
|
||||||
|
([]T)(%return self.realloc_fn(self, ([]u8)(old_mem), byte_count))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free(self: &Allocator, inline T: type, mem: []T) {
|
||||||
|
self.free_fn(self, ([]u8)(mem));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy all of source into dest at position 0.
|
/// Copy all of source into dest at position 0.
|
||||||
/// dest.len must be >= source.len.
|
/// dest.len must be >= source.len.
|
||||||
pub fn copy(T)(dest: []T, source: []T) {
|
pub fn copy(inline T: type, dest: []T, source: []T) {
|
||||||
assert(dest.len >= source.len);
|
assert(dest.len >= source.len);
|
||||||
@memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len);
|
@memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len);
|
||||||
}
|
}
|
||||||
|
13
std/net.zig
13
std/net.zig
@ -99,14 +99,14 @@ pub fn connect_addr(addr: &Address, port: u16) -> %Connection {
|
|||||||
const connect_ret = if (addr.family == linux.AF_INET) {
|
const connect_ret = if (addr.family == linux.AF_INET) {
|
||||||
var os_addr: linux.sockaddr_in = undefined;
|
var os_addr: linux.sockaddr_in = undefined;
|
||||||
os_addr.family = addr.family;
|
os_addr.family = addr.family;
|
||||||
os_addr.port = host_to_be(u16)(port);
|
os_addr.port = swap_if_little_endian(u16, port);
|
||||||
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
||||||
@memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero)));
|
@memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero)));
|
||||||
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in))
|
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in))
|
||||||
} else if (addr.family == linux.AF_INET6) {
|
} else if (addr.family == linux.AF_INET6) {
|
||||||
var os_addr: linux.sockaddr_in6 = undefined;
|
var os_addr: linux.sockaddr_in6 = undefined;
|
||||||
os_addr.family = addr.family;
|
os_addr.family = addr.family;
|
||||||
os_addr.port = host_to_be(u16)(port);
|
os_addr.port = swap_if_little_endian(u16, port);
|
||||||
os_addr.flowinfo = 0;
|
os_addr.flowinfo = 0;
|
||||||
os_addr.scope_id = addr.scope_id;
|
os_addr.scope_id = addr.scope_id;
|
||||||
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
||||||
@ -319,7 +319,7 @@ fn parse_ip4(buf: []const u8) -> %u32 {
|
|||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn test_parse_ip4() {
|
fn test_parse_ip4() {
|
||||||
assert(%%parse_ip4("127.0.0.1") == be_to_host(u32)(0x7f000001));
|
assert(%%parse_ip4("127.0.0.1") == swap_if_little_endian(u32, 0x7f000001));
|
||||||
switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, }
|
switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, }
|
||||||
switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, }
|
switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, }
|
||||||
switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, }
|
switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, }
|
||||||
@ -352,12 +352,11 @@ fn test_lookup_simple_ip() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const be_to_host = host_to_be;
|
fn swap_if_little_endian(inline T: type, x: T) -> T {
|
||||||
fn host_to_be(T: type)(x: T) -> T {
|
if (@compile_var("is_big_endian")) x else endian_swap(T, x)
|
||||||
if (@compile_var("is_big_endian")) x else endian_swap(T)(x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endian_swap(T: type)(x: T) -> T {
|
fn endian_swap(inline T: type, x: T) -> T {
|
||||||
const x_slice = ([]u8)((&const x)[0...1]);
|
const x_slice = ([]u8)((&const x)[0...1]);
|
||||||
var result: T = undefined;
|
var result: T = undefined;
|
||||||
const result_slice = ([]u8)((&result)[0...1]);
|
const result_slice = ([]u8)((&result)[0...1]);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
const assert = @import("debug.zig").assert;
|
const assert = @import("debug.zig").assert;
|
||||||
|
|
||||||
pub const eql = slice_eql(u8);
|
pub fn eql(a: []const u8, b: []const u8) -> bool {
|
||||||
|
slice_eql(u8, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slice_eql(T: type)(a: []const T, b: []const T) -> bool {
|
pub fn slice_eql(inline T: type, a: []const T, b: []const T) -> bool {
|
||||||
if (a.len != b.len) return false;
|
if (a.len != b.len) return false;
|
||||||
for (a) |item, index| {
|
for (a) |item, index| {
|
||||||
if (b[index] != item) return false;
|
if (b[index] != item) return false;
|
||||||
|
@ -9,6 +9,7 @@ extern var zig_test_fn_list: []TestFn;
|
|||||||
|
|
||||||
pub fn run_tests() -> %void {
|
pub fn run_tests() -> %void {
|
||||||
for (zig_test_fn_list) |test_fn, i| {
|
for (zig_test_fn_list) |test_fn, i| {
|
||||||
|
// TODO: print var args
|
||||||
%%io.stderr.write("Test ");
|
%%io.stderr.write("Test ");
|
||||||
%%io.stderr.print_i64(i + 1);
|
%%io.stderr.print_i64(i + 1);
|
||||||
%%io.stderr.write("/");
|
%%io.stderr.write("/");
|
||||||
|
@ -1181,11 +1181,11 @@ const invalid = foo > foo;
|
|||||||
)SOURCE", 1, ".tmp_source.zig:3:21: error: operator not allowed for type 'fn()'");
|
)SOURCE", 1, ".tmp_source.zig:3:21: error: operator not allowed for type 'fn()'");
|
||||||
|
|
||||||
add_compile_fail_case("generic function instance with non-constant expression", R"SOURCE(
|
add_compile_fail_case("generic function instance with non-constant expression", R"SOURCE(
|
||||||
fn foo(x: i32)(y: i32) -> i32 { return x + y; }
|
fn foo(inline x: i32, y: i32) -> i32 { return x + y; }
|
||||||
fn test1(a: i32, b: i32) -> i32 {
|
fn test1(a: i32, b: i32) -> i32 {
|
||||||
return foo(a)(b);
|
return foo(a, b);
|
||||||
}
|
}
|
||||||
)SOURCE", 1, ".tmp_source.zig:4:16: error: unable to evaluate constant expression");
|
)SOURCE", 1, ".tmp_source.zig:4:16: error: unable to evaluate constant expression for inline parameter");
|
||||||
|
|
||||||
add_compile_fail_case("goto jumping into block", R"SOURCE(
|
add_compile_fail_case("goto jumping into block", R"SOURCE(
|
||||||
fn f() {
|
fn f() {
|
||||||
@ -1406,6 +1406,27 @@ fn f() {
|
|||||||
}
|
}
|
||||||
)SOURCE", 1, ".tmp_source.zig:3:13: error: unable to evaluate constant expression");
|
)SOURCE", 1, ".tmp_source.zig:3:13: error: unable to evaluate constant expression");
|
||||||
|
|
||||||
|
add_compile_fail_case("export function with inline parameter", R"SOURCE(
|
||||||
|
export fn foo(inline x: i32, y: i32) -> i32{
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
)SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function");
|
||||||
|
|
||||||
|
add_compile_fail_case("extern function with inline parameter", R"SOURCE(
|
||||||
|
extern fn foo(inline x: i32, y: i32) -> i32;
|
||||||
|
fn f() -> i32 {
|
||||||
|
foo(1, 2)
|
||||||
|
}
|
||||||
|
)SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function");
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
add_compile_fail_case("inline export function", R"SOURCE(
|
||||||
|
export inline fn foo(x: i32, y: i32) -> i32{
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
)SOURCE", 1, ".tmp_source.zig:2:1: error: extern functions cannot be inline");
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -712,17 +712,17 @@ three)";
|
|||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn simple_generic_fn() {
|
fn simple_generic_fn() {
|
||||||
assert(max(i32)(3, -1) == 3);
|
assert(max(i32, 3, -1) == 3);
|
||||||
assert(max(f32)(0.123, 0.456) == 0.456);
|
assert(max(f32, 0.123, 0.456) == 0.456);
|
||||||
assert(add(2)(3) == 5);
|
assert(add(2, 3) == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max(T: type)(a: T, b: T) -> T {
|
fn max(inline T: type, a: T, b: T) -> T {
|
||||||
return if (a > b) a else b;
|
return if (a > b) a else b;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(a: i32)(b: i32) -> i32 {
|
fn add(inline a: i32, b: i32) -> i32 {
|
||||||
return a + b;
|
return @const_eval(a) + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -734,23 +734,18 @@ fn constant_equal_function_pointers() {
|
|||||||
|
|
||||||
fn empty_fn() {}
|
fn empty_fn() {}
|
||||||
|
|
||||||
#attribute("test")
|
|
||||||
fn generic_function_equality() {
|
|
||||||
assert(max(i32) == max(i32));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn generic_malloc_free() {
|
fn generic_malloc_free() {
|
||||||
const a = %%mem_alloc(u8)(10);
|
const a = %%mem_alloc(u8, 10);
|
||||||
mem_free(u8)(a);
|
mem_free(u8, a);
|
||||||
}
|
}
|
||||||
const some_mem : [100]u8 = undefined;
|
const some_mem : [100]u8 = undefined;
|
||||||
#static_eval_enable(false)
|
#static_eval_enable(false)
|
||||||
fn mem_alloc(T: type)(n: isize) -> %[]T {
|
fn mem_alloc(inline T: type, n: isize) -> %[]T {
|
||||||
return (&T)(&some_mem[0])[0...n];
|
return (&T)(&some_mem[0])[0...n];
|
||||||
}
|
}
|
||||||
fn mem_free(T: type)(mem: []T) { }
|
fn mem_free(inline T: type, mem: []T) { }
|
||||||
|
|
||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
@ -982,11 +977,11 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
|
|||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn generic_fn_with_implicit_cast() {
|
fn generic_fn_with_implicit_cast() {
|
||||||
assert(get_first_byte(u8)([]u8 {13}) == 13);
|
assert(get_first_byte(u8, []u8 {13}) == 13);
|
||||||
assert(get_first_byte(u16)([]u16 {0, 13}) == 0);
|
assert(get_first_byte(u16, []u16 {0, 13}) == 0);
|
||||||
}
|
}
|
||||||
fn get_byte(ptr: ?&u8) -> u8 {*??ptr}
|
fn get_byte(ptr: ?&u8) -> u8 {*??ptr}
|
||||||
fn get_first_byte(T: type)(mem: []T) -> u8 {
|
fn get_first_byte(inline T: type, mem: []T) -> u8 {
|
||||||
get_byte((&u8)(&mem[0]))
|
get_byte((&u8)(&mem[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1651,9 +1646,9 @@ struct GenericDataThing(count: isize) {
|
|||||||
|
|
||||||
#attribute("test")
|
#attribute("test")
|
||||||
fn use_generic_param_in_generic_param() {
|
fn use_generic_param_in_generic_param() {
|
||||||
assert(a_generic_fn(i32, 3)(4) == 7);
|
assert(a_generic_fn(i32, 3, 4) == 7);
|
||||||
}
|
}
|
||||||
fn a_generic_fn(T: type, a: T)(b: T) -> T {
|
fn a_generic_fn(inline T: type, inline a: T, b: T) -> T {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user