mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-08 22:01:47 -04:00
parapoly: remove the in-place polymorphic match; substitution engine is now the only resolver
This commit is contained in:
1 parent
2b722879ce
commit
98203dbd56
3 files changed
+44
-610
No files matched your search
+3
-395
@@ -747,8 +747,8 @@ gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerC
|
||||
return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data);
|
||||
}
|
||||
|
||||
gb_internal bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type *type, bool compound, bool modify_type, bool finalize = true);
|
||||
gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type);
|
||||
gb_internal bool subst_poly_assignable(CheckerContext *c, Type *pattern, Type *source, bool modify_type);
|
||||
gb_internal bool subst_check_specialization(CheckerContext *ctx, Type *specialization, Type *type, bool modify_type);
|
||||
gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
|
||||
gb_internal bool check_proc_params_assignable(CheckerContext *c, Type *x, Type *y);
|
||||
|
||||
@@ -955,7 +955,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
|
||||
if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
|
||||
bool modify_type = !c->no_polymorphic_errors;
|
||||
if (is_polymorphic_type_assignable(c, type, s, false, modify_type)) {
|
||||
if (subst_poly_assignable(c, type, s, modify_type)) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -1521,398 +1521,6 @@ gb_internal bool polymorphic_assign_index(Type **gt_, i64 *dst_count, i64 source
|
||||
return false;
|
||||
}
|
||||
|
||||
// In-place matcher: mutates `poly` to bind its polymorphic vars to `source`. This is NOT the primary
|
||||
// resolver — the substitution engine in check_type.cpp handles determine_type_from_polymorphic (both
|
||||
// the resolve and the probe) and spec-conformance (subst_check_specialization / subst_unify_constraint),
|
||||
// and as of the constraint fix it covers those completely: instrumentation shows this matcher produces
|
||||
// no successful result in either path across the stdlib. It is still genuinely reached from:
|
||||
// * check_distance_between_types (assignability scoring for overload resolution),
|
||||
// * the type_is_specialization_of intrinsic (via check_type_specialization_to),
|
||||
// * and as the never-succeeding Subst_Unhandled fallback of the two paths above (genuine subtyping and
|
||||
// untyped/conversion cases the engine defers rather than resolves).
|
||||
// Deleting it outright would require reimplementing subtyping and conversion matching in the engine.
|
||||
gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type) {
|
||||
Operand o = {Addressing_Value};
|
||||
o.type = source;
|
||||
switch (poly->kind) {
|
||||
case Type_Basic:
|
||||
if (compound) return are_types_identical(poly, source);
|
||||
return check_is_assignable_to(c, &o, poly);
|
||||
|
||||
case Type_Named: {
|
||||
if (check_type_specialization_to(c, poly, source, compound, modify_type)) {
|
||||
return true;
|
||||
}
|
||||
if (compound || !is_type_generic(poly)) {
|
||||
return are_types_identical(poly, source);
|
||||
}
|
||||
return check_is_assignable_to(c, &o, poly);
|
||||
}
|
||||
|
||||
case Type_Generic: {
|
||||
if (poly->Generic.specialized != nullptr) {
|
||||
Type *s = poly->Generic.specialized;
|
||||
if (!check_type_specialization_to(c, s, source, compound, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (modify_type) {
|
||||
Type *ds = default_type(source);
|
||||
gb_memmove(poly, ds, gb_size_of(Type));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type_Pointer:
|
||||
if (source->kind == Type_Pointer) {
|
||||
isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
|
||||
if (level > 0) {
|
||||
return true;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true, modify_type);
|
||||
} else if (source->kind == Type_MultiPointer) {
|
||||
isize level = check_is_assignable_to_using_subtype(source->MultiPointer.elem, poly->Pointer.elem);
|
||||
if (level > 0) {
|
||||
return true;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->MultiPointer.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_MultiPointer:
|
||||
if (source->kind == Type_MultiPointer) {
|
||||
isize level = check_is_assignable_to_using_subtype(source->MultiPointer.elem, poly->MultiPointer.elem);
|
||||
if (level > 0) {
|
||||
return true;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->MultiPointer.elem, true, modify_type);
|
||||
} else if (source->kind == Type_Pointer) {
|
||||
isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->MultiPointer.elem);
|
||||
if (level > 0) {
|
||||
return true;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->Pointer.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_SoaPointer:
|
||||
if (source->kind == Type_SoaPointer) {
|
||||
isize level = check_is_assignable_to_using_subtype(source->SoaPointer.elem, poly->SoaPointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
|
||||
if (level > 0) {
|
||||
return true;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->SoaPointer.elem, source->SoaPointer.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Array:
|
||||
if (source->kind == Type_Array) {
|
||||
Type *generic_count = poly->Array.generic_count;
|
||||
i64 count = poly->Array.count;
|
||||
if (generic_count != nullptr) {
|
||||
if (!polymorphic_assign_index(&generic_count, &count, source->Array.count, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
if (modify_type) {
|
||||
poly->Array.generic_count = generic_count;
|
||||
poly->Array.count = count;
|
||||
}
|
||||
}
|
||||
if (count == source->Array.count) {
|
||||
return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true, modify_type);
|
||||
}
|
||||
} else if (source->kind == Type_EnumeratedArray) {
|
||||
if (poly->Array.generic_count != nullptr) {
|
||||
Type *gt = poly->Array.generic_count;
|
||||
GB_ASSERT(gt->kind == Type_Generic);
|
||||
Entity *e = scope_lookup(gt->Generic.scope, gt->Generic.interned_name, 0);
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->kind == Entity_TypeName) {
|
||||
Type *index = source->EnumeratedArray.index;
|
||||
Type *it = base_type(index);
|
||||
if (it->kind != Type_Enum) {
|
||||
return false;
|
||||
}
|
||||
if (!modify_type) {
|
||||
return is_polymorphic_type_assignable(c, poly->Array.elem, source->EnumeratedArray.elem, true, false);
|
||||
}
|
||||
|
||||
// Resolve `[$N]$T` to an enumerated array by constructing the concrete node
|
||||
// (keeping the $T element node) and finalizing poly to it, rather than rewriting
|
||||
// poly's kind and fields one at a time.
|
||||
Type *poly_elem = poly->Array.elem;
|
||||
Type *ea = alloc_type_enumerated_array(poly_elem, index,
|
||||
source->EnumeratedArray.min_value, source->EnumeratedArray.max_value,
|
||||
source->EnumeratedArray.count, source->EnumeratedArray.op);
|
||||
ea->flags.exchange(source->flags);
|
||||
gb_memmove(poly, ea, gb_size_of(Type));
|
||||
|
||||
e->kind = Entity_TypeName;
|
||||
e->TypeName.is_type_alias = true;
|
||||
e->type = index;
|
||||
|
||||
if (poly->EnumeratedArray.count == source->EnumeratedArray.count) {
|
||||
return is_polymorphic_type_assignable(c, poly->EnumeratedArray.elem, source->EnumeratedArray.elem, true, modify_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case Type_EnumeratedArray:
|
||||
if (source->kind == Type_EnumeratedArray) {
|
||||
if (poly->EnumeratedArray.op != source->EnumeratedArray.op) {
|
||||
return false;
|
||||
}
|
||||
if (poly->EnumeratedArray.op) {
|
||||
if (poly->EnumeratedArray.count != source->EnumeratedArray.count) {
|
||||
return false;
|
||||
}
|
||||
if (compare_exact_values(Token_NotEq, *poly->EnumeratedArray.min_value, *source->EnumeratedArray.min_value)) {
|
||||
return false;
|
||||
}
|
||||
if (compare_exact_values(Token_NotEq, *poly->EnumeratedArray.max_value, *source->EnumeratedArray.max_value)) {
|
||||
return false;
|
||||
}
|
||||
return is_polymorphic_type_assignable(c, poly->EnumeratedArray.index, source->EnumeratedArray.index, true, modify_type);
|
||||
}
|
||||
// Evaluate both (for modify_type side effects) but require both to match.
|
||||
bool index = is_polymorphic_type_assignable(c, poly->EnumeratedArray.index, source->EnumeratedArray.index, true, modify_type);
|
||||
bool elem = is_polymorphic_type_assignable(c, poly->EnumeratedArray.elem, source->EnumeratedArray.elem, true, modify_type);
|
||||
return index && elem;
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_DynamicArray:
|
||||
if (source->kind == Type_DynamicArray) {
|
||||
return is_polymorphic_type_assignable(c, poly->DynamicArray.elem, source->DynamicArray.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_FixedCapacityDynamicArray:
|
||||
if (source->kind == Type_FixedCapacityDynamicArray) {
|
||||
Type *generic_capacity = poly->FixedCapacityDynamicArray.generic_capacity;
|
||||
i64 capacity = poly->FixedCapacityDynamicArray.capacity;
|
||||
if (generic_capacity != nullptr) {
|
||||
if (!polymorphic_assign_index(&generic_capacity, &capacity, source->FixedCapacityDynamicArray.capacity, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
if (modify_type) {
|
||||
poly->FixedCapacityDynamicArray.generic_capacity = generic_capacity;
|
||||
poly->FixedCapacityDynamicArray.capacity = capacity;
|
||||
}
|
||||
}
|
||||
if (capacity == source->FixedCapacityDynamicArray.capacity) {
|
||||
return is_polymorphic_type_assignable(c, poly->FixedCapacityDynamicArray.elem, source->FixedCapacityDynamicArray.elem, true, modify_type);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Slice:
|
||||
if (source->kind == Type_Slice) {
|
||||
return is_polymorphic_type_assignable(c, poly->Slice.elem, source->Slice.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Enum:
|
||||
return false;
|
||||
|
||||
case Type_BitSet:
|
||||
if (source->kind == Type_BitSet) {
|
||||
if (!is_type_polymorphic(poly->BitSet.elem)) {
|
||||
if (poly->BitSet.upper != source->BitSet.upper || poly->BitSet.lower != source->BitSet.lower) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For generic types like bit_set[$T] the upper and lower of the poly type will be zeroes since
|
||||
// it could not figure that stuff out when the poly type was created.
|
||||
if (poly->BitSet.upper == 0 && modify_type) {
|
||||
poly->BitSet.upper = source->BitSet.upper;
|
||||
}
|
||||
if (poly->BitSet.lower == 0 && modify_type) {
|
||||
poly->BitSet.lower = source->BitSet.lower;
|
||||
}
|
||||
|
||||
if (poly->BitSet.underlying == nullptr) {
|
||||
if (modify_type) {
|
||||
poly->BitSet.underlying = source->BitSet.underlying;
|
||||
}
|
||||
} else if (!is_polymorphic_type_assignable(c, poly->BitSet.underlying, source->BitSet.underlying, true, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Union:
|
||||
if (source->kind == Type_Union) {
|
||||
TypeUnion *x = &poly->Union;
|
||||
TypeUnion *y = &source->Union;
|
||||
if (x->variants.count != y->variants.count) {
|
||||
return false;
|
||||
}
|
||||
for_array(i, x->variants) {
|
||||
Type *a = x->variants[i];
|
||||
Type *b = y->variants[i];
|
||||
bool ok = is_polymorphic_type_assignable(c, a, b, false, modify_type);
|
||||
if (!ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Struct:
|
||||
if (source->kind == Type_Struct) {
|
||||
if (poly->Struct.soa_kind == source->Struct.soa_kind &&
|
||||
poly->Struct.soa_kind != StructSoa_None) {
|
||||
bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
|
||||
if (ok) switch (source->Struct.soa_kind) {
|
||||
case StructSoa_None:
|
||||
default:
|
||||
GB_PANIC("Unhandled SOA Kind");
|
||||
break;
|
||||
case StructSoa_Fixed:
|
||||
if (modify_type) {
|
||||
Type *type = make_soa_struct_fixed(c, nullptr, poly->Struct.node, poly->Struct.soa_elem, poly->Struct.soa_count, nullptr);
|
||||
gb_memmove(poly, type, gb_size_of(*type));
|
||||
}
|
||||
break;
|
||||
case StructSoa_Slice:
|
||||
if (modify_type) {
|
||||
Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
|
||||
gb_memmove(poly, type, gb_size_of(*type));
|
||||
}
|
||||
break;
|
||||
case StructSoa_Dynamic:
|
||||
if (modify_type) {
|
||||
Type *type = make_soa_struct_dynamic_array(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
|
||||
gb_memmove(poly, type, gb_size_of(*type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
// NOTE(bill): Check for subtypes of
|
||||
// return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_BitField:
|
||||
if (source->kind == Type_BitField) {
|
||||
return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Tuple:
|
||||
GB_PANIC("This should never happen");
|
||||
return false;
|
||||
case Type_Proc:
|
||||
if (source->kind == Type_Proc) {
|
||||
TypeProc *x = &poly->Proc;
|
||||
TypeProc *y = &source->Proc;
|
||||
if (x->calling_convention != y->calling_convention) {
|
||||
return false;
|
||||
}
|
||||
if (x->c_vararg != y->c_vararg) {
|
||||
return false;
|
||||
}
|
||||
if (x->variadic != y->variadic) {
|
||||
return false;
|
||||
}
|
||||
if (x->param_count != y->param_count) {
|
||||
return false;
|
||||
}
|
||||
if (x->result_count != y->result_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < x->param_count; i++) {
|
||||
Entity *a = x->params->Tuple.variables[i];
|
||||
Entity *b = y->params->Tuple.variables[i];
|
||||
bool ok = is_polymorphic_type_assignable(c, a->type, b->type, false, modify_type);
|
||||
if (!ok) return false;
|
||||
}
|
||||
for (isize i = 0; i < x->result_count; i++) {
|
||||
Entity *a = x->results->Tuple.variables[i];
|
||||
Entity *b = y->results->Tuple.variables[i];
|
||||
bool ok = is_polymorphic_type_assignable(c, a->type, b->type, false, modify_type);
|
||||
if (!ok) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Type_Map:
|
||||
if (source->kind == Type_Map) {
|
||||
// Evaluate both (for modify_type side effects) but require both to match.
|
||||
bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
|
||||
bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
|
||||
if (key && value) {
|
||||
if (modify_type) {
|
||||
poly->Map.lookup_result_type = nullptr;
|
||||
init_map_internal_types(poly);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Matrix:
|
||||
if (source->kind == Type_Matrix) {
|
||||
Type *generic_row_count = poly->Matrix.generic_row_count;
|
||||
Type *generic_column_count = poly->Matrix.generic_column_count;
|
||||
i64 row_count = poly->Matrix.row_count;
|
||||
i64 column_count = poly->Matrix.column_count;
|
||||
if (generic_row_count != nullptr) {
|
||||
if (!polymorphic_assign_index(&generic_row_count, &row_count, source->Matrix.row_count, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (generic_column_count != nullptr) {
|
||||
if (!polymorphic_assign_index(&generic_column_count, &column_count, source->Matrix.column_count, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (modify_type && (poly->Matrix.generic_row_count != nullptr || poly->Matrix.generic_column_count != nullptr)) {
|
||||
poly->Matrix.generic_row_count = generic_row_count;
|
||||
poly->Matrix.generic_column_count = generic_column_count;
|
||||
poly->Matrix.row_count = row_count;
|
||||
poly->Matrix.column_count = column_count;
|
||||
poly->Matrix.stride_in_bytes = 0;
|
||||
}
|
||||
if (row_count == source->Matrix.row_count &&
|
||||
column_count == source->Matrix.column_count) {
|
||||
return is_polymorphic_type_assignable(c, poly->Matrix.elem, source->Matrix.elem, true, modify_type);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_SimdVector:
|
||||
if (source->kind == Type_SimdVector) {
|
||||
Type *generic_count = poly->SimdVector.generic_count;
|
||||
i64 count = poly->SimdVector.count;
|
||||
if (generic_count != nullptr) {
|
||||
if (!polymorphic_assign_index(&generic_count, &count, source->SimdVector.count, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
if (modify_type) {
|
||||
poly->SimdVector.generic_count = generic_count;
|
||||
poly->SimdVector.count = count;
|
||||
}
|
||||
}
|
||||
if (count == source->SimdVector.count) {
|
||||
return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
|
||||
if (curr->state != EntityState_InProgress) {
|
||||
return false;
|
||||
|
||||
Reference in new issue
Block a user