- Support untyped second argument for operator overloading.

- Distinct versions of builtin types ignore @operator overloads #2204.
- @operator macro using untyped parameter causes compiler segfault #2200.
- Add comparison with `==` for ZString types.
This commit is contained in:
Christoffer Lerno
2025-06-13 17:12:39 +02:00
parent 82491a6f85
commit e0237096d6
8 changed files with 201 additions and 59 deletions

View File

@@ -19,7 +19,7 @@ static inline bool unit_add_base_extension_method(SemaContext *context, Compilat
static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl *method);
static bool sema_analyse_operator_common(SemaContext *context, Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr,
uint32_t parameters);
static inline Decl *operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload,
static inline bool operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload,
OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref);
static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped);
static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl *method);
@@ -48,7 +48,7 @@ static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **att
static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl, bool *erase_decl);
static bool sema_check_section(SemaContext *context, Attr *attr);
static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext *c, Decl *decl, bool *erase_decl);
static Decl *sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref);
static bool sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref);
static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped);
static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl);
@@ -1709,16 +1709,15 @@ INLINE bool decl_matches_overload(Decl *method, Type *type, OperatorOverload ove
return method->func_decl.operator == overload && typeget(method->func_decl.type_parent)->canonical == type;
}
static inline Decl *operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref)
static inline bool operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref)
{
if (module->is_generic) return NULL;
Decl *found = sema_find_typed_operator_in_list(c, module->private_method_extensions, operator_overload, OVERLOAD_TYPE_SYMMETRIC, method_type, binary_arg, binary_type, candidate_ref, ambiguous_ref);
if (found) return found;
if (!sema_find_typed_operator_in_list(c, module->private_method_extensions, operator_overload, OVERLOAD_TYPE_SYMMETRIC, method_type, binary_arg, binary_type, candidate_ref, ambiguous_ref)) return false;
FOREACH(Module *, sub_module, module->sub_modules)
{
return operator_in_module_typed(c, sub_module, operator_overload, overload_type, method_type, binary_arg, binary_type, candidate_ref, ambiguous_ref);
if (!operator_in_module_typed(c, sub_module, operator_overload, overload_type, method_type, binary_arg, binary_type, candidate_ref, ambiguous_ref)) return false;
}
return NULL;
return true;
}
static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped)
@@ -1779,6 +1778,7 @@ Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverl
static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped)
{
Decl *wildcard = NULL;
FOREACH(Decl *, func, methods)
{
if (func == skipped) continue;
@@ -1786,39 +1786,46 @@ static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOver
if (func->func_decl.operator != operator_overload) continue;
if (parent_type && parent_type != typeget(func->func_decl.type_parent)) continue;
if ((overload_type & func->func_decl.overload_type) == 0) continue;
Type *first_arg = func->func_decl.signature.params[1]->type->canonical;
if (first_arg != binary_type) continue;
if (func->func_decl.is_wildcard_overload)
{
wildcard = func;
continue;
}
Type *first_arg = func->func_decl.signature.params[1]->type;
if (first_arg->canonical != binary_type) continue;
return func;
}
return NULL;
return wildcard;
}
static Decl *sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref)
static bool sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref)
{
Decl *candidate = *candidate_ref;
FOREACH(Decl *, func, methods)
{
if (func->func_decl.operator != operator_overload) continue;
if (parent_type && parent_type != typeget(func->func_decl.type_parent)) continue;
if ((overload_type & func->func_decl.overload_type) == 0) continue;
Type *first_arg = func->func_decl.signature.params[1]->type->canonical;
if (first_arg != binary_type)
if (!func->func_decl.is_wildcard_overload)
{
if (!binary_arg) continue;
if (may_cast(context, binary_arg, first_arg, false, true))
Type *first_arg = func->func_decl.signature.params[1]->type->canonical;
if (first_arg != binary_type)
{
if (*candidate_ref)
{
*ambiguous_ref = func;
continue;
}
*candidate_ref = func;
if (!binary_arg) continue;
if (!may_cast(context, binary_arg, first_arg, false, true)) continue;
}
continue;
}
unit_register_external_symbol(context, func);
return func;
if (candidate && !candidate->func_decl.is_wildcard_overload)
{
*ambiguous_ref = func;
*candidate_ref = candidate;
return false;
}
candidate = func;
}
return NULL;
*candidate_ref = candidate;
return true;
}
static Decl *sema_find_exact_typed_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, OverloadType overload_type, Type *param_type, Decl *skipped)
@@ -1848,67 +1855,69 @@ static Decl *sema_find_exact_typed_operator(SemaContext *context, Type *type, Op
return NULL;
}
static Decl *sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overloat_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, Decl **ambiguous_ref)
static bool sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overloat_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, Decl **ambiguous_ref)
{
// Can we find the overload directly on the method?
Decl *func = sema_find_typed_operator_in_list(context, lhs_type->decl->methods, operator_overload, overloat_type, lhs_type,
rhs, rhs_type, candidate_ref, ambiguous_ref);
if (func) return func;
if (!sema_find_typed_operator_in_list(context, lhs_type->decl->methods,
operator_overload, overloat_type, lhs_type,
rhs, rhs_type, candidate_ref, ambiguous_ref)) return false;
// Can we find it as a local extension?
Decl *extension = sema_find_typed_operator_in_list(context, context->unit->local_method_extensions,
operator_overload, overloat_type, lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref);
if (!sema_find_typed_operator_in_list(context, context->unit->local_method_extensions,
operator_overload, overloat_type, lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref)) return false;
// Can we find it in the current module?
if (extension) return extension;
extension = operator_in_module_typed(context, context->compilation_unit->module, operator_overload, overloat_type,
lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref);
if (extension) return extension;
// TODO incorrect, doesn't recurse
// Look through our public imports
if (!operator_in_module_typed(context, context->compilation_unit->module, operator_overload, overloat_type,
lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref)) return false;
FOREACH(Decl *, import, context->unit->public_imports)
{
extension = operator_in_module_typed(context, import->import.module, operator_overload, overloat_type,
lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref);
if (extension) return extension;
if (!operator_in_module_typed(context, import->import.module, operator_overload, overloat_type,
lhs_type, rhs, rhs_type, candidate_ref, ambiguous_ref)) return false;
}
return NULL;
return true;
}
Decl *sema_find_typed_operator(SemaContext *context, OperatorOverload operator_overload, Expr *lhs, Expr *rhs, Decl **ambiguous_ref, bool *reverse)
{
assert(operator_overload >= OVERLOAD_TYPED_START);
assert(lhs && rhs && ambiguous_ref);
Type *left_type = type_no_optional(lhs->type)->canonical;
Type *right_type = type_no_optional(rhs->type)->canonical;
Decl *candidate = NULL;
Decl *ambiguous = NULL;
Decl *left_candidate = NULL;
*reverse = false;
if (type_is_user_defined(left_type))
{
Decl *found = sema_find_typed_operator_type(context, operator_overload, OVERLOAD_TYPE_LEFT, left_type, right_type, rhs, &candidate, &ambiguous);
if (found) return found;
if (!sema_find_typed_operator_type(context, operator_overload, OVERLOAD_TYPE_LEFT, left_type, right_type, rhs, &candidate, &ambiguous)) return NULL;
left_candidate = candidate;
}
candidate = NULL;
if (type_is_user_defined(right_type))
{
Decl *found = sema_find_typed_operator_type(context, operator_overload, OVERLOAD_TYPE_RIGHT, right_type, left_type, lhs, &candidate, &ambiguous);
if (found)
{
*reverse = true;
return found;
}
if (!sema_find_typed_operator_type(context, operator_overload, OVERLOAD_TYPE_RIGHT, right_type, left_type, lhs, &candidate, &ambiguous)) return NULL;
}
if (ambiguous)
// If one or the other is missing, pick the right one.
if (!left_candidate)
{
*ambiguous_ref = ambiguous;
return NULL;
}
if (candidate)
{
*reverse = candidate != left_candidate;
*reverse = true;
return candidate;
}
if (!candidate) return left_candidate;
// Both exist, prefer non-wildcard
if (left_candidate->func_decl.is_wildcard_overload && !candidate->func_decl.is_wildcard_overload)
{
*reverse = true;
return candidate;
}
if (candidate->func_decl.is_wildcard_overload && !left_candidate->func_decl.is_wildcard_overload)
{
return left_candidate;
}
*ambiguous_ref = candidate;
return NULL;
}
@@ -2193,7 +2202,22 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type
// See if the operator has already been defined.
OperatorOverload operator = method->func_decl.operator;
Type *second_param = vec_size(method->func_decl.signature.params) > 1 ? method->func_decl.signature.params[1]->type->canonical : NULL;
Type *second_param = NULL;
if (vec_size(method->func_decl.signature.params) > 1)
{
second_param = method->func_decl.signature.params[1]->type;
if (!second_param)
{
if (method->func_decl.overload_type & OVERLOAD_TYPE_RIGHT)
{
RETURN_SEMA_ERROR(method, "Only regular overloads can have untyped right hand parameters");
}
method->func_decl.is_wildcard_overload = true;
second_param = type_void;
}
second_param = second_param->canonical;
}
if (!type_is_user_defined(parent_type))
{