Add support for : slices. Version bumped to 0.2.16

This commit is contained in:
Christoffer Lerno
2022-07-16 10:28:29 +02:00
committed by Christoffer Lerno
parent 48a31cfa48
commit 4beb7eff8f
12 changed files with 528 additions and 62 deletions

View File

@@ -708,6 +708,7 @@ typedef struct
{
bool start_from_back : 1;
bool end_from_back : 1;
bool is_lenrange : 1;
ExprId expr;
ExprId start;
ExprId end;

View File

@@ -2265,7 +2265,7 @@ static void llvm_emit_trap_invalid_shift(GenContext *c, LLVMValueRef value, Type
}
}
static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_ref, BEValue *start_ref, BEValue *end_ref)
static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_ref, BEValue *start_ref, BEValue *end_ref, bool *is_exclusive)
{
assert(slice->expr_kind == EXPR_SLICE);
@@ -2351,7 +2351,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r
Type *end_type;
BEValue end_index;
bool is_len_range = *is_exclusive = slice->slice_expr.is_lenrange;
if (end)
{
// Get the index.
@@ -2365,9 +2365,13 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r
end_index.value = llvm_emit_sub_int(c, end_type, len.value, end_index.value, slice->span);
llvm_value_rvalue(c, &end_index);
}
if (is_len_range)
{
end_index.value = llvm_emit_add_int(c, end_type, start_index.value, end_index.value, slice->span);
}
// This will trap any bad negative index, so we're fine.
if (active_target.feature.safe_mode)
if (active_target.feature.safe_mode && !is_len_range)
{
BEValue excess;
llvm_emit_int_comparison(c, &excess, &start_index, &end_index, BINARYOP_GT);
@@ -2375,7 +2379,6 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r
if (len.value)
{
llvm_emit_int_comparison(c, &excess, &len, &end_index, BINARYOP_LT);
llvm_emit_panic_if_true(c, &excess, "Size exceeds index", slice->span);
}
@@ -2384,9 +2387,10 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r
else
{
assert(len.value && "Pointer should never end up here.");
// Otherwise everything is fine and dandy. Our len - 1 is our end index.
end_index.value = LLVMBuildSub(c->builder, len.value, LLVMConstInt(LLVMTypeOf(len.value), 1, false), "");
end_index.value = len.value;
end_type = type_usize;
// Use "len-range" when implicit, this avoids len - 1 here.
*is_exclusive = true;
}
llvm_value_set(end_ref, end_index.value, end_type);
@@ -2400,13 +2404,22 @@ static void gencontext_emit_slice(GenContext *c, BEValue *be_value, Expr *expr)
BEValue parent;
BEValue start;
BEValue end;
llvm_emit_slice_values(c, expr, &parent, &start, &end);
bool is_exclusive;
llvm_emit_slice_values(c, expr, &parent, &start, &end, &is_exclusive);
llvm_value_rvalue(c, &start);
llvm_value_rvalue(c, &end);
// Calculate the size
LLVMValueRef size = LLVMBuildSub(c->builder, LLVMBuildAdd(c->builder, end.value, llvm_const_int(c, start.type, 1), ""), start.value, "size");
LLVMValueRef size;
if (is_exclusive)
{
size = LLVMBuildSub(c->builder, end.value, start.value, "size");
}
else
{
size = LLVMBuildSub(c->builder, LLVMBuildAdd(c->builder, end.value, llvm_const_int(c, start.type, 1), ""), start.value, "size");
}
LLVMValueRef start_pointer;
Type *type = type_lowering(parent.type);
switch (type->type_kind)
@@ -2452,7 +2465,8 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr)
BEValue start;
BEValue end;
// Use general function to get all the values we need (a lot!)
llvm_emit_slice_values(c, exprptr(expr->slice_assign_expr.left), &parent, &start, &end);
bool is_exclusive;
llvm_emit_slice_values(c, exprptr(expr->slice_assign_expr.left), &parent, &start, &end, &is_exclusive);
llvm_value_rvalue(c, &start);
llvm_value_rvalue(c, &end);
@@ -2468,6 +2482,11 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr)
assert(start_val <= INT64_MAX);
assert(end_val <= INT64_MAX);
if (start_val > end_val) return;
if (is_exclusive)
{
if (start_val == end_val) return;
end_val--;
}
if (end_val - start_val < SLICE_MAX_UNROLL)
{
BEValue addr;
@@ -2503,7 +2522,8 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr)
// Check if we're not at the end.
BEValue value;
llvm_emit_int_comp(c, &value, start.type, end.type, offset, end.value, BINARYOP_LE);
BinaryOp op = is_exclusive ? BINARYOP_LT : BINARYOP_LE;
llvm_emit_int_comp(c, &value, start.type, end.type, offset, end.value, op);
// If jump to the assign block if we're not at the end index.
EMIT_LOC(c, expr);

