diff --git a/releasenotes.md b/releasenotes.md index 0755711b6..8e290f74e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -57,6 +57,7 @@ - Linking fails on operator method imported as `@public` #2224. - Lambda C-style vaargs were not properly rejected, leading to crash #2229. - Incorrect handling of constant null fault causing compiler crash #2232. +- Overload resolution fixes to inline typedef #2226. ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/sema_const.c b/src/compiler/sema_const.c index 851c1dfd1..3ed085605 100644 --- a/src/compiler/sema_const.c +++ b/src/compiler/sema_const.c @@ -291,6 +291,7 @@ static inline ConstInitializer *expr_const_initializer_from_expr(Expr *expr) } } + /** * The following valid cases exist: * @@ -303,14 +304,11 @@ static inline ConstInitializer *expr_const_initializer_from_expr(Expr *expr) */ bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *left, Expr *right, bool *failed_ref) { - ASSERT(concat_expr->resolve_status == RESOLVE_RUNNING); + ASSERT_SPAN(concat_expr, concat_expr->resolve_status == RESOLVE_RUNNING); + if (!sema_check_left_right_const(context, left, right)) return false; ArraySize len = 0; bool use_array = true; Type *indexed_type = NULL; - if (!sema_analyse_expr(context, left)) return false; - if (!sema_cast_const(left)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); - if (!sema_analyse_expr(context, right)) return false; - if (!sema_cast_const(right)) RETURN_SEMA_ERROR(right, "Expected this to evaluate to a constant value."); Type *element_type = left->type->canonical; Type *right_type = right->type->canonical; switch (left->const_expr.const_kind) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c8b8fe66d..eee889855 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1802,27 +1802,37 @@ static bool sema_find_typed_operator_in_list(SemaContext *context, Decl **method { Decl *candidate = *candidate_ref; + Type *candidate_type = candidate ? candidate->func_decl.signature.params[1]->type : NULL; + bool last_first_arg_exact_match = candidate_type ? candidate_type->canonical == binary_type : false; + 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; + bool is_exact_match = true; if (!func->func_decl.is_wildcard_overload) { Type *first_arg = func->func_decl.signature.params[1]->type->canonical; if (first_arg != binary_type) { + is_exact_match = false; if (!binary_arg) continue; if (!may_cast(context, binary_arg, first_arg, false, true)) continue; } } if (candidate && !candidate->func_decl.is_wildcard_overload) { + if (last_first_arg_exact_match && !is_exact_match) continue; + if (!last_first_arg_exact_match && is_exact_match) goto MATCH; + assert(!last_first_arg_exact_match); *ambiguous_ref = func; *candidate_ref = candidate; return false; } +MATCH: candidate = func; + last_first_arg_exact_match = is_exact_match; } *candidate_ref = candidate; return true; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 391cfb69b..0de6a0b2e 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1224,6 +1224,50 @@ static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *e } +static inline bool sema_binary_analyse_with_inference(SemaContext *context, Expr *left, Expr *right, BinaryOp op) +{ + const static int op_table[BINARYOP_LAST + 1] = { + [BINARYOP_AND] = 1, [BINARYOP_OR] = 1, [BINARYOP_CT_AND] = 1, [BINARYOP_CT_OR] = 1, + [BINARYOP_EQ] = 2, [BINARYOP_NE] = 2 }; + int op_result = op_table[op]; + if (op_result == 1) return true; + // If lhs or rhs is an initializer list, infer + bool is_init_rhs = right->expr_kind == EXPR_INITIALIZER_LIST; + bool is_init_lhs = left->expr_kind == EXPR_INITIALIZER_LIST; + if (is_init_rhs && is_init_lhs) goto EVAL_BOTH; + + if (is_init_rhs) + { + if (!sema_analyse_expr(context, left)) return false; + if (type_kind_is_any_vector(type_flatten(left->type)->type_kind)) + { + return sema_analyse_inferred_expr(context, left->type, right); + } + return sema_analyse_expr(context, right); + } + if (is_init_lhs) + { + if (!sema_analyse_expr(context, right)) return false; + if (type_kind_is_any_vector(type_flatten(right->type)->type_kind)) + { + return sema_analyse_inferred_expr(context, right->type, left); + } + return sema_analyse_expr(context, left); + } + + if (op_result != 2) goto EVAL_BOTH; + + if (!sema_analyse_expr(context, left)) return false; + if (left->type->canonical->type_kind == TYPE_ENUM) + { + return sema_analyse_inferred_expr(context, left->type, right); + } + return sema_analyse_expr(context, right); + +EVAL_BOTH: + return sema_analyse_expr(context, left) && sema_analyse_expr(context, right); +} + static inline bool sema_binary_analyse_subexpr(SemaContext *context, Expr *left, Expr *right) { // Special handling of f = FOO_BAR @@ -1273,7 +1317,8 @@ static inline bool sema_binary_analyse_arithmetic_subexpr(SemaContext *context, Expr *right = exprptr(expr->binary_expr.right); // 1. Analyse both sides. - if (!sema_binary_analyse_subexpr(context, left, right)) return false; + ASSERT_SPAN(expr, left->resolve_status == RESOLVE_DONE); + ASSERT_SPAN(expr, right->resolve_status == RESOLVE_DONE); Type *left_type = type_no_optional(left->type)->canonical; Type *right_type = type_no_optional(right->type)->canonical; @@ -8656,10 +8701,22 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Type *infer_ty RETURN_SEMA_ERROR(expr, "You need to add explicit parentheses to clarify precedence."); } BinaryOp operator = expr->binary_expr.operator; + if (operator >= BINARYOP_ASSIGN) + { + if (left->expr_kind != EXPR_TYPEINFO) + { + if (!sema_analyse_expr_lvalue(context, left, NULL)) return false; + } + } + else + { + if (operator == BINARYOP_ELSE) return sema_expr_analyse_or_error(context, expr, left, right, infer_type, failed_ref); + if (!sema_binary_analyse_with_inference(context, left, right, operator)) return false; + } switch (operator) { case BINARYOP_ELSE: - return sema_expr_analyse_or_error(context, expr, left, right, infer_type, failed_ref); + UNREACHABLE case BINARYOP_CT_CONCAT: return sema_expr_analyse_ct_concat(context, expr, left, right, failed_ref); case BINARYOP_CT_OR: diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 9cd87cbff..bd3776ae9 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -130,6 +130,12 @@ INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSiz INLINE void sema_display_deprecated_warning_on_use(Decl *decl, SourceSpan span); bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *left, Expr *right, bool *failed_ref); +INLINE bool sema_check_left_right_const(SemaContext *context, Expr *left, Expr *right) +{ + if (!sema_cast_const(left)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); + if (!sema_cast_const(right)) RETURN_SEMA_ERROR(right, "Expected this to evaluate to a constant value."); + return true; +} INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result) { diff --git a/test/test_suite/methods/overload_method_example.c3t b/test/test_suite/methods/overload_method_example.c3t new file mode 100644 index 000000000..629d32ee2 --- /dev/null +++ b/test/test_suite/methods/overload_method_example.c3t @@ -0,0 +1,39 @@ +// #target: macos-x64 +module test; +struct Word {int x;} +typedef Byte = inline ushort; + +macro Word Word.add0(self, int other) @operator(+) => { self.x + (int)other }; +macro Word Word.add1(self, Byte other) @operator(+) => { self.x + (int)other }; + +fn int main() +{ + Word a = {1}; + Byte b = 2; + Word c = a + b; + return 0; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %a = alloca %Word, align 4 + %b = alloca i16, align 2 + %c = alloca %Word, align 4 + %self = alloca %Word, align 4 + %other = alloca i16, align 2 + %literal = alloca %Word, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %a, ptr align 4 @.__const, i32 4, i1 false) + store i16 2, ptr %b, align 2 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %self, ptr align 4 %a, i32 4, i1 false) + %0 = load i16, ptr %b, align 2 + store i16 %0, ptr %other, align 2 + %1 = load i32, ptr %self, align 4 + %2 = load i16, ptr %other, align 2 + %zext = zext i16 %2 to i32 + %add = add i32 %1, %zext + store i32 %add, ptr %literal, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %c, ptr align 4 %literal, i32 4, i1 false) + ret i32 0 +}