aboutsummaryrefslogtreecommitdiff
path: root/src/analyze.cpp
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2016-09-08 00:24:48 -0400
committerAndrew Kelley <superjoe30@gmail.com>2016-09-08 00:24:48 -0400
commitd324b1befa6c7f0fd5d18ffe45a98d104a62d8a0 (patch)
tree0258132b13007f3b93cabc2ca54f4d17689e0c2d /src/analyze.cpp
parentde7e88c38fcaa37c98c2425be341568e8e193ffc (diff)
downloadzig-d324b1befa6c7f0fd5d18ffe45a98d104a62d8a0.tar.gz
zig-d324b1befa6c7f0fd5d18ffe45a98d104a62d8a0.zip
ability to infer parameter types
Diffstat (limited to 'src/analyze.cpp')
-rw-r--r--src/analyze.cpp222
1 files changed, 153 insertions, 69 deletions
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 6a24c42bc7..ad0dd7431e 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -109,6 +109,7 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
case NodeTypeContainerInitExpr:
+ case NodeTypeVarLiteral:
return node;
}
zig_unreachable();
@@ -219,6 +220,7 @@ static int bits_needed_for_unsigned(uint64_t x) {
static bool type_is_complete(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
+ case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdStruct:
return type_entry->data.structure.complete;
@@ -919,39 +921,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
fn_type_id.is_var_args = fn_proto->is_var_args;
fn_type_id.return_type = analyze_type_expr(g, import, context, fn_proto->return_type);
- switch (fn_type_id.return_type->id) {
- case TypeTableEntryIdInvalid:
- fn_proto->skip = true;
- break;
- case TypeTableEntryIdNumLitFloat:
- case TypeTableEntryIdNumLitInt:
- case TypeTableEntryIdUndefLit:
- case TypeTableEntryIdNullLit:
- 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:
- 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) {
AstNode *child = fn_proto->params.at(i);
assert(child->type == NodeTypeParamDecl);
@@ -996,6 +965,10 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
break;
+ case TypeTableEntryIdVar:
+ // var types are treated as generic functions; if we get to this code we should
+ // already be an instantiated function.
+ zig_unreachable();
}
if (type_entry->id == TypeTableEntryIdInvalid) {
fn_proto->skip = true;
@@ -1005,6 +978,42 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
param_info->is_noalias = child->data.param_decl.is_noalias;
}
+ switch (fn_type_id.return_type->id) {
+ case TypeTableEntryIdInvalid:
+ fn_proto->skip = true;
+ break;
+ case TypeTableEntryIdNumLitFloat:
+ case TypeTableEntryIdNumLitInt:
+ case TypeTableEntryIdUndefLit:
+ case TypeTableEntryIdNullLit:
+ 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:
+ 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;
+ case TypeTableEntryIdVar:
+ zig_panic("TODO var return type");
+ }
+
+
if (fn_proto->skip) {
return g->builtin_types.entry_invalid;
}
@@ -1618,6 +1627,11 @@ static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNo
node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node);
}
+static bool get_is_generic_fn(AstNode *proto_node) {
+ assert(proto_node->type == NodeTypeFnProto);
+ return proto_node->data.fn_proto.inline_or_var_type_arg_count > 0;
+}
+
static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node,
BlockContext *containing_context)
{
@@ -1628,7 +1642,7 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
}
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;
+ bool is_generic_fn = get_is_generic_fn(proto_node);
assert(!is_generic_instance || !is_generic_fn);
AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl;
@@ -1875,6 +1889,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only)
case NodeTypeArrayType:
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
+ case NodeTypeVarLiteral:
zig_unreachable();
}
@@ -1936,6 +1951,9 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
case TypeTableEntryIdTypeDecl:
return type_has_codegen_value(type_entry->data.type_decl.canonical_type);
+
+ case TypeTableEntryIdVar:
+ zig_unreachable();
}
zig_unreachable();
}
@@ -3374,6 +3392,9 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
add_node_error(g, node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
return g->builtin_types.entry_invalid;
+
+ case TypeTableEntryIdVar:
+ zig_unreachable();
}
ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
@@ -3751,20 +3772,22 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
}
// Set name to nullptr to make the variable anonymous (not visible to programmer).
-static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
- BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node)
+static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
+ BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node,
+ bool shadowable)
{
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
variable_entry->type = type_entry;
variable_entry->block_context = context;
variable_entry->import = import;
+ variable_entry->shadowable = shadowable;
if (name) {
buf_init_from_buf(&variable_entry->name, name);
if (type_entry->id != TypeTableEntryIdInvalid) {
VariableTableEntry *existing_var = find_variable(g, context, name);
- if (existing_var) {
+ if (existing_var && !existing_var->shadowable) {
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
@@ -3805,6 +3828,12 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Impor
return variable_entry;
}
+static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
+ BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node)
+{
+ return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false);
+}
+
static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import,
BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node)
{
@@ -5227,6 +5256,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdGenericFn:
+ case TypeTableEntryIdVar:
add_node_error(g, expr_node,
buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
@@ -5542,23 +5572,22 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE
return g->builtin_types.entry_invalid;
}
- int inline_arg_count = decl_node->data.fn_proto.inline_arg_count;
- assert(inline_arg_count > 0);
+ int inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count;
+ assert(inline_or_var_type_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);
+ generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count;
+ generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count);
+ int next_impl_i = 0;
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,
@@ -5567,9 +5596,17 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE
return expected_param_type;
}
+ bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar);
+ bool is_inline = generic_param_decl_node->data.param_decl.is_inline;
+ if (!is_inline && !is_var_type) {
+ next_impl_i += 1;
+ continue;
+ }
+
+
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);
+ is_var_type ? nullptr : expected_param_type, *param_node);
if (param_type->id == TypeTableEntryIdInvalid) {
return param_type;
}
@@ -5578,27 +5615,32 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE
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) {
- VariableTableEntry *var = 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);
- // This generic function instance could be called with anything, so when this variable is read it
- // needs to know that it depends on compile time variable data.
- var->force_depends_on_compile_var = true;
- } else {
+ if (is_inline && !const_val->ok) {
add_node_error(g, *param_node,
buf_sprintf("unable to evaluate constant expression for inline parameter"));
return g->builtin_types.entry_invalid;
}
+ VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context,
+ generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true);
+ // This generic function instance could be called with anything, so when this variable is read it
+ // needs to know that it depends on compile time variable data.
+ var->force_depends_on_compile_var = true;
+
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;
+ generic_param_value->node = is_inline ? *param_node : nullptr;
+ generic_param_value->impl_index = next_impl_i;
next_generic_param_index += 1;
+
+ if (!is_inline) {
+ next_impl_i += 1;
+ }
}
- assert(next_generic_param_index == inline_arg_count);
+ assert(next_generic_param_index == inline_or_var_type_arg_count);
auto entry = g->generic_table.maybe_get(generic_fn_type_id);
FnTableEntry *impl_fn;
@@ -5612,8 +5654,23 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE
&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.inline_or_var_type_arg_count = 0;
impl_decl_node->data.fn_proto.generic_proto_node = decl_node;
+ // replace var arg types with actual types
+ for (int generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) {
+ GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i];
+ if (!generic_param_value->node) {
+ int impl_i = generic_param_value->impl_index;
+ AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i);
+ assert(impl_param_decl_node->type == NodeTypeParamDecl);
+
+ impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import,
+ generic_param_value->type, impl_param_decl_node);
+ normalize_parent_ptrs(impl_param_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;
@@ -5660,6 +5717,9 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
if (expected_param_type->id == TypeTableEntryIdInvalid) {
return expected_param_type;
}
+
+
+
AstNode **param_node = &node->data.fn_call_expr.params.at(i);
TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type,
@@ -6605,6 +6665,9 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn
case NodeTypeSwitchExpr:
return_type = analyze_switch_expr(g, import, context, expected_type, node);
break;
+ case NodeTypeVarLiteral:
+ return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_var, false);
+ break;
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeDirective:
@@ -6750,18 +6813,25 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex
}
}
-static int fn_proto_inline_arg_count(AstNode *proto_node) {
+static void count_inline_and_var_args(AstNode *proto_node) {
assert(proto_node->type == NodeTypeFnProto);
- int result = 0;
+
+ int *inline_arg_count = &proto_node->data.fn_proto.inline_arg_count;
+ int *inline_or_var_type_arg_count = &proto_node->data.fn_proto.inline_or_var_type_arg_count;
+
+ *inline_arg_count = 0;
+ *inline_or_var_type_arg_count = 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;
+ bool is_inline = param_node->data.param_decl.is_inline;
+ *inline_arg_count += is_inline ? 1 : 0;
+ *inline_or_var_type_arg_count += (is_inline ||
+ param_node->data.param_decl.type->type == NodeTypeVarLiteral) ? 1 : 0;
}
- return result;
}
-
static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
switch (node->type) {
case NodeTypeRoot:
@@ -6804,7 +6874,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte
add_node_error(g, node, buf_sprintf("missing function name"));
break;
}
- node->data.fn_proto.inline_arg_count = fn_proto_inline_arg_count(node);
+ count_inline_and_var_args(node);
add_top_level_decl(g, import, context, node, fn_name);
break;
@@ -6861,6 +6931,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte
case NodeTypeArrayType:
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
+ case NodeTypeVarLiteral:
zig_unreachable();
}
}
@@ -7118,6 +7189,8 @@ Expr *get_resolved_expr(AstNode *node) {
return &node->data.switch_expr.resolved_expr;
case NodeTypeFnProto:
return &node->data.fn_proto.resolved_expr;
+ case NodeTypeVarLiteral:
+ return &node->data.var_literal.resolved_expr;
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeRoot:
@@ -7192,6 +7265,7 @@ static TopLevelDecl *get_as_top_level_decl(AstNode *node) {
case NodeTypeArrayType:
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
+ case NodeTypeVarLiteral:
zig_unreachable();
}
zig_unreachable();
@@ -7250,6 +7324,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdGenericFn:
+ case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVoid:
@@ -7392,6 +7467,7 @@ static uint32_t hash_const_val(TypeTableEntry *type, ConstExprValue *const_val)
case TypeTableEntryIdGenericFn:
case TypeTableEntryIdInvalid:
case TypeTableEntryIdUnreachable:
+ case TypeTableEntryIdVar:
zig_unreachable();
}
zig_unreachable();
@@ -7402,9 +7478,12 @@ uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
result += hash_ptr(id->decl_node);
for (int i = 0; i < id->generic_param_count; i += 1) {
GenericParamValue *generic_param = &id->generic_params[i];
- ConstExprValue *const_val = &get_resolved_expr(generic_param->node)->const_val;
- assert(const_val->ok);
- result += hash_const_val(generic_param->type, const_val);
+ if (generic_param->node) {
+ ConstExprValue *const_val = &get_resolved_expr(generic_param->node)->const_val;
+ assert(const_val->ok);
+ result += hash_const_val(generic_param->type, const_val);
+ }
+ result += hash_ptr(generic_param->type);
}
return result;
}
@@ -7415,13 +7494,17 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
for (int i = 0; i < a->generic_param_count; i += 1) {
GenericParamValue *a_val = &a->generic_params[i];
GenericParamValue *b_val = &b->generic_params[i];
- assert(a_val->type == b_val->type);
- ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->const_val;
- ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->const_val;
- assert(a_const_val->ok);
- assert(b_const_val->ok);
- if (!const_values_equal(a_const_val, b_const_val, a_val->type)) {
- return false;
+ if (a_val->type != b_val->type) return false;
+ if (a_val->node && b_val->node) {
+ ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->const_val;
+ ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->const_val;
+ assert(a_const_val->ok);
+ assert(b_const_val->ok);
+ if (!const_values_equal(a_const_val, b_const_val, a_val->type)) {
+ return false;
+ }
+ } else {
+ assert(!a_val->node && !b_val->node);
}
}
return true;
@@ -7457,6 +7540,7 @@ static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry)
case TypeTableEntryIdVoid:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdGenericFn:
+ case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdArray:
return type_of_first_thing_in_memory(type_entry->data.array.child_type);