Browse Source

Default parameters for procedures

Ginger Bill 8 years ago
parent
commit
366b306df0
4 changed files with 175 additions and 51 deletions
  1. 91 27
      src/check_expr.cpp
  2. 5 4
      src/entity.cpp
  3. 31 8
      src/ir.cpp
  4. 48 12
      src/parser.cpp

+ 91 - 27
src/check_expr.cpp

@@ -255,13 +255,18 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 }
 
 
+i64 assign_score_function(i64 distance) {
+	// TODO(bill): A decent score function
+	return gb_max(1000000 - distance*distance, 0);
+}
+
+
 bool check_is_assignable_to_with_score(Checker *c, Operand *operand, Type *type, i64 *score_) {
 	i64 score = 0;
 	i64 distance = check_distance_between_types(c, operand, type);
 	bool ok = distance >= 0;
 	if (ok) {
-		// TODO(bill): A decent score function
-		score = gb_max(1000000 - distance*distance, 0);
+		score = assign_score_function(distance);
 	}
 	if (score_) *score_ = score;
 	return ok;
@@ -1051,7 +1056,22 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 		}
 		ast_node(p, Field, params[i]);
 		AstNode *type_expr = p->type;
-		if (type_expr) {
+		Type *type = NULL;
+		AstNode *default_value = p->default_value;
+		ExactValue value = {};
+
+		if (type_expr == NULL) {
+			Operand o = {};
+			check_expr(c, &o, default_value);
+
+			if (o.mode != Addressing_Constant) {
+				error_node(default_value, "Default parameter must be a constant");
+			} else {
+				value = o.value;
+			}
+
+			type = default_type(o.type);
+		} else {
 			if (type_expr->kind == AstNode_Ellipsis) {
 				type_expr = type_expr->Ellipsis.expr;
 				if (i+1 == params.count) {
@@ -1061,28 +1081,49 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 				}
 			}
 
-			Type *type = check_type(c, type_expr);
-			if (p->flags&FieldFlag_no_alias) {
-				if (!is_type_pointer(type)) {
-					error_node(params[i], "`no_alias` can only be applied to fields of pointer type");
-					p->flags &= ~FieldFlag_no_alias; // Remove the flag
+			type = check_type(c, type_expr);
+
+			if (default_value != NULL) {
+				Operand o = {};
+				check_expr(c, &o, default_value);
+
+				if (o.mode != Addressing_Constant) {
+					error_node(default_value, "Default parameter must be a constant");
+				} else {
+					value = o.value;
 				}
+
+				check_is_assignable_to(c, &o, type);
 			}
 
-			for_array(j, p->names) {
-				AstNode *name = p->names[j];
-				if (ast_node_expect(name, AstNode_Ident)) {
-					Entity *param = make_entity_param(c->allocator, scope, name->Ident, type,
-					                                  (p->flags&FieldFlag_using) != 0, (p->flags&FieldFlag_immutable) != 0);
-					if (p->flags&FieldFlag_no_alias) {
-						param->flags |= EntityFlag_NoAlias;
-					}
-					if (p->flags&FieldFlag_immutable) {
-						param->Variable.is_immutable = true;
-					}
-					add_entity(c, scope, name, param);
-					variables[variable_index++] = param;
+		}
+		if (type == NULL) {
+			error_node(params[i], "Invalid parameter type");
+			type = t_invalid;
+		}
+
+		if (p->flags&FieldFlag_no_alias) {
+			if (!is_type_pointer(type)) {
+				error_node(params[i], "`no_alias` can only be applied to fields of pointer type");
+				p->flags &= ~FieldFlag_no_alias; // Remove the flag
+			}
+		}
+
+		for_array(j, p->names) {
+			AstNode *name = p->names[j];
+			if (ast_node_expect(name, AstNode_Ident)) {
+				Entity *param = make_entity_param(c->allocator, scope, name->Ident, type,
+				                                  (p->flags&FieldFlag_using) != 0, (p->flags&FieldFlag_immutable) != 0);
+				if (p->flags&FieldFlag_no_alias) {
+					param->flags |= EntityFlag_NoAlias;
+				}
+				if (p->flags&FieldFlag_immutable) {
+					param->Variable.is_immutable = true;
 				}
+				param->Variable.default_value = value;
+
+				add_entity(c, scope, name, param);
+				variables[variable_index++] = param;
 			}
 		}
 	}
@@ -4775,18 +4816,35 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
 CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	ast_node(ce, CallExpr, call);
 	isize param_count = 0;
+	isize param_count_excluding_defaults = 0;
 	bool variadic = proc_type->Proc.variadic;
 	bool vari_expand = (ce->ellipsis.pos.line != 0);
 	i64 score = 0;
 	bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
 
+	TypeTuple *param_tuple = NULL;
+
 	if (proc_type->Proc.params != NULL) {
-		param_count = proc_type->Proc.params->Tuple.variable_count;
+		param_tuple = &proc_type->Proc.params->Tuple;
+
+		param_count = param_tuple->variable_count;
 		if (variadic) {
 			param_count--;
 		}
 	}
 
+	param_count_excluding_defaults = param_count;
+	if (param_tuple != NULL) {
+		for (isize i = param_count-1; i >= 0; i--) {
+			Entity *e = param_tuple->variables[i];
+			GB_ASSERT(e->kind == Entity_Variable);
+			if (e->Variable.default_value.kind == ExactValue_Invalid) {
+				break;
+			}
+			param_count_excluding_defaults--;
+		}
+	}
+
 	if (vari_expand && !variadic) {
 		if (show_error) {
 			error(ce->ellipsis,
@@ -4797,13 +4855,13 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		return CallArgumentError_NonVariadicExpand;
 	}
 
-	if (operands.count == 0 && param_count == 0) {
+	if (operands.count == 0 && param_count_excluding_defaults == 0) {
 		if (score_) *score_ = score;
 		return CallArgumentError_None;
 	}
 
 	i32 error_code = 0;
-	if (operands.count < param_count) {
+	if (operands.count < param_count_excluding_defaults) {
 		error_code = -1;
 	} else if (!variadic && operands.count > param_count) {
 		error_code = +1;
@@ -4818,7 +4876,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 
 		if (show_error) {
 			gbString proc_str = expr_to_string(ce->proc);
-			error_node(call, err_fmt, proc_str, param_count);
+			error_node(call, err_fmt, proc_str, param_count_excluding_defaults);
 			gb_string_free(proc_str);
 		}
 		if (score_) *score_ = score;
@@ -4828,9 +4886,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	CallArgumentError err = CallArgumentError_None;
 
 	GB_ASSERT(proc_type->Proc.params != NULL);
-	Entity **sig_params = proc_type->Proc.params->Tuple.variables;
+	Entity **sig_params = param_tuple->variables;
 	isize operand_index = 0;
-	for (; operand_index < param_count; operand_index++) {
+	for (; operand_index < param_count_excluding_defaults; operand_index++) {
 		Type *t = sig_params[operand_index]->type;
 		Operand o = operands[operand_index];
 		if (variadic) {
@@ -4972,6 +5030,12 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 			if (e->token.string == "_") {
 				continue;
 			}
+			GB_ASSERT(e->kind == Entity_Variable);
+			if (e->Variable.default_value.kind != ExactValue_Invalid) {
+				score += assign_score_function(1);
+				continue;
+			}
+
 			if (show_error) {
 				gbString str = type_to_string(e->type);
 				error_node(call, "Parameter `%.*s` of type `%s` is missing in procedure call",

+ 5 - 4
src/entity.cpp

@@ -81,10 +81,11 @@ struct Entity {
 			ExactValue value;
 		} Constant;
 		struct {
-			i32  field_index;
-			i32  field_src_index;
-			bool is_immutable;
-			bool is_thread_local;
+			i32        field_index;
+			i32        field_src_index;
+			bool       is_immutable;
+			bool       is_thread_local;
+			ExactValue default_value;
 		} Variable;
 		struct {
 			bool is_type_alias;

+ 31 - 8
src/ir.cpp

@@ -4616,16 +4616,20 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			}
 			TypeTuple *pt = &type->params->Tuple;
 			for (isize i = 0; i < param_count; i++) {
-				Type *param_type = pt->variables[i]->type;
+				Entity *e = pt->variables[i];
+				GB_ASSERT(e->kind == Entity_Variable);
 				if (args[i] == NULL) {
-					args[i] = ir_value_nil(proc->module->allocator, param_type);
+					if (e->Variable.default_value.kind != ExactValue_Invalid) {
+						args[i] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
+					} else {
+						args[i] = ir_value_nil(proc->module->allocator, e->type);
+					}
 				} else {
-					args[i] = ir_emit_conv(proc, args[i], param_type);
+					args[i] = ir_emit_conv(proc, args[i], e->type);
 				}
 			}
 
 			return ir_emit_call(proc, value, args, param_count);
-			// GB_PANIC("HERE!\n");
 		}
 
 		isize arg_index = 0;
@@ -4640,7 +4644,9 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 				arg_count++;
 			}
 		}
-		irValue **args = gb_alloc_array(proc->module->allocator, irValue *, arg_count);
+
+
+		irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count));
 		bool variadic = type->variadic;
 		bool vari_expand = ce->ellipsis.pos.line != 0;
 
@@ -4660,6 +4666,23 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 
 		TypeTuple *pt = &type->params->Tuple;
 
+		if (arg_count < type->param_count) {
+			isize end = type->param_count;
+			if (variadic) {
+				end--;
+			}
+			while (arg_index < end) {
+				Entity *e = pt->variables[arg_index];
+				GB_ASSERT(e->kind == Entity_Variable);
+				if (e->Variable.default_value.kind != ExactValue_Invalid) {
+					args[arg_index++] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
+				} else {
+					args[arg_index++] = ir_value_nil(proc->module->allocator, e->type);
+				}
+			}
+		}
+
+
 		if (variadic) {
 			isize i = 0;
 			for (; i < type->param_count-1; i++) {
@@ -4674,7 +4697,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 				}
 			}
 		} else {
-			for (isize i = 0; i < arg_count; i++) {
+			for (isize i = 0; i < type->param_count; i++) {
 				args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
 			}
 		}
@@ -4704,7 +4727,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			args[arg_count-1] = ir_emit_load(proc, slice);
 		}
 
-		return ir_emit_call(proc, value, args, arg_count);
+		return ir_emit_call(proc, value, args, type->param_count);
 	case_end;
 
 	case_ast_node(se, SliceExpr, expr);
@@ -6680,7 +6703,7 @@ void ir_number_proc_registers(irProcedure *proc) {
 		b->index = i;
 		for_array(j, b->instrs) {
 			irValue *value = b->instrs[j];
-			GB_ASSERT(value->kind == irValue_Instr);
+			GB_ASSERT_MSG(value->kind == irValue_Instr, "%.*s", LIT(proc->name));
 			irInstr *instr = &value->Instr;
 			if (ir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions
 				value->index = -1;

+ 48 - 12
src/parser.cpp

@@ -326,9 +326,10 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 	}) \
 AST_NODE_KIND(_DeclEnd,   "", i32) \
 	AST_NODE_KIND(Field, "field", struct { \
-		Array<AstNode *> names;    \
-		AstNode *    type;     \
-		u32          flags;    \
+		Array<AstNode *> names;            \
+		AstNode *    type;                 \
+		AstNode *    default_value;        \
+		u32          flags;                \
 	}) \
 	AST_NODE_KIND(FieldList, "field list", struct { \
 		Token token; \
@@ -1276,10 +1277,11 @@ AstNode *ast_bad_decl(AstFile *f, Token begin, Token end) {
 	return result;
 }
 
-AstNode *ast_field(AstFile *f, Array<AstNode *> names, AstNode *type, u32 flags) {
+AstNode *ast_field(AstFile *f, Array<AstNode *> names, AstNode *type, AstNode *default_value, u32 flags) {
 	AstNode *result = make_ast_node(f, AstNode_Field);
 	result->Field.names = names;
 	result->Field.type = type;
+	result->Field.default_value = default_value;
 	result->Field.flags = flags;
 	return result;
 }
@@ -2645,7 +2647,7 @@ AstNode *parse_results(AstFile *f) {
 		Array<AstNode *> empty_names = {};
 		Array<AstNode *> list = make_ast_node_array(f);
 		AstNode *type = parse_type(f);
-		array_add(&list, ast_field(f, empty_names, type, 0));
+		array_add(&list, ast_field(f, empty_names, type, NULL, 0));
 		return ast_field_list(f, begin_token, list);
 	}
 
@@ -2839,6 +2841,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 	Array<AstNodeAndFlags> list = {}; array_init(&list, heap_allocator()); // LEAK(bill):
 	isize total_name_count = 0;
 	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
+	bool is_procedure = (allowed_flags&FieldFlag_Signature) != 0;
 
 	while (f->curr_token.kind != follow &&
 	       f->curr_token.kind != Token_Colon &&
@@ -2866,8 +2869,25 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 		total_name_count += names.count;
 
 		expect_token_after(f, Token_Colon, "field list");
-		AstNode *type = parse_var_type(f, allow_ellipsis);
-		AstNode *param = ast_field(f, names, type, set_flags);
+		AstNode *type = NULL;
+		AstNode *default_value = NULL;
+
+		if (f->curr_token.kind != Token_Eq) {
+			type = parse_var_type(f, allow_ellipsis);
+		}
+		if (allow_token(f, Token_Eq)) {
+			// TODO(bill): Should this be true==lhs or false==rhs?
+			default_value = parse_expr(f, true);
+			if (!is_procedure) {
+				syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
+			}
+		}
+
+		if (default_value != NULL && names.count > 1) {
+			syntax_error(f->curr_token, "Default parameters can only be applied to single values");
+		}
+
+		AstNode *param = ast_field(f, names, type, default_value, set_flags);
 		array_add(&params, param);
 
 		parse_expect_field_separator(f, type);
@@ -2884,8 +2904,24 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 			total_name_count += names.count;
 
 			expect_token_after(f, Token_Colon, "field list");
-			AstNode *type = parse_var_type(f, allow_ellipsis);
-			AstNode *param = ast_field(f, names, type, set_flags);
+			AstNode *type = NULL;
+			AstNode *default_value = NULL;
+			if (f->curr_token.kind != Token_Eq) {
+				type = parse_var_type(f, allow_ellipsis);
+			}
+			if (allow_token(f, Token_Eq)) {
+				// TODO(bill): Should this be true==lhs or false==rhs?
+				default_value = parse_expr(f, true);
+				if (!is_procedure) {
+					syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
+				}
+			}
+
+			if (default_value != NULL && names.count > 1) {
+				syntax_error(f->curr_token, "Default parameters can only be applied to single values");
+			}
+
+			AstNode *param = ast_field(f, names, type, default_value, set_flags);
 			array_add(&params, param);
 
 			if (!parse_expect_field_separator(f, param)) {
@@ -2907,7 +2943,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 		names[0] = ast_ident(f, token);
 		u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags);
 
-		AstNode *param = ast_field(f, names, list[i].node, flags);
+		AstNode *param = ast_field(f, names, list[i].node, NULL, flags);
 		array_add(&params, param);
 	}
 
@@ -3084,7 +3120,7 @@ AstNode *parse_type_or_ident(AstFile *f) {
 				total_decl_name_count += names.count;
 				expect_token_after(f, Token_Colon, "field list");
 				AstNode *type = parse_var_type(f, false);
-				array_add(&decls, ast_field(f, names, type, set_flags));
+				array_add(&decls, ast_field(f, names, type, NULL, set_flags));
 			} else {
 				Array<AstNode *> names = parse_ident_list(f);
 				if (names.count == 0) {
@@ -3095,7 +3131,7 @@ AstNode *parse_type_or_ident(AstFile *f) {
 					total_decl_name_count += names.count;
 					expect_token_after(f, Token_Colon, "field list");
 					AstNode *type = parse_var_type(f, false);
-					array_add(&decls, ast_field(f, names, type, set_flags));
+					array_add(&decls, ast_field(f, names, type, NULL, set_flags));
 				} else {
 					AstNode *name  = names[0];
 					Token    open  = expect_token(f, Token_OpenBrace);