mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Comparing slices and arrays of user-defined types that implement == operator now works #2486.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
- Infer generic parameters lhs -> rhs: `List{int} x = list::NOHEAP`.
|
||||
- Unify generic and regular module namespace.
|
||||
- `env::PROJECT_VERSION` now returns the version in project.json.
|
||||
- Comparing slices and arrays of user-defined types that implement == operator now works #2486.
|
||||
|
||||
### Fixes
|
||||
- Compiler assert with var x @noinit = 0 #2452
|
||||
|
||||
@@ -855,6 +855,7 @@ typedef struct
|
||||
{
|
||||
ExprId expr;
|
||||
SubscriptIndex index;
|
||||
bool no_check;
|
||||
} ExprSubscript;
|
||||
|
||||
typedef struct
|
||||
@@ -2032,6 +2033,13 @@ ARENA_DEF(expr, Expr)
|
||||
ARENA_DEF(decl, Decl)
|
||||
ARENA_DEF(type_info, TypeInfo)
|
||||
|
||||
INLINE Ast *ast_new(AstKind kind, SourceSpan span)
|
||||
{
|
||||
Ast *ast = ast_calloc();
|
||||
ast->ast_kind = kind;
|
||||
ast->span = span;
|
||||
return ast;
|
||||
}
|
||||
|
||||
INLINE TypeInfo *vartype(Decl *var)
|
||||
{
|
||||
@@ -2315,16 +2323,18 @@ Expr *expr_new_const_string(SourceSpan span, const char *string);
|
||||
Expr *expr_new_const_null(SourceSpan span, Type *type);
|
||||
Expr *expr_new_const_initializer(SourceSpan span, Type *type, ConstInitializer *initializer);
|
||||
Expr *expr_new_expr_list_resolved(SourceSpan span, Type *type, Expr **expressions);
|
||||
Expr *expr_new_binary(SourceSpan span, Expr *left, Expr *right, BinaryOp op);
|
||||
Expr *expr_new_cond(Expr *expr);
|
||||
const char *expr_kind_to_string(ExprKind kind);
|
||||
bool expr_is_simple(Expr *expr, bool to_float);
|
||||
bool expr_is_pure(Expr *expr);
|
||||
bool expr_is_runtime_const(Expr *expr);
|
||||
Expr *expr_generate_decl(Decl *decl, Expr *assign);
|
||||
Expr *expr_new_two(Expr *first, Expr *second);
|
||||
void expr_rewrite_two(Expr *original, Expr *first, Expr *second);
|
||||
void expr_insert_addr(Expr *original);
|
||||
bool sema_expr_rewrite_insert_deref(SemaContext *context, Expr *original);
|
||||
Expr *expr_generate_decl(Decl *decl, Expr *assign);
|
||||
Expr *expr_generated_local(Expr *assign, Decl **decl_ref);
|
||||
Expr *expr_variable(Decl *decl);
|
||||
Expr *expr_negate_expr(Expr *expr);
|
||||
bool expr_may_addr(Expr *expr);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include <iso646.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
@@ -592,6 +593,16 @@ void expr_insert_addr(Expr *original)
|
||||
original->unary_expr.expr = inner;
|
||||
}
|
||||
|
||||
Expr *expr_generated_local(Expr *assign, Decl **decl_ref)
|
||||
{
|
||||
Decl *decl = decl_new_generated_var(assign->type, VARDECL_LOCAL, assign->span);
|
||||
Expr *expr_decl = expr_new(EXPR_DECL, decl->span);
|
||||
expr_decl->decl_expr = decl;
|
||||
decl->var.init_expr = assign;
|
||||
*decl_ref = decl;
|
||||
return expr_decl;
|
||||
}
|
||||
|
||||
Expr *expr_generate_decl(Decl *decl, Expr *assign)
|
||||
{
|
||||
ASSERT(decl->decl_kind == DECL_VAR);
|
||||
@@ -1006,6 +1017,23 @@ bool expr_is_simple(Expr *expr, bool to_float)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
Expr *expr_new_binary(SourceSpan span, Expr *left, Expr *right, BinaryOp op)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_BINARY;
|
||||
expr->span = span;
|
||||
expr->binary_expr.operator = op;
|
||||
expr->binary_expr.left = exprid(left);
|
||||
expr->binary_expr.right = exprid(right);
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_cond(Expr *expr)
|
||||
{
|
||||
Expr *cond = expr_new(EXPR_COND, expr->span);
|
||||
vec_add(cond->cond_expr, expr);
|
||||
return cond;
|
||||
}
|
||||
|
||||
Expr *expr_new(ExprKind kind, SourceSpan start)
|
||||
{
|
||||
|
||||
@@ -711,6 +711,9 @@ static inline void llvm_emit_subscript_addr(GenContext *c, BEValue *value, Expr
|
||||
Expr *parent_expr = exprptr(expr->subscript_expr.expr);
|
||||
Expr *index_expr = exprptr(expr->subscript_expr.index.expr);
|
||||
Type *parent_type = type_lowering(parent_expr->type);
|
||||
|
||||
bool is_safe = !expr->subscript_expr.no_check && safe_mode_enabled();
|
||||
|
||||
// First, get thing being subscripted.
|
||||
llvm_emit_expr(c, value, parent_expr);
|
||||
BEValue len = { .value = NULL };
|
||||
@@ -721,7 +724,7 @@ static inline void llvm_emit_subscript_addr(GenContext *c, BEValue *value, Expr
|
||||
bool start_from_end = expr->subscript_expr.index.start_from_end;
|
||||
if (parent_type_kind == TYPE_SLICE)
|
||||
{
|
||||
needs_len = (safe_mode_enabled() && !llvm_is_global_eval(c)) || start_from_end;
|
||||
needs_len = (is_safe && !llvm_is_global_eval(c)) || start_from_end;
|
||||
if (needs_len)
|
||||
{
|
||||
if (LLVMIsAGlobalVariable(value->value) && llvm_is_global_eval(c))
|
||||
@@ -740,7 +743,7 @@ static inline void llvm_emit_subscript_addr(GenContext *c, BEValue *value, Expr
|
||||
{
|
||||
// From back should always be folded.
|
||||
ASSERT(!expr_is_const(expr) || !start_from_end);
|
||||
needs_len = (safe_mode_enabled() && !expr_is_const(expr)) || start_from_end;
|
||||
needs_len = (is_safe && !expr_is_const(expr)) || start_from_end;
|
||||
if (needs_len)
|
||||
{
|
||||
llvm_value_set_int(c, &len, type_isz, value->type->array.len);
|
||||
@@ -760,7 +763,7 @@ static inline void llvm_emit_subscript_addr(GenContext *c, BEValue *value, Expr
|
||||
ASSERT(needs_len);
|
||||
index.value = LLVMBuildNUWSub(c->builder, llvm_zext_trunc(c, len.value, llvm_get_type(c, index.type)), index.value, "");
|
||||
}
|
||||
if (needs_len && safe_mode_enabled() && !llvm_is_global_eval(c))
|
||||
if (needs_len && is_safe && !llvm_is_global_eval(c))
|
||||
{
|
||||
llvm_emit_array_bounds_check(c, &index, len.value, index_expr->span);
|
||||
}
|
||||
@@ -6230,15 +6233,17 @@ DONE:
|
||||
static inline void llvm_emit_macro_block(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
DebugScope *old_inline_location = c->debug.block_stack;
|
||||
DebugScope updated;
|
||||
if (llvm_use_debug(c))
|
||||
DebugScope updated_val;
|
||||
DebugScope *inline_location = old_inline_location;
|
||||
Decl *macro = expr->macro_block.macro;
|
||||
if (llvm_use_debug(c) && macro)
|
||||
{
|
||||
SourceSpan span = expr->span;
|
||||
Decl *macro = expr->macro_block.macro;
|
||||
LLVMMetadataRef macro_def = llvm_debug_create_macro(c, macro);
|
||||
LLVMMetadataRef loc = llvm_create_debug_location(c, span);
|
||||
|
||||
updated = (DebugScope) { .lexical_block = macro_def, .inline_loc = loc, .outline_loc = old_inline_location };
|
||||
updated_val = (DebugScope) { .lexical_block = macro_def, .inline_loc = loc, .outline_loc = old_inline_location };
|
||||
inline_location = &updated_val;
|
||||
}
|
||||
FOREACH(Decl *, val, expr->macro_block.params)
|
||||
{
|
||||
@@ -6273,7 +6278,7 @@ static inline void llvm_emit_macro_block(GenContext *c, BEValue *be_value, Expr
|
||||
llvm_emit_expr(c, &value, init_expr);
|
||||
if (llvm_value_is_addr(&value) || val->var.is_written || val->var.is_addr || llvm_use_accurate_debug_info(c))
|
||||
{
|
||||
c->debug.block_stack = &updated;
|
||||
c->debug.block_stack = inline_location;
|
||||
llvm_emit_and_set_decl_alloca(c, val);
|
||||
llvm_store_decl(c, val, &value);
|
||||
continue;
|
||||
@@ -6282,7 +6287,7 @@ static inline void llvm_emit_macro_block(GenContext *c, BEValue *be_value, Expr
|
||||
val->backend_value = value.value;
|
||||
}
|
||||
|
||||
c->debug.block_stack = &updated;
|
||||
c->debug.block_stack = inline_location;
|
||||
llvm_emit_return_block(c, be_value, expr->type, expr->macro_block.first_stmt, expr->macro_block.block_exit);
|
||||
bool is_unreachable = expr->macro_block.is_noreturn && c->current_block;
|
||||
if (is_unreachable)
|
||||
|
||||
@@ -918,10 +918,7 @@ static Expr *parse_orelse(ParseContext *c, Expr *left_side, SourceSpan lhs_start
|
||||
// Assignment operators have precedence right -> left.
|
||||
ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, PREC_TERNARY), poisoned_expr);
|
||||
|
||||
Expr *expr = expr_new(EXPR_BINARY, lhs_start);
|
||||
expr->binary_expr.operator = BINARYOP_ELSE;
|
||||
expr->binary_expr.left = exprid(left_side);
|
||||
expr->binary_expr.right = exprid(right_side);
|
||||
Expr *expr = expr_new_binary(lhs_start, left_side, right_side, BINARYOP_ELSE);
|
||||
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
return expr;
|
||||
@@ -947,10 +944,7 @@ static Expr *parse_binary(ParseContext *c, Expr *left_side, SourceSpan start)
|
||||
ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, rules[operator_type].precedence + 1), poisoned_expr);
|
||||
}
|
||||
|
||||
Expr *expr = expr_new(EXPR_BINARY, start);
|
||||
expr->binary_expr.operator = binaryop_from_token(operator_type);
|
||||
expr->binary_expr.left = exprid(left_side);
|
||||
expr->binary_expr.right = exprid(right_side);
|
||||
Expr *expr = expr_new_binary(start, left_side, right_side, binaryop_from_token(operator_type));
|
||||
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
return expr;
|
||||
|
||||
@@ -5735,20 +5735,18 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr,
|
||||
expr_rewrite_const_bool(expr, type_bool, type_is_ordered(flat));
|
||||
return true;
|
||||
case TYPE_PROPERTY_IS_EQ:
|
||||
if (type_is_comparable(flat))
|
||||
switch (sema_type_can_check_equality_with_overload(context, flat))
|
||||
{
|
||||
expr_rewrite_const_bool(expr, type_bool, type_is_comparable(flat));
|
||||
case BOOL_ERR:
|
||||
return false;
|
||||
case BOOL_TRUE:
|
||||
expr_rewrite_const_bool(expr, type_bool, true);
|
||||
return true;
|
||||
}
|
||||
if (type_is_user_defined(type))
|
||||
{
|
||||
BoolErr res = sema_type_has_equality_overload(context, type);
|
||||
if (res == BOOL_ERR) return false;
|
||||
expr_rewrite_const_bool(expr, type_bool, res == BOOL_TRUE);
|
||||
return true;
|
||||
}
|
||||
case BOOL_FALSE:
|
||||
expr_rewrite_const_bool(expr, type_bool, false);
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
case TYPE_PROPERTY_IS_SUBSTRUCT:
|
||||
expr_rewrite_const_bool(expr, type_bool, type_is_substruct(flat));
|
||||
return true;
|
||||
@@ -8211,6 +8209,179 @@ BoolErr sema_type_has_equality_overload(SemaContext *context, CanonicalType *typ
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
BoolErr sema_type_can_check_equality_with_overload(SemaContext *context, Type *type)
|
||||
{
|
||||
RETRY:
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_INFERRED_VECTOR:
|
||||
case TYPE_INFERRED_ARRAY:
|
||||
case TYPE_POISONED:
|
||||
UNREACHABLE
|
||||
case TYPE_VOID:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_MEMBER:
|
||||
case TYPE_UNTYPED_LIST:
|
||||
return false;
|
||||
case TYPE_UNION:
|
||||
case TYPE_STRUCT:
|
||||
if (type->decl->attr_compact && compiler.build.old_compact_eq) return true;
|
||||
return sema_type_has_equality_overload(context, type);
|
||||
case TYPE_BITSTRUCT:
|
||||
return true;
|
||||
case TYPE_TYPEDEF:
|
||||
type = type->canonical;
|
||||
goto RETRY;
|
||||
case TYPE_SLICE:
|
||||
case TYPE_ARRAY:
|
||||
// Arrays are comparable if elements are
|
||||
type = type->array.base;
|
||||
goto RETRY;
|
||||
case TYPE_DISTINCT:
|
||||
case TYPE_CONST_ENUM:
|
||||
if (sema_type_has_equality_overload(context, type)) return true;
|
||||
type = type_inline(type);
|
||||
goto RETRY;
|
||||
case TYPE_BOOL:
|
||||
case ALL_INTS:
|
||||
case ALL_FLOATS:
|
||||
case TYPE_ANY:
|
||||
case TYPE_INTERFACE:
|
||||
case TYPE_ANYFAULT:
|
||||
case TYPE_TYPEID:
|
||||
case TYPE_POINTER:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_FUNC_PTR:
|
||||
case TYPE_FUNC_RAW:
|
||||
case TYPE_TYPEINFO:
|
||||
case TYPE_VECTOR:
|
||||
case TYPE_WILDCARD:
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
INLINE Decl *ast_append_generated_local(Ast **ast_current_ref, Expr *init)
|
||||
{
|
||||
Ast *ast = ast_new(AST_DECLARE_STMT, init->span);
|
||||
Decl *var = decl_new_generated_var(init->type, VARDECL_LOCAL, init->span);
|
||||
assert(init->resolve_status == RESOLVE_DONE);
|
||||
var->var.init_expr = init;
|
||||
ast->declare_stmt = var;
|
||||
(*ast_current_ref)->next = astid(ast);
|
||||
*ast_current_ref = ast;
|
||||
return var;
|
||||
}
|
||||
|
||||
static inline bool sema_rewrite_expr_as_macro_block(SemaContext *context, Expr *expr, AstId start)
|
||||
{
|
||||
Type *old_expected_block = context->expected_block_type;
|
||||
BlockExit **old_exit_ref = context->block_exit_ref;
|
||||
context->expected_block_type = type_bool;
|
||||
BlockExit** block_exit_ref = CALLOCS(BlockExit*);
|
||||
context->block_exit_ref = block_exit_ref;
|
||||
bool success;
|
||||
Ast *compound_stmt = ast_new(AST_COMPOUND_STMT, expr->span);
|
||||
compound_stmt->compound_stmt.first_stmt = start;
|
||||
SCOPE_START_WITH_FLAGS(SCOPE_MACRO)
|
||||
success = sema_analyse_stmt_chain(context, compound_stmt);
|
||||
SCOPE_END;
|
||||
context->expected_block_type = old_expected_block;
|
||||
context->block_exit_ref = old_exit_ref;
|
||||
|
||||
if (!success) return false;
|
||||
expr->expr_kind = EXPR_MACRO_BLOCK;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
expr->type = type_bool;
|
||||
expr->macro_block = (ExprMacroBlock) {
|
||||
.first_stmt = astid(compound_stmt),
|
||||
.block_exit = block_exit_ref
|
||||
};
|
||||
return true;
|
||||
}
|
||||
static bool sema_rewrite_slice_comparison(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *max)
|
||||
{
|
||||
Ast dummy;
|
||||
Ast *current = &dummy;
|
||||
Decl *left_var = left->expr_kind == EXPR_IDENTIFIER ? left->ident_expr : ast_append_generated_local(¤t, left);
|
||||
Decl *right_var = right->expr_kind == EXPR_IDENTIFIER ? right->ident_expr : ast_append_generated_local(¤t, right);
|
||||
Decl *len_var_left = NULL;
|
||||
ArraySize len = 0;
|
||||
SourceSpan default_span = expr->span;
|
||||
if (max->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
len = max->array.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
Expr *len_left = expr_new(EXPR_SLICE_LEN, left->span);
|
||||
Expr *len_right = expr_new(EXPR_SLICE_LEN, right->span);
|
||||
len_left->inner_expr = expr_variable(left_var);
|
||||
len_right->inner_expr = expr_variable(right_var);
|
||||
len_left->type = type_usz;
|
||||
len_right->type = type_usz;
|
||||
if (!sema_analyse_expr(context, len_left)) return false;
|
||||
if (!sema_analyse_expr(context, len_right)) return false;
|
||||
len_var_left = ast_append_generated_local(¤t, len_left);
|
||||
Ast *ast_if = ast_new(AST_IF_STMT, default_span);
|
||||
Expr *expr_comparison = expr_new_binary(default_span, expr_variable(len_var_left), len_right, BINARYOP_NE);
|
||||
Ast *ast_then = ast_new(AST_RETURN_STMT, default_span);
|
||||
ast_then->return_stmt.expr = expr_new_const_bool(default_span, type_bool, false);
|
||||
ast_if->if_stmt = (AstIfStmt) {
|
||||
.cond = exprid(expr_new_cond(expr_comparison)),
|
||||
.then_body = astid(ast_then),
|
||||
};
|
||||
current->next = astid(ast_if);
|
||||
current = ast_if;
|
||||
}
|
||||
Decl *index = ast_append_generated_local(¤t, expr_new_const_int(default_span, type_usz, 0));
|
||||
Ast *ast = ast_new(AST_FOR_STMT, default_span);
|
||||
Expr *cond_expr;
|
||||
if (len > 0)
|
||||
{
|
||||
cond_expr = expr_new_binary(default_span, expr_variable(index), expr_new_const_int(default_span, type_usz, len), BINARYOP_LT);
|
||||
}
|
||||
else
|
||||
{
|
||||
cond_expr = expr_new_binary(default_span, expr_variable(index), expr_variable(len_var_left), BINARYOP_LT);
|
||||
}
|
||||
ast->for_stmt.cond = exprid(expr_new_cond(cond_expr));
|
||||
Expr *update = expr_new(EXPR_UNARY, default_span);
|
||||
update->unary_expr.expr = expr_variable(index);
|
||||
update->unary_expr.operator = UNARYOP_INC;
|
||||
update->unary_expr.no_wrap = true;
|
||||
ast->for_stmt.incr = exprid(update);
|
||||
|
||||
Expr *left_check = expr_new(EXPR_SUBSCRIPT, default_span);
|
||||
left_check->subscript_expr.expr = exprid(expr_variable(left_var));
|
||||
left_check->subscript_expr.index.expr = exprid(expr_variable(index));
|
||||
left_check->subscript_expr.no_check = true;
|
||||
Expr *right_check = expr_new(EXPR_SUBSCRIPT, default_span);
|
||||
right_check->subscript_expr.expr = exprid(expr_variable(right_var));
|
||||
right_check->subscript_expr.index.expr = exprid(expr_variable(index));
|
||||
right_check->subscript_expr.no_check = true;
|
||||
|
||||
Expr *expr_comparison = expr_new_binary(default_span, left_check, right_check, BINARYOP_NE);
|
||||
Ast *ast_then = ast_new(AST_RETURN_STMT, default_span);
|
||||
ast_then->return_stmt.expr = expr_new_const_bool(default_span, type_bool, false);
|
||||
Ast *ast_if = ast_new(AST_IF_STMT, default_span);
|
||||
ast_if->if_stmt = (AstIfStmt) {
|
||||
.cond = exprid(expr_new_cond(expr_comparison)),
|
||||
.then_body = astid(ast_then),
|
||||
};
|
||||
ast->for_stmt.body = astid(ast_if);
|
||||
|
||||
current->next = astid(ast);
|
||||
current = ast;
|
||||
Ast *ast_after = ast_new(AST_RETURN_STMT, default_span);
|
||||
ast_after->return_stmt.expr = expr_new_const_bool(default_span, type_bool, true);
|
||||
current->next = astid(ast_after);
|
||||
|
||||
return sema_rewrite_expr_as_macro_block(context, expr, dummy.next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a == b, a != b, a > b, a < b, a >= b, a <= b
|
||||
* @return
|
||||
@@ -8311,12 +8482,6 @@ NEXT:
|
||||
{
|
||||
RETURN_SEMA_ERROR(expr, "Both sides are untyped and cannot be compared. Please cast one or both sides to a type, e.g. (Foo){ 1, 2 } == { 1, 2 }.");
|
||||
}
|
||||
if (!type_is_comparable(max))
|
||||
{
|
||||
CHECK_ON_DEFINED(failed_ref);
|
||||
RETURN_SEMA_ERROR(expr, "%s does not support comparisons.",
|
||||
type_quoted_error_string(left->type));
|
||||
}
|
||||
|
||||
if (!is_equality_type_op)
|
||||
{
|
||||
@@ -8327,6 +8492,7 @@ NEXT:
|
||||
"cannot be ordered, did you make a mistake?",
|
||||
type_quoted_error_string(left->type));
|
||||
}
|
||||
|
||||
if (type_is_pointer_type(max))
|
||||
{
|
||||
|
||||
@@ -8344,6 +8510,26 @@ NEXT:
|
||||
bool success = cast_explicit_checkable(context, left, max, failed_ref) && cast_explicit_checkable(context, right, max, failed_ref);
|
||||
ASSERT_SPAN(expr, success);
|
||||
|
||||
if (!type_is_comparable(max))
|
||||
{
|
||||
if (is_equality_type_op && (max->type_kind == TYPE_SLICE || max->type_kind == TYPE_ARRAY))
|
||||
{
|
||||
Type *base = max->array.base;
|
||||
switch (sema_type_has_equality_overload(context, base))
|
||||
{
|
||||
case BOOL_ERR:
|
||||
return false;
|
||||
case BOOL_FALSE:
|
||||
break;
|
||||
case BOOL_TRUE:
|
||||
return sema_rewrite_slice_comparison(context, expr, left, right, max);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_ON_DEFINED(failed_ref);
|
||||
RETURN_SEMA_ERROR(expr, "%s does not support comparisons.",
|
||||
type_quoted_error_string(left->type));
|
||||
}
|
||||
|
||||
DONE:
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ bool sema_analyse_ct_expr(SemaContext *context, Expr *expr);
|
||||
Decl *sema_find_typed_operator(SemaContext *context, OperatorOverload operator_overload, SourceSpan span, Expr *lhs, Expr *rhs, bool *reverse);
|
||||
OverloadMatch sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overload_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref);
|
||||
BoolErr sema_type_has_equality_overload(SemaContext *context, Type *type);
|
||||
BoolErr sema_type_can_check_equality_with_overload(SemaContext *context, Type *type);
|
||||
Decl *sema_find_untyped_operator(Type *type, OperatorOverload operator_overload, Decl *skipped);
|
||||
bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *method_decl, Expr *parent, Expr **arguments, bool reverse_overload);
|
||||
bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr);
|
||||
@@ -136,6 +137,24 @@ bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *
|
||||
bool sema_analyse_const_enum_constant_val(SemaContext *context, Decl *decl);
|
||||
bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, bool *erase_decl);
|
||||
|
||||
INLINE bool sema_analyse_stmt_chain(SemaContext *context, Ast *statement)
|
||||
{
|
||||
if (!ast_ok(statement)) return false;
|
||||
AstId current = astid(statement);
|
||||
Ast *ast = NULL;
|
||||
bool all_ok = true;
|
||||
while (current)
|
||||
{
|
||||
ast = ast_next(¤t);
|
||||
if (!sema_analyse_statement(context, ast))
|
||||
{
|
||||
ast_poison(ast);
|
||||
all_ok = false;
|
||||
}
|
||||
}
|
||||
return all_ok;
|
||||
}
|
||||
|
||||
INLINE bool sema_analyse_func_macro(SemaContext *context, Decl *decl, AttributeDomain domain, bool *erase_decl)
|
||||
{
|
||||
assert((domain & CALLABLE_TYPE) == domain);
|
||||
|
||||
@@ -499,6 +499,7 @@ static inline bool sema_check_return_matches_opt_returns(SemaContext *context, E
|
||||
|
||||
static bool sema_analyse_macro_constant_ensures(SemaContext *context, Expr *ret_expr)
|
||||
{
|
||||
if (!context->current_macro) return true;
|
||||
ASSERT(context->current_macro);
|
||||
// This is a per return check, so we don't do it if the return expression is missing,
|
||||
// or if it is optional, or – obviously - if there are no '@ensure'.
|
||||
@@ -1823,11 +1824,8 @@ SKIP_OVERLOAD:;
|
||||
if (is_reverse)
|
||||
{
|
||||
// Create __idx$ > 0
|
||||
cond = expr_new(EXPR_BINARY, idx_decl->span);
|
||||
cond->binary_expr.operator = BINARYOP_GT;
|
||||
cond->binary_expr.left = exprid(expr_variable(idx_decl));
|
||||
Expr *rhs = expr_new_const_int(enumerator->span, index_type, 0);
|
||||
cond->binary_expr.right = exprid(rhs);
|
||||
cond = expr_new_binary(idx_decl->span, expr_variable(idx_decl), rhs, BINARYOP_GT);
|
||||
|
||||
// Create --__idx$
|
||||
Expr *dec = expr_new(EXPR_UNARY, idx_decl->span);
|
||||
|
||||
202
test/test_suite/expressions/overloaded_slice_comparison.c3t
Normal file
202
test/test_suite/expressions/overloaded_slice_comparison.c3t
Normal file
@@ -0,0 +1,202 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std;
|
||||
import std::core::env;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int a;
|
||||
}
|
||||
|
||||
fn bool Foo.eq(self, Foo f) @operator(==)
|
||||
{
|
||||
return self.a == f.a;
|
||||
}
|
||||
|
||||
fn Foo[2] get()
|
||||
{
|
||||
return { { 2 }, { 5 }};
|
||||
}
|
||||
fn void main()
|
||||
{
|
||||
Foo f = { 2 };
|
||||
Foo h = { 2 };
|
||||
Foo[] g = { f, h };
|
||||
Foo[] z = { f, f };
|
||||
bool test = g == z;
|
||||
Foo[2] g2 = { f, h };
|
||||
Foo[2] z2 = { f, f };
|
||||
bool test2 = g2 == z2;
|
||||
bool test3 = g2 == get();
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
define void @test.main() #0 {
|
||||
entry:
|
||||
%f = alloca %Foo, align 4
|
||||
%h = alloca %Foo, align 4
|
||||
%g = alloca %"Foo[]", align 8
|
||||
%literal = alloca [2 x %Foo], align 4
|
||||
%z = alloca %"Foo[]", align 8
|
||||
%literal1 = alloca [2 x %Foo], align 4
|
||||
%test = alloca i8, align 1
|
||||
%blockret = alloca i8, align 1
|
||||
%.anon = alloca i64, align 8
|
||||
%g2 = alloca [2 x %Foo], align 4
|
||||
%z2 = alloca [2 x %Foo], align 4
|
||||
%test2 = alloca i8, align 1
|
||||
%blockret9 = alloca i8, align 1
|
||||
%.anon10 = alloca i64, align 8
|
||||
%test3 = alloca i8, align 1
|
||||
%blockret21 = alloca i8, align 1
|
||||
%.anon22 = alloca [2 x %Foo], align 4
|
||||
%result = alloca [2 x %Foo], align 4
|
||||
%.anon23 = alloca i64, align 8
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %f, ptr align 4 @.__const.1, i32 4, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %h, ptr align 4 @.__const.2, i32 4, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %literal, ptr align 4 %f, i32 4, i1 false)
|
||||
%ptradd = getelementptr inbounds i8, ptr %literal, i64 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %ptradd, ptr align 4 %h, i32 4, i1 false)
|
||||
%0 = insertvalue %"Foo[]" undef, ptr %literal, 0
|
||||
%1 = insertvalue %"Foo[]" %0, i64 2, 1
|
||||
store %"Foo[]" %1, ptr %g, align 8
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %literal1, ptr align 4 %f, i32 4, i1 false)
|
||||
%ptradd2 = getelementptr inbounds i8, ptr %literal1, i64 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %ptradd2, ptr align 4 %f, i32 4, i1 false)
|
||||
%2 = insertvalue %"Foo[]" undef, ptr %literal1, 0
|
||||
%3 = insertvalue %"Foo[]" %2, i64 2, 1
|
||||
store %"Foo[]" %3, ptr %z, align 8
|
||||
%ptradd3 = getelementptr inbounds i8, ptr %g, i64 8
|
||||
%4 = load i64, ptr %ptradd3, align 8
|
||||
%ptradd4 = getelementptr inbounds i8, ptr %z, i64 8
|
||||
%5 = load i64, ptr %ptradd4, align 8
|
||||
%neq = icmp ne i64 %4, %5
|
||||
br i1 %neq, label %if.then, label %if.exit
|
||||
|
||||
if.then: ; preds = %entry
|
||||
store i8 0, ptr %blockret, align 1
|
||||
br label %expr_block.exit
|
||||
|
||||
if.exit: ; preds = %entry
|
||||
store i64 0, ptr %.anon, align 8
|
||||
br label %loop.cond
|
||||
|
||||
loop.cond: ; preds = %if.exit6, %if.exit
|
||||
%6 = load i64, ptr %.anon, align 8
|
||||
%lt = icmp ult i64 %6, %4
|
||||
br i1 %lt, label %loop.body, label %loop.exit
|
||||
|
||||
loop.body: ; preds = %loop.cond
|
||||
%7 = load ptr, ptr %g, align 8
|
||||
%8 = load i64, ptr %.anon, align 8
|
||||
%ptroffset = getelementptr inbounds [4 x i8], ptr %7, i64 %8
|
||||
%9 = load ptr, ptr %z, align 8
|
||||
%10 = load i64, ptr %.anon, align 8
|
||||
%ptroffset5 = getelementptr inbounds [4 x i8], ptr %9, i64 %10
|
||||
%11 = load i32, ptr %ptroffset, align 4
|
||||
%12 = load i32, ptr %ptroffset5, align 4
|
||||
%13 = call i8 @test.Foo.eq(i32 %11, i32 %12)
|
||||
%14 = trunc i8 %13 to i1
|
||||
br i1 %14, label %if.exit6, label %if.else
|
||||
|
||||
if.else: ; preds = %loop.body
|
||||
store i8 0, ptr %blockret, align 1
|
||||
br label %expr_block.exit
|
||||
|
||||
if.exit6: ; preds = %loop.body
|
||||
%15 = load i64, ptr %.anon, align 8
|
||||
%addnuw = add nuw i64 %15, 1
|
||||
store i64 %addnuw, ptr %.anon, align 8
|
||||
br label %loop.cond
|
||||
|
||||
loop.exit: ; preds = %loop.cond
|
||||
store i8 1, ptr %blockret, align 1
|
||||
br label %expr_block.exit
|
||||
|
||||
expr_block.exit: ; preds = %loop.exit, %if.else, %if.then
|
||||
%16 = load i8, ptr %blockret, align 1
|
||||
store i8 %16, ptr %test, align 1
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %g2, ptr align 4 %f, i32 4, i1 false)
|
||||
%ptradd7 = getelementptr inbounds i8, ptr %g2, i64 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %ptradd7, ptr align 4 %h, i32 4, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %z2, ptr align 4 %f, i32 4, i1 false)
|
||||
%ptradd8 = getelementptr inbounds i8, ptr %z2, i64 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %ptradd8, ptr align 4 %f, i32 4, i1 false)
|
||||
store i64 0, ptr %.anon10, align 8
|
||||
br label %loop.cond11
|
||||
|
||||
loop.cond11: ; preds = %if.exit17, %expr_block.exit
|
||||
%17 = load i64, ptr %.anon10, align 8
|
||||
%lt12 = icmp ult i64 %17, 2
|
||||
br i1 %lt12, label %loop.body13, label %loop.exit19
|
||||
|
||||
loop.body13: ; preds = %loop.cond11
|
||||
%18 = load i64, ptr %.anon10, align 8
|
||||
%ptroffset14 = getelementptr inbounds [4 x i8], ptr %g2, i64 %18
|
||||
%19 = load i64, ptr %.anon10, align 8
|
||||
%ptroffset15 = getelementptr inbounds [4 x i8], ptr %z2, i64 %19
|
||||
%20 = load i32, ptr %ptroffset14, align 4
|
||||
%21 = load i32, ptr %ptroffset15, align 4
|
||||
%22 = call i8 @test.Foo.eq(i32 %20, i32 %21)
|
||||
%23 = trunc i8 %22 to i1
|
||||
br i1 %23, label %if.exit17, label %if.else16
|
||||
|
||||
if.else16: ; preds = %loop.body13
|
||||
store i8 0, ptr %blockret9, align 1
|
||||
br label %expr_block.exit20
|
||||
|
||||
if.exit17: ; preds = %loop.body13
|
||||
%24 = load i64, ptr %.anon10, align 8
|
||||
%addnuw18 = add nuw i64 %24, 1
|
||||
store i64 %addnuw18, ptr %.anon10, align 8
|
||||
br label %loop.cond11
|
||||
|
||||
loop.exit19: ; preds = %loop.cond11
|
||||
store i8 1, ptr %blockret9, align 1
|
||||
br label %expr_block.exit20
|
||||
|
||||
expr_block.exit20: ; preds = %loop.exit19, %if.else16
|
||||
%25 = load i8, ptr %blockret9, align 1
|
||||
store i8 %25, ptr %test2, align 1
|
||||
%26 = call i64 @test.get()
|
||||
store i64 %26, ptr %result, align 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %.anon22, ptr align 4 %result, i32 8, i1 false)
|
||||
store i64 0, ptr %.anon23, align 8
|
||||
br label %loop.cond24
|
||||
|
||||
loop.cond24: ; preds = %if.exit30, %expr_block.exit20
|
||||
%27 = load i64, ptr %.anon23, align 8
|
||||
%lt25 = icmp ult i64 %27, 2
|
||||
br i1 %lt25, label %loop.body26, label %loop.exit32
|
||||
|
||||
loop.body26: ; preds = %loop.cond24
|
||||
%28 = load i64, ptr %.anon23, align 8
|
||||
%ptroffset27 = getelementptr inbounds [4 x i8], ptr %g2, i64 %28
|
||||
%29 = load i64, ptr %.anon23, align 8
|
||||
%ptroffset28 = getelementptr inbounds [4 x i8], ptr %.anon22, i64 %29
|
||||
%30 = load i32, ptr %ptroffset27, align 4
|
||||
%31 = load i32, ptr %ptroffset28, align 4
|
||||
%32 = call i8 @test.Foo.eq(i32 %30, i32 %31)
|
||||
%33 = trunc i8 %32 to i1
|
||||
br i1 %33, label %if.exit30, label %if.else29
|
||||
|
||||
if.else29: ; preds = %loop.body26
|
||||
store i8 0, ptr %blockret21, align 1
|
||||
br label %expr_block.exit33
|
||||
|
||||
if.exit30: ; preds = %loop.body26
|
||||
%34 = load i64, ptr %.anon23, align 8
|
||||
%addnuw31 = add nuw i64 %34, 1
|
||||
store i64 %addnuw31, ptr %.anon23, align 8
|
||||
br label %loop.cond24
|
||||
|
||||
loop.exit32: ; preds = %loop.cond24
|
||||
store i8 1, ptr %blockret21, align 1
|
||||
br label %expr_block.exit33
|
||||
|
||||
expr_block.exit33: ; preds = %loop.exit32, %if.else29
|
||||
%35 = load i8, ptr %blockret21, align 1
|
||||
store i8 %35, ptr %test3, align 1
|
||||
ret void
|
||||
}
|
||||
Reference in New Issue
Block a user