View File

@@ -827,14 +827,16 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, As
llvm_value_rvalue(c, &be_value);
case_value = be_value.value;
LLVMAddCase(switch_stmt, case_value, block);
Expr *to = case_stmt->case_stmt.to_expr;
if (to)
Expr *to_expr =case_stmt->case_stmt.to_expr;
if (to_expr)
{
BEValue to_value;
llvm_emit_expr(c, &to_value, case_stmt->case_stmt.to_expr);
assert(LLVMIsAConstant(to_value.value));
llvm_emit_expr(c, &to_value, to_expr);
llvm_value_rvalue(c, &to_value);
LLVMValueRef to = to_value.value;
assert(LLVMIsAConstant(to));
LLVMValueRef one = llvm_const_int(c, to_value.type, 1);
while (LLVMConstIntGetZExtValue(LLVMConstICmp(LLVMIntEQ, to_value.value, case_value)) != 1)
while (LLVMConstIntGetZExtValue(LLVMConstICmp(LLVMIntEQ, to, case_value)) != 1)
{
case_value = LLVMConstAdd(case_value, one);
LLVMAddCase(switch_stmt, case_value, block);

View File

@@ -752,7 +752,7 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left)
Expr *end = NULL;
// Not range with missing entry
if (!tok_is(c, TOKEN_DOTDOT))
if (!tok_is(c, TOKEN_DOTDOT) && !tok_is(c, TOKEN_COLON))
{
// Might be ^ prefix
from_back = try_consume(c, TOKEN_BIT_XOR);
@@ -765,7 +765,8 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left)
index->resolve_status = RESOLVE_DONE;
expr_const_set_int(&index->const_expr, 0, type_uint->type_kind);
}
if (try_consume(c, TOKEN_DOTDOT))
bool is_len_range = try_consume(c, TOKEN_COLON);
if (is_len_range || try_consume(c, TOKEN_DOTDOT))
{
is_range = true;
if (!tok_is(c, TOKEN_RBRACKET))
@@ -785,6 +786,7 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left)
subs_expr->slice_expr.start_from_back = from_back;
subs_expr->slice_expr.end = end ? exprid(end) : 0;
subs_expr->slice_expr.end_from_back = end_from_back;
subs_expr->slice_expr.is_lenrange = is_len_range;
}
else
{

View File

@@ -296,9 +296,21 @@ static inline Ast* parse_switch_stmt(ParseContext *c)
Ast *switch_ast = new_ast(AST_SWITCH_STMT, c->span);
advance_and_verify(c, TOKEN_SWITCH);
ASSIGN_DECL_OR_RET(switch_ast->switch_stmt.flow.label, parse_optional_label(c, switch_ast), poisoned_ast);
CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast);
ASSIGN_EXPRID_OR_RET(switch_ast->switch_stmt.cond, parse_cond(c), poisoned_ast);
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast);
if (!try_consume(c, TOKEN_LPAREN))
{
Expr *cond = expr_new(EXPR_COND, switch_ast->span);
Expr *expr = expr_new(EXPR_CONST, switch_ast->span);
expr_const_set_bool(&expr->const_expr, true);
expr->resolve_status = RESOLVE_DONE;
expr->type = type_bool;
vec_add(cond->cond_expr, expr);
switch_ast->switch_stmt.cond = exprid(cond);
}
else
{
ASSIGN_EXPRID_OR_RET(switch_ast->switch_stmt.cond, parse_cond(c), poisoned_ast);
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast);
}
if (!parse_switch_body(c, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast;
return switch_ast;

View File

@@ -2479,6 +2479,64 @@ static void sema_deref_array_pointers(Expr *expr)
}
}
static bool expr_check_len_in_range(SemaContext *context, Type *type, Expr *len_expr, bool from_end, bool *remove_from_end)
{
assert(type == type->canonical);
if (len_expr->expr_kind != EXPR_CONST) return true;
Int const_len = len_expr->const_expr.ixx;
if (!int_fits(const_len, TYPE_I64))
{
SEMA_ERROR(len_expr, "The length cannot be stored in a 64-signed integer, which isn't supported.");
return false;
}
if (int_is_neg(const_len))
{
SEMA_ERROR(len_expr, "The length may not be negative.");
return false;
}
MemberIndex len_val = (MemberIndex)const_len.i.low;
switch (type->type_kind)
{
case TYPE_POINTER:
case TYPE_FLEXIBLE_ARRAY:
assert(!from_end);
FALLTHROUGH;
case TYPE_SUBARRAY:
return true;
case TYPE_ARRAY:
case TYPE_VECTOR:
{
MemberIndex len = (MemberIndex)type->array.len;
bool is_vector = type->type_kind == TYPE_VECTOR;
if (from_end)
{
if (len_val > len)
{
SEMA_ERROR(len_expr, "This would result in a negative length.");
return false;
}
len_expr->const_expr.ixx.i.low = len - len_val;
*remove_from_end = true;
return true;
}
// Checking end can only be done for arrays and vectors.
if (len_val > len)
{
SEMA_ERROR(len_expr,
is_vector ? "Length out of bounds, was %lld, exceeding vector length %lld."
: "Array length out of bounds, was %lld, exceeding array length %lld.",
(long long)len_val, (long long)len);
return false;
}
return true;
}
default:
UNREACHABLE
}
UNREACHABLE
}
static bool expr_check_index_in_range(SemaContext *context, Type *type, Expr *index_expr, bool end_index, bool from_end, bool *remove_from_end)
{
assert(type == type->canonical);
@@ -2799,6 +2857,11 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr)
// Fix index sizes
if (!expr_cast_to_index(start)) return false;
if (end && !expr_cast_to_index(end)) return false;
if (end && end->type != start->type)
{
Type *common = type_find_max_type(start->type, end->type);
if (!common || !cast_implicit(start, common) || !cast_implicit(end, common)) return false;
}
// Check range
if (type->type_kind == TYPE_POINTER)
@@ -2819,30 +2882,27 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr)
return false;
}
}
bool is_lenrange = expr->slice_expr.is_lenrange;
bool remove_from_end = false;
if (!expr_check_index_in_range(context, type, start, false, expr->slice_expr.start_from_back, &remove_from_end)) return false;
if (remove_from_end) expr->slice_expr.start_from_back = false;
remove_from_end = false;
if (end && !expr_check_index_in_range(context, type, end, true, expr->slice_expr.end_from_back, &remove_from_end)) return false;
if (end)
{
if (is_lenrange)
{
if (!expr_check_len_in_range(context, type, end, expr->slice_expr.end_from_back, &remove_from_end)) return false;
}
else
{
if (!expr_check_index_in_range(context, type, end, true, expr->slice_expr.end_from_back, &remove_from_end)) return false;
}
}
if (remove_from_end) expr->slice_expr.end_from_back = false;
if (start && end && start->expr_kind == EXPR_CONST && end->expr_kind == EXPR_CONST)
{
if (type->type_kind == TYPE_ARRAY)
{
Int128 len = { 0, type->array.len };
if (expr->slice_expr.start_from_back)
{
start->const_expr.ixx.i = i128_sub(len, start->const_expr.ixx.i);
expr->slice_expr.start_from_back = false;
}
if (expr->slice_expr.end_from_back)
{
end->const_expr.ixx.i = i128_sub(len, end->const_expr.ixx.i);
expr->slice_expr.end_from_back = false;
}
}
if (expr->slice_expr.start_from_back && expr->slice_expr.end_from_back)
if (!is_lenrange && expr->slice_expr.start_from_back && expr->slice_expr.end_from_back)
{
if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_LT))
{
@@ -2850,7 +2910,7 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr)
return false;
}
}
else if (!expr->slice_expr.start_from_back && !expr->slice_expr.end_from_back)
else if (!is_lenrange && !expr->slice_expr.start_from_back && !expr->slice_expr.end_from_back)
{
if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_GT))
{
@@ -2858,6 +2918,17 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr)
return false;
}
}
// If both are
if (type->type_kind == TYPE_ARRAY || type->type_kind == TYPE_VECTOR)
{
assert(!expr->slice_expr.start_from_back);
assert(!expr->slice_expr.end_from_back);
if (!is_lenrange)
{
end->const_expr.ixx = int_sub(int_add64(end->const_expr.ixx, 1), start->const_expr.ixx);
is_lenrange = expr->slice_expr.is_lenrange = true;
}
}
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.2.15"
#define COMPILER_VERSION "0.2.16"