Browse Source

Fix default parameters on record types

gingerBill 4 years ago
parent
commit
31f4590f4b
4 changed files with 142 additions and 45 deletions
  1. 53 10
      src/check_expr.cpp
  2. 86 33
      src/check_type.cpp
  3. 1 0
      src/entity.cpp
  4. 2 2
      src/parser.cpp

+ 53 - 10
src/check_expr.cpp

@@ -7338,6 +7338,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
 						Entity *e = params->variables[i];
 						if (e->kind == Entity_Constant) {
 							check_expr_with_type_hint(c, &operands[i], fv->value, e->type);
+							continue;
 						}
 					}
 
@@ -7371,9 +7372,22 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
 
 	TypeTuple *tuple = get_record_polymorphic_params(original_type);
 	isize param_count = tuple->variables.count;
+	isize minimum_param_count = param_count;
+	for (minimum_param_count = tuple->variables.count-1; minimum_param_count >= 0; minimum_param_count--) {
+		Entity *e = tuple->variables[minimum_param_count];
+		if (e->kind != Entity_Constant) {
+			break;
+		}
+		if (e->Constant.param_value.kind == ParameterValue_Invalid) {
+			break;
+		}
+	}
 
 	Array<Operand> ordered_operands = operands;
-	if (named_fields) {
+	if (!named_fields) {
+		ordered_operands = array_make<Operand>(c->allocator, param_count);
+		array_copy(&ordered_operands, operands, 0);
+	} else {
 		bool *visited = gb_alloc_array(c->allocator, bool, param_count);
 
 		// LEAK(bill)
@@ -7440,26 +7454,55 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
 		return err;
 	}
 
-	if (param_count < ordered_operands.count) {
-		error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
-		err = CallArgumentError_TooManyArguments;
-	} else if (param_count > ordered_operands.count) {
-		error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
-		err = CallArgumentError_TooFewArguments;
+	if (minimum_param_count != param_count) {
+		if (param_count < ordered_operands.count) {
+			error(call, "Too many polymorphic type arguments, expected a maximum of %td, got %td", param_count, ordered_operands.count);
+			err = CallArgumentError_TooManyArguments;
+		} else if (minimum_param_count > ordered_operands.count) {
+			error(call, "Too few polymorphic type arguments, expected a minimum of %td, got %td", minimum_param_count, ordered_operands.count);
+			err = CallArgumentError_TooFewArguments;
+		}
+	} else {
+		if (param_count < ordered_operands.count) {
+			error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+			err = CallArgumentError_TooManyArguments;
+		} else if (param_count > ordered_operands.count) {
+			error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+			err = CallArgumentError_TooFewArguments;
+		}
 	}
 
 	if (err != 0) {
 		return err;
 	}
 
+	if (minimum_param_count != param_count) {
+		isize missing_count = 0;
+		// NOTE(bill): Replace missing operands with the default values (if possible)
+		for_array(i, ordered_operands) {
+			Operand *o = &ordered_operands[i];
+			if (o->expr == nullptr) {
+				Entity *e = tuple->variables[i];
+				if (e->kind == Entity_Constant) {
+					missing_count += 1;
+					o->mode = Addressing_Constant;
+					o->type = default_type(e->type);
+					o->expr = unparen_expr(e->Constant.param_value.original_ast_expr);
+					if (e->Constant.param_value.kind == ParameterValue_Constant) {
+						o->value = e->Constant.param_value.value;
+					}
+				}
+			}
+		}
+	}
+
 	i64 score = 0;
 	for (isize i = 0; i < param_count; i++) {
+		Entity *e = tuple->variables[i];
 		Operand *o = &ordered_operands[i];
 		if (o->mode == Addressing_Invalid) {
 			continue;
 		}
-		Entity *e = tuple->variables[i];
-
 		if (e->kind == Entity_TypeName) {
 			if (o->mode != Addressing_Type) {
 				if (show_error) {
@@ -7800,7 +7843,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
 		err_str = "used as a value";
 		break;
 	case Addressing_Type:
-		err_str = "is not an expression";
+		err_str = "is not an expression but a";
 		break;
 	case Addressing_Builtin:
 		err_str = "must be called";

+ 86 - 33
src/check_type.cpp

@@ -1,3 +1,4 @@
+ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
 
 void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
 	t = base_type(t);
@@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 				}
 				ast_node(p, Field, param);
 				Ast *type_expr = p->type;
+				Ast *default_value = unparen_expr(p->default_value);
 				Type *type = nullptr;
 				bool is_type_param = false;
 				bool is_type_polymorphic_type = false;
-				if (type_expr == nullptr) {
+				if (type_expr == nullptr && default_value == nullptr) {
 					error(param, "Expected a type for this parameter");
 					continue;
 				}
-				if (type_expr->kind == Ast_Ellipsis) {
-					type_expr = type_expr->Ellipsis.expr;
-					error(param, "A polymorphic parameter cannot be variadic");
+
+				if (type_expr != nullptr) {
+					if (type_expr->kind == Ast_Ellipsis) {
+						type_expr = type_expr->Ellipsis.expr;
+						error(param, "A polymorphic parameter cannot be variadic");
+					}
+					if (type_expr->kind == Ast_TypeidType) {
+						is_type_param = true;
+						Type *specialization = nullptr;
+						if (type_expr->TypeidType.specialization != nullptr) {
+							Ast *s = type_expr->TypeidType.specialization;
+							specialization = check_type(ctx, s);
+						}
+						type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+					} else {
+						type = check_type(ctx, type_expr);
+						if (is_type_polymorphic(type)) {
+							is_type_polymorphic_type = true;
+						}
+					}
 				}
-				if (type_expr->kind == Ast_TypeidType) {
-					is_type_param = true;
-					Type *specialization = nullptr;
-					if (type_expr->TypeidType.specialization != nullptr) {
-						Ast *s = type_expr->TypeidType.specialization;
-						specialization = check_type(ctx, s);
+
+				ParameterValue param_value = {};
+				if (default_value != nullptr)  {
+					Type *out_type = nullptr;
+					param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+					if (type == nullptr && out_type != nullptr) {
+						type = out_type;
 					}
-					type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
-				} else {
-					type = check_type(ctx, type_expr);
-					if (is_type_polymorphic(type)) {
-						is_type_polymorphic_type = true;
+					if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+						error(default_value, "Invalid parameter value");
+						param_value = {};
 					}
 				}
 
+
 				if (type == nullptr) {
 					error(params[i], "Invalid parameter type");
 					type = t_invalid;
@@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 					Token token = name->Ident.token;
 
 					if (poly_operands != nullptr) {
-						Operand operand = (*poly_operands)[entities.count];
+						Operand operand = {};
+						operand.type = t_invalid;
+						if (entities.count < poly_operands->count) {
+							operand = (*poly_operands)[entities.count];
+						} else if (param_value.kind != ParameterValue_Invalid) {
+							operand.mode = Addressing_Constant;
+							operand.value = param_value.value;
+						}
 						if (is_type_param) {
 							if (is_type_polymorphic(base_type(operand.type))) {
 								is_polymorphic = true;
@@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 							}
 							if (e == nullptr) {
 								e = alloc_entity_constant(scope, token, operand.type, operand.value);
+								e->Constant.param_value = param_value;
 							}
 						}
 					} else {
@@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 							e = alloc_entity_type_name(scope, token, type);
 							e->TypeName.is_type_alias = true;
 						} else {
-							e = alloc_entity_constant(scope, token, type, empty_exact_value);
+							e = alloc_entity_constant(scope, token, type, param_value.value);
+							e->Constant.param_value = param_value;
 						}
 					}
 
@@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 				}
 				ast_node(p, Field, param);
 				Ast *type_expr = p->type;
+				Ast *default_value = unparen_expr(p->default_value);
 				Type *type = nullptr;
 				bool is_type_param = false;
 				bool is_type_polymorphic_type = false;
-				if (type_expr == nullptr) {
+				if (type_expr == nullptr && default_value == nullptr) {
 					error(param, "Expected a type for this parameter");
 					continue;
 				}
-				if (type_expr->kind == Ast_Ellipsis) {
-					type_expr = type_expr->Ellipsis.expr;
-					error(param, "A polymorphic parameter cannot be variadic");
+				if (type_expr != nullptr) {
+					if (type_expr->kind == Ast_Ellipsis) {
+						type_expr = type_expr->Ellipsis.expr;
+						error(param, "A polymorphic parameter cannot be variadic");
+					}
+					if (type_expr->kind == Ast_TypeidType) {
+						is_type_param = true;
+						Type *specialization = nullptr;
+						if (type_expr->TypeidType.specialization != nullptr) {
+							Ast *s = type_expr->TypeidType.specialization;
+							specialization = check_type(ctx, s);
+						}
+						type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
+					} else {
+						type = check_type(ctx, type_expr);
+						if (is_type_polymorphic(type)) {
+							is_type_polymorphic_type = true;
+						}
+					}
 				}
-				if (type_expr->kind == Ast_TypeidType) {
-					is_type_param = true;
-					Type *specialization = nullptr;
-					if (type_expr->TypeidType.specialization != nullptr) {
-						Ast *s = type_expr->TypeidType.specialization;
-						specialization = check_type(ctx, s);
+
+				ParameterValue param_value = {};
+				if (default_value != nullptr)  {
+					Type *out_type = nullptr;
+					param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
+					if (type == nullptr && out_type != nullptr) {
+						type = out_type;
 					}
-					type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
-				} else {
-					type = check_type(ctx, type_expr);
-					if (is_type_polymorphic(type)) {
-						is_type_polymorphic_type = true;
+					if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
+						error(default_value, "Invalid parameter value");
+						param_value = {};
 					}
 				}
 
@@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 					Token token = name->Ident.token;
 
 					if (poly_operands != nullptr) {
-						Operand operand = (*poly_operands)[entities.count];
+						Operand operand = {};
+						operand.type = t_invalid;
+						if (entities.count < poly_operands->count) {
+							operand = (*poly_operands)[entities.count];
+						} else if (param_value.kind != ParameterValue_Invalid) {
+							operand.mode = Addressing_Constant;
+							operand.value = param_value.value;
+						}
 						if (is_type_param) {
 							GB_ASSERT(operand.mode == Addressing_Type ||
 							          operand.mode == Addressing_Invalid);
@@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 						} else {
 							// GB_ASSERT(operand.mode == Addressing_Constant);
 							e = alloc_entity_constant(scope, token, operand.type, operand.value);
+							e->Constant.param_value = param_value;
 						}
 					} else {
 						if (is_type_param) {
@@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 							e->TypeName.is_type_alias = true;
 						} else {
 							e = alloc_entity_constant(scope, token, type, empty_exact_value);
+							e->Constant.param_value = param_value;
 						}
 					}
 

+ 1 - 0
src/entity.cpp

@@ -120,6 +120,7 @@ struct Entity {
 	union {
 		struct {
 			ExactValue value;
+			ParameterValue param_value;
 		} Constant;
 		struct {
 			Ast *init_expr; // only used for some variables within procedure bodies

+ 2 - 2
src/parser.cpp

@@ -2122,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 		if (allow_token(f, Token_OpenParen)) {
 			isize param_count = 0;
-			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
 			if (param_count == 0) {
 				syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter");
 				polymorphic_params = nullptr;
@@ -2203,7 +2203,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 		if (allow_token(f, Token_OpenParen)) {
 			isize param_count = 0;
-			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, true, true);
 			if (param_count == 0) {
 				syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
 				polymorphic_params = nullptr;