瀏覽代碼

Refactor polymorphic parameter for records; Improve error message for parametric polymorphic enumerated array index types

gingerBill 4 年之前
父節點
當前提交
5ade037b7d
共有 1 個文件被更改,包括 216 次插入327 次删除
  1. 216 327
      src/check_type.cpp

+ 216 - 327
src/check_type.cpp

@@ -337,6 +337,179 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
 	}
 }
 
+Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
+                                      bool *is_polymorphic_, bool *can_check_fields_,
+                                      Ast *node, Array<Operand> *poly_operands,
+                                      Type *named_type, Type *original_type_for_poly) {
+	Type *polymorphic_params_type = nullptr;
+
+	if (polymorphic_params == nullptr) {
+		return polymorphic_params_type;
+	}
+
+	ast_node(field_list, FieldList, polymorphic_params);
+	Slice<Ast *> params = field_list->list;
+	if (params.count != 0) {
+		isize variable_count = 0;
+		for_array(i, params) {
+			Ast *field = params[i];
+			if (ast_node_expect(field, Ast_Field)) {
+				ast_node(f, Field, field);
+				variable_count += gb_max(f->names.count, 1);
+			}
+		}
+
+		auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
+
+		for_array(i, params) {
+			Ast *param = params[i];
+			if (param->kind != Ast_Field) {
+				continue;
+			}
+			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 && default_value == nullptr) {
+				error(param, "Expected a type for this parameter");
+				continue;
+			}
+
+			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;
+					}
+				}
+			}
+
+			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;
+				}
+				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;
+			}
+			if (is_type_untyped(type)) {
+				if (is_type_untyped_undef(type)) {
+					error(params[i], "Cannot determine parameter type from ---");
+				} else {
+					error(params[i], "Cannot determine parameter type from a nil");
+				}
+				type = t_invalid;
+			}
+
+			if (is_type_polymorphic_type) {
+				gbString str = type_to_string(type);
+				error(params[i], "Parameter types cannot be polymorphic, got %s", str);
+				gb_string_free(str);
+				type = t_invalid;
+			}
+
+			if (!is_type_param && !is_type_constant_type(type)) {
+				gbString str = type_to_string(type);
+				error(params[i], "A parameter must be a valid constant type, got %s", str);
+				gb_string_free(str);
+			}
+
+			Scope *scope = ctx->scope;
+			for_array(j, p->names) {
+				Ast *name = p->names[j];
+				if (!ast_node_expect2(name, Ast_Ident, Ast_PolyType)) {
+					continue;
+				}
+				if (name->kind == Ast_PolyType) {
+					name = name->PolyType.type;
+				}
+				Entity *e = nullptr;
+
+				Token token = name->Ident.token;
+
+				if (poly_operands != nullptr) {
+					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;
+							*can_check_fields_ = false;
+						}
+						e = alloc_entity_type_name(scope, token, operand.type);
+						e->TypeName.is_type_alias = true;
+						e->flags |= EntityFlag_PolyConst;
+					} else {
+						if (is_type_polymorphic(base_type(operand.type))) {
+							*is_polymorphic_ = true;
+							*can_check_fields_ = false;
+						}
+						if (e == nullptr) {
+							e = alloc_entity_constant(scope, token, operand.type, operand.value);
+							e->Constant.param_value = param_value;
+						}
+					}
+				} else {
+					if (is_type_param) {
+						e = alloc_entity_type_name(scope, token, type);
+						e->TypeName.is_type_alias = true;
+						e->flags |= EntityFlag_PolyConst;
+					} else {
+						e = alloc_entity_constant(scope, token, type, param_value.value);
+						e->Constant.param_value = param_value;
+					}
+				}
+
+				e->state = EntityState_Resolved;
+				add_entity(ctx->checker, scope, name, e);
+				array_add(&entities, e);
+			}
+		}
+
+		if (entities.count > 0) {
+			Type *tuple = alloc_type_tuple();
+			tuple->Tuple.variables = entities;
+			polymorphic_params_type = tuple;
+		}
+	}
+
+	if (original_type_for_poly != nullptr) {
+		GB_ASSERT(named_type != nullptr);
+		add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
+	}
+
+	return polymorphic_params_type;
+}
+
 void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
 	GB_ASSERT(is_type_struct(struct_type));
 	ast_node(st, StructType, node);
@@ -371,165 +544,12 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 	bool can_check_fields    = true;
 	bool is_poly_specialized = false;
 
-	if (st->polymorphic_params != nullptr) {
-		ast_node(field_list, FieldList, st->polymorphic_params);
-		Slice<Ast *> params = field_list->list;
-		if (params.count != 0) {
-			isize variable_count = 0;
-			for_array(i, params) {
-				Ast *field = params[i];
-				if (ast_node_expect(field, Ast_Field)) {
-					ast_node(f, Field, field);
-					variable_count += gb_max(f->names.count, 1);
-				}
-			}
-
-			auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
-
-			for_array(i, params) {
-				Ast *param = params[i];
-				if (param->kind != Ast_Field) {
-					continue;
-				}
-				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 && default_value == nullptr) {
-					error(param, "Expected a type for this parameter");
-					continue;
-				}
-
-				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;
-						}
-					}
-				}
-
-				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;
-					}
-					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;
-				}
-				if (is_type_untyped(type)) {
-					if (is_type_untyped_undef(type)) {
-						error(params[i], "Cannot determine parameter type from ---");
-					} else {
-						error(params[i], "Cannot determine parameter type from a nil");
-					}
-					type = t_invalid;
-				}
-
-				if (is_type_polymorphic_type) {
-					gbString str = type_to_string(type);
-					error(params[i], "Parameter types cannot be polymorphic, got %s", str);
-					gb_string_free(str);
-					type = t_invalid;
-				}
-
-				if (!is_type_param && !is_type_constant_type(type)) {
-					gbString str = type_to_string(type);
-					error(params[i], "A parameter must be a valid constant type, got %s", str);
-					gb_string_free(str);
-				}
-
-				Scope *scope = ctx->scope;
-				for_array(j, p->names) {
-					Ast *name = p->names[j];
-					if (!ast_node_expect2(name, Ast_Ident, Ast_PolyType)) {
-						continue;
-					}
-					if (name->kind == Ast_PolyType) {
-						name = name->PolyType.type;
-					}
-					Entity *e = nullptr;
-
-					Token token = name->Ident.token;
-
-					if (poly_operands != nullptr) {
-						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;
-								can_check_fields = false;
-							}
-							e = alloc_entity_type_name(scope, token, operand.type);
-							e->TypeName.is_type_alias = true;
-						} else {
-							if (is_type_polymorphic(base_type(operand.type))) {
-								is_polymorphic = true;
-								can_check_fields = false;
-							}
-							if (e == nullptr) {
-								e = alloc_entity_constant(scope, token, operand.type, operand.value);
-								e->Constant.param_value = param_value;
-							}
-						}
-					} else {
-						if (is_type_param) {
-							e = alloc_entity_type_name(scope, token, type);
-							e->TypeName.is_type_alias = true;
-						} else {
-							e = alloc_entity_constant(scope, token, type, param_value.value);
-							e->Constant.param_value = param_value;
-						}
-					}
-
-					e->state = EntityState_Resolved;
-					add_entity(ctx->checker, scope, name, e);
-					array_add(&entities, e);
-				}
-			}
-
-			if (entities.count > 0) {
-				Type *tuple = alloc_type_tuple();
-				tuple->Tuple.variables = entities;
-				polymorphic_params = tuple;
-			}
-		}
-
-		if (original_type_for_poly != nullptr) {
-			GB_ASSERT(named_type != nullptr);
-			add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
-		}
-	}
+	polymorphic_params = check_record_polymorphic_params(
+		ctx, st->polymorphic_params,
+		&is_polymorphic, &can_check_fields,
+		node, poly_operands,
+		named_type, original_type_for_poly
+	);
 
 	if (!is_polymorphic) {
 		is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr;
@@ -587,165 +607,19 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 
 	union_type->Union.scope = ctx->scope;
 
-	Type *polymorphic_params     = nullptr;
-	bool is_polymorphic          = false;
-	bool can_check_fields        = true;
-	bool is_poly_specialized     = false;
-
-	if (ut->polymorphic_params != nullptr) {
-		ast_node(field_list, FieldList, ut->polymorphic_params);
-		Slice<Ast *> params = field_list->list;
-		if (params.count != 0) {
-			isize variable_count = 0;
-			for_array(i, params) {
-				Ast *field = params[i];
-				if (ast_node_expect(field, Ast_Field)) {
-					ast_node(f, Field, field);
-					variable_count += gb_max(f->names.count, 1);
-				}
-			}
-
-			auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
-
-			for_array(i, params) {
-				Ast *param = params[i];
-				if (param->kind != Ast_Field) {
-					continue;
-				}
-				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 && default_value == nullptr) {
-					error(param, "Expected a type for this parameter");
-					continue;
-				}
-				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;
-						}
-					}
-				}
-
-				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;
-					}
-					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;
-				}
-				if (is_type_untyped(type)) {
-					if (is_type_untyped_undef(type)) {
-						error(params[i], "Cannot determine parameter type from ---");
-					} else {
-						error(params[i], "Cannot determine parameter type from a nil");
-					}
-					type = t_invalid;
-				}
-
-				if (is_type_polymorphic_type) {
-					gbString str = type_to_string(type);
-					error(params[i], "Parameter types cannot be polymorphic, got %s", str);
-					gb_string_free(str);
-					type = t_invalid;
-				}
-
-				if (!is_type_param && !is_type_constant_type(type)) {
-					gbString str = type_to_string(type);
-					error(params[i], "A parameter must be a valid constant type, got %s", str);
-					gb_string_free(str);
-				}
-
-				Scope *scope = ctx->scope;
-				for_array(j, p->names) {
-					Ast *name = p->names[j];
-					if (!ast_node_expect2(name, Ast_Ident, Ast_PolyType)) {
-						continue;
-					}
-					if (name->kind == Ast_PolyType) {
-						name = name->PolyType.type;
-					}
-					Entity *e = nullptr;
-
-					Token token = name->Ident.token;
-
-					if (poly_operands != nullptr) {
-						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);
-							if (is_type_polymorphic(base_type(operand.type))) {
-								is_polymorphic = true;
-								can_check_fields = false;
-							}
-							e = alloc_entity_type_name(scope, token, operand.type);
-							e->TypeName.is_type_alias = true;
-						} 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) {
-							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->Constant.param_value = param_value;
-						}
-					}
-
-					e->state = EntityState_Resolved;
-					add_entity(ctx->checker, scope, name, e);
-					array_add(&entities, e);
-				}
-			}
+	// NOTE(bill): Yes I know it's a non-const reference, what you gonna do?
+	bool &is_polymorphic = union_type->Union.is_polymorphic;
 
-			if (entities.count > 0) {
-				Type *tuple = alloc_type_tuple();
-				tuple->Tuple.variables = entities;
-				polymorphic_params = tuple;
-			}
-		}
+	Type *polymorphic_params = nullptr;
+	bool can_check_fields    = true;
+	bool is_poly_specialized = false;
 
-		if (original_type_for_poly != nullptr) {
-			GB_ASSERT(named_type != nullptr);
-			add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
-		}
-	}
+	polymorphic_params = check_record_polymorphic_params(
+		ctx, ut->polymorphic_params,
+		&is_polymorphic, &can_check_fields,
+		node, poly_operands,
+		named_type, original_type_for_poly
+	);
 
 	if (!is_polymorphic) {
 		is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr;
@@ -2661,25 +2535,40 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
 	}
 
 	check_expr_or_type(ctx, o, e);
-	if (o->mode == Addressing_Type && o->type->kind == Type_Generic) {
-		if (ctx->allow_polymorphic_types) {
-			if (o->type->Generic.specialized) {
-				o->type->Generic.specialized = nullptr;
-				error(o->expr, "Polymorphic array length cannot have a specialization");
+	if (o->mode == Addressing_Type) {
+		Type *ot = base_type(o->type);
+
+		if (ot->kind == Type_Generic) {
+			if (ctx->allow_polymorphic_types) {
+				if (ot->Generic.specialized) {
+					ot->Generic.specialized = nullptr;
+					error(o->expr, "Polymorphic array length cannot have a specialization");
+				}
+				return 0;
 			}
-			return 0;
 		}
-	}
-	if (o->mode == Addressing_Type) {
-		if (is_type_enum(o->type)) {
+		if (is_type_enum(ot)) {
 			return -1;
 		}
 	}
 
 	if (o->mode != Addressing_Constant) {
 		if (o->mode != Addressing_Invalid) {
+			gbString s = expr_to_string(o->expr);
+			error(e, "Array count must be a constant integer, got %s", s);
+			gb_string_free(s);
+
+			Entity *e = entity_of_node(o->expr);
+			if (e != nullptr) {
+				// NOTE(bill, 2021-03-27): Improve error message for parametric polymorphic parameters which want to generate
+				// and enumerated array but cannot determine what it ought to be yet
+				if (ctx->allow_polymorphic_types && e->kind == Entity_TypeName && e->type == t_typeid && e->flags&EntityFlag_PolyConst) {
+					error_line("\tSuggestion: 'where' clause may be required to restrict the enumerated array index type to an enum\n");
+					error_line("\t            'where intrinsics.type_is_enum(%.*s)'\n", LIT(e->token.string));
+				}
+			}
+
 			o->mode = Addressing_Invalid;
-			error(e, "Array count must be a constant");
 		}
 		return 0;
 	}
@@ -2706,7 +2595,7 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
 		}
 	}
 
-	error(e, "Array count must be an integer");
+	error(e, "Array count must be a constant integer");
 	return 0;
 }