mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compile time array assign ops, e.g. $c[1] += 3 #1890.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
- Warn on if-catch with just a `default` case.
|
||||
- Compile time array inc/dec.
|
||||
- Improve error message when using ',' in struct declarations. #1920
|
||||
- Compile time array assign ops, e.g. `$c[1] += 3` #1890.
|
||||
|
||||
### Fixes
|
||||
- Fix issue requiring prefix on a generic interface declaration.
|
||||
|
||||
@@ -2265,6 +2265,7 @@ bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *
|
||||
|
||||
void expr_rewrite_to_binary(Expr *expr_to_rewrite, Expr *left, Expr *right, BinaryOp op);
|
||||
|
||||
Expr *expr_from_const_expr_at_index(Expr *expr, ArrayIndex index);
|
||||
bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to);
|
||||
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op);
|
||||
void expr_contract_array(ExprConst *expr_const, ConstKind contract_type);
|
||||
|
||||
@@ -696,6 +696,45 @@ void expr_rewrite_to_const_zero(Expr *expr, Type *type)
|
||||
expr->type = type;
|
||||
}
|
||||
|
||||
Expr *expr_from_const_expr_at_index(Expr *expr, ArrayIndex index)
|
||||
{
|
||||
ExprConst *expr_const = &expr->const_expr;
|
||||
switch (expr_const->const_kind)
|
||||
{
|
||||
case CONST_ERR:
|
||||
case CONST_FLOAT:
|
||||
case CONST_INTEGER:
|
||||
case CONST_BOOL:
|
||||
case CONST_ENUM:
|
||||
case CONST_POINTER:
|
||||
case CONST_TYPEID:
|
||||
case CONST_REF:
|
||||
case CONST_MEMBER:
|
||||
UNREACHABLE
|
||||
case CONST_BYTES:
|
||||
case CONST_STRING:
|
||||
{
|
||||
uint8_t c = expr_const->bytes.ptr[index];
|
||||
Type *indexed = type_get_indexed_type(expr->type);
|
||||
return expr_new_const_int(expr->span, indexed, c);
|
||||
}
|
||||
case CONST_UNTYPED_LIST:
|
||||
return copy_expr_single(expr_const->untyped_list[index]);
|
||||
case CONST_SLICE:
|
||||
{
|
||||
Expr *val = expr_new_expr(EXPR_CONST, expr);
|
||||
if (!expr_rewrite_to_const_initializer_index(expr->type, expr_const->slice_init, val, index, false)) return poisoned_expr;
|
||||
return val;
|
||||
}
|
||||
case CONST_INITIALIZER:
|
||||
{
|
||||
Expr *val = expr_new_expr(EXPR_CONST, expr);
|
||||
if (!expr_rewrite_to_const_initializer_index(expr->type, expr_const->initializer, val, index, false)) return poisoned_expr;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index, bool from_back)
|
||||
{
|
||||
ConstInitializer *initializer = initializer_for_index(list, index, from_back);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
// implicit conversions. C3 has a fairly complex set of rules,
|
||||
// which makes this code somewhat lengthy.
|
||||
|
||||
#include <tgmath.h>
|
||||
|
||||
#include "sema_internal.h"
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -135,7 +135,7 @@ static void expr_binary_unify_failability(Expr *expr, Expr *left, Expr *right);
|
||||
static inline bool sema_binary_analyse_subexpr(SemaContext *context, Expr *binary, Expr *left, Expr *right);
|
||||
static inline bool sema_binary_analyse_arithmetic_subexpr(SemaContext *context, Expr *expr, const char *error, bool bool_and_bitstruct_is_allowed);
|
||||
static bool sema_binary_check_unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right_side);
|
||||
static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *expr, Expr *left);
|
||||
static bool sema_binary_analyse_ct_op_assign(SemaContext *context, Expr *expr, Expr *left);
|
||||
static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type,
|
||||
Expr *parent, const char *error_message, bool allow_bool_vec);
|
||||
static bool sema_binary_is_unsigned_always_same_comparison(SemaContext *context, Expr *expr, Expr *left, Expr *right,
|
||||
@@ -5836,7 +5836,7 @@ static bool sema_expr_analyse_ct_subscript_rhs(SemaContext *context, Decl *ct_va
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_expr_analyse_ct_subscript_set_value(SemaContext *context, Expr *expr, Expr *left, Decl *ct_var, Expr *right)
|
||||
static bool sema_expr_analyse_ct_subscript_set_value(SemaContext *context, Expr *left, Decl *ct_var, Expr *right)
|
||||
{
|
||||
if (context->call_env.in_other)
|
||||
{
|
||||
@@ -5847,7 +5847,6 @@ static bool sema_expr_analyse_ct_subscript_set_value(SemaContext *context, Expr
|
||||
ASSERT_SPAN(original_value, original_value->expr_kind == EXPR_CONST);
|
||||
ExprConst *expr_const = &original_value->const_expr;
|
||||
|
||||
|
||||
switch (expr_const->const_kind)
|
||||
{
|
||||
case CONST_FLOAT:
|
||||
@@ -5880,8 +5879,6 @@ static bool sema_expr_analyse_ct_subscript_set_value(SemaContext *context, Expr
|
||||
expr_const->untyped_list[index] = right;
|
||||
break;
|
||||
}
|
||||
Expr *original = expr_copy(original_value);
|
||||
expr_replace(expr, original);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5889,7 +5886,9 @@ static bool sema_expr_analyse_ct_subscript_assign(SemaContext *context, Expr *ex
|
||||
{
|
||||
Decl *ct_var = left->ct_subscript_expr.var;
|
||||
if (!sema_expr_analyse_ct_subscript_rhs(context, ct_var, right)) return false;
|
||||
return sema_expr_analyse_ct_subscript_set_value(context, expr, left, ct_var, right);
|
||||
if (!sema_expr_analyse_ct_subscript_set_value(context, left, ct_var, right)) return false;
|
||||
expr_replace(expr, right);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right)
|
||||
@@ -5996,7 +5995,7 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef
|
||||
*
|
||||
* @return true if analysis worked.
|
||||
*/
|
||||
static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *expr, Expr *left)
|
||||
static bool sema_binary_analyse_ct_op_assign(SemaContext *context, Expr *expr, Expr *left)
|
||||
{
|
||||
ASSERT_SPAN(left, left->expr_kind == EXPR_CT_IDENT);
|
||||
|
||||
@@ -6027,10 +6026,26 @@ static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *exp
|
||||
*
|
||||
* @return true if analysis worked.
|
||||
*/
|
||||
static bool sema_binary_analyse_ct_subscript_assign(SemaContext *context, Expr *expr, Expr *left)
|
||||
static bool sema_binary_analyse_ct_subscript_op_assign(SemaContext *context, Expr *expr, Expr *left)
|
||||
{
|
||||
TODO
|
||||
ASSERT_SPAN(left, left->expr_kind == EXPR_CT_SUBSCRIPT);
|
||||
if (context->call_env.in_other)
|
||||
{
|
||||
RETURN_SEMA_ERROR(left, "Compile time variables may only be modified in the scope they are defined in.");
|
||||
}
|
||||
|
||||
Decl *left_var = left->ct_subscript_expr.var;
|
||||
ArrayIndex idx = left->ct_subscript_expr.index;
|
||||
Expr *lhs_expr = left_var->var.init_expr;
|
||||
ASSERT_SPAN(lhs_expr, lhs_expr->expr_kind == EXPR_CONST);
|
||||
Expr *value = expr_from_const_expr_at_index(left_var->var.init_expr, idx);
|
||||
|
||||
BinaryOp op = binaryop_assign_base_op(expr->binary_expr.operator);
|
||||
expr->binary_expr = (ExprBinary) { .left = exprid(value), .right = expr->binary_expr.right, .operator = op };
|
||||
if (!sema_expr_analyse_binary(context, expr, NULL)) return false;
|
||||
if (!sema_expr_analyse_ct_subscript_set_value(context, left, left_var, expr)) return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6077,9 +6092,9 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *
|
||||
switch (left->expr_kind)
|
||||
{
|
||||
case EXPR_CT_IDENT:
|
||||
return sema_binary_analyse_ct_common_assign(context, expr, left);
|
||||
return sema_binary_analyse_ct_op_assign(context, expr, left);
|
||||
case EXPR_CT_SUBSCRIPT:
|
||||
return sema_binary_analyse_ct_subscript_assign(context, expr, left);
|
||||
return sema_binary_analyse_ct_subscript_op_assign(context, expr, left);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -7580,66 +7595,19 @@ static inline bool sema_expr_analyse_not(SemaContext *context, Expr *expr)
|
||||
|
||||
static inline bool sema_expr_analyse_ct_subscript_incdec(SemaContext *context, Expr *expr, Expr *inner)
|
||||
{
|
||||
Expr *init = inner->ct_subscript_expr.var->var.init_expr;
|
||||
Decl *ct_var = inner->ct_subscript_expr.var;
|
||||
Expr *init = ct_var->var.init_expr;
|
||||
ArrayIndex index = inner->ct_subscript_expr.index;
|
||||
ExprConst *expr_const = &init->const_expr;
|
||||
bool post = expr->expr_kind == EXPR_POST_UNARY;
|
||||
bool dec = expr->unary_expr.operator == UNARYOP_DEC;
|
||||
Expr *value;
|
||||
Expr temp;
|
||||
switch (expr_const->const_kind)
|
||||
{
|
||||
case CONST_ERR:
|
||||
case CONST_FLOAT:
|
||||
case CONST_INTEGER:
|
||||
case CONST_BOOL:
|
||||
case CONST_ENUM:
|
||||
case CONST_POINTER:
|
||||
case CONST_TYPEID:
|
||||
case CONST_REF:
|
||||
case CONST_MEMBER:
|
||||
UNREACHABLE
|
||||
case CONST_BYTES:
|
||||
case CONST_STRING:
|
||||
{
|
||||
uint8_t c = expr_const->bytes.ptr[index];
|
||||
unsigned char *copy = MALLOC(expr_const->bytes.len + 1);
|
||||
memcpy(copy, expr_const->bytes.ptr, expr_const->bytes.len + 1);
|
||||
Type *indexed = type_get_indexed_type(init->type);
|
||||
if (post) expr_rewrite_const_int(expr, indexed, c);
|
||||
if (dec)
|
||||
{
|
||||
c--;
|
||||
}
|
||||
else
|
||||
{
|
||||
c++;
|
||||
}
|
||||
copy[index] = c;
|
||||
expr_const->bytes.ptr = (char *)copy;
|
||||
if (!post) expr_rewrite_const_int(expr, indexed, c);
|
||||
return true;
|
||||
}
|
||||
case CONST_UNTYPED_LIST:
|
||||
value = expr_const->untyped_list[index];
|
||||
break;
|
||||
case CONST_SLICE:
|
||||
value = &temp;
|
||||
if (!expr_rewrite_to_const_initializer_index(init->type, expr_const->slice_init, value, index, false)) return false;
|
||||
break;
|
||||
case CONST_INITIALIZER:
|
||||
value = &temp;
|
||||
if (!expr_rewrite_to_const_initializer_index(init->type, expr_const->initializer, value, index, false)) return false;
|
||||
break;
|
||||
}
|
||||
ASSIGN_EXPR_OR_RET(Expr *value, expr_from_const_expr_at_index(init, index), false);
|
||||
if (!expr_is_const_int(value))
|
||||
{
|
||||
RETURN_SEMA_ERROR(expr, "The indexed type is not an integer.");
|
||||
}
|
||||
value = expr_copy(value);
|
||||
if (post)
|
||||
{
|
||||
expr_replace(expr, value);
|
||||
expr_replace(expr, copy_expr_single(value));
|
||||
}
|
||||
if (dec)
|
||||
{
|
||||
@@ -7653,21 +7621,7 @@ static inline bool sema_expr_analyse_ct_subscript_incdec(SemaContext *context, E
|
||||
{
|
||||
expr_replace(expr, value);
|
||||
}
|
||||
switch (expr_const->const_kind)
|
||||
{
|
||||
case CONST_UNTYPED_LIST:
|
||||
expr_const->untyped_list[index] = value;
|
||||
break;
|
||||
case CONST_SLICE:
|
||||
const_init_rewrite_array_at(expr_const->slice_init, value, index);
|
||||
break;
|
||||
case CONST_INITIALIZER:
|
||||
const_init_rewrite_array_at(expr_const->initializer, value, index);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
return true;
|
||||
return sema_expr_analyse_ct_subscript_set_value(context, inner, ct_var, value);
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr, Expr *inner)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
27
test/test_suite/compile_time/ct_subscript_op.c3t
Normal file
27
test/test_suite/compile_time/ct_subscript_op.c3t
Normal file
@@ -0,0 +1,27 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
fn void main()
|
||||
{
|
||||
var $x2 = { 1, 2, 4 };
|
||||
int y = $x2[2] += 1;
|
||||
int[3] gh = $x2;
|
||||
$x2[1] <<= 4;
|
||||
int[3] kl = $x2;
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
@.__const = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 5], align 4
|
||||
@.__const.1 = private unnamed_addr constant [3 x i32] [i32 1, i32 32, i32 5], align 4
|
||||
|
||||
; Function Attrs: nounwind uwtable
|
||||
define void @test.main() #0 {
|
||||
entry:
|
||||
%y = alloca i32, align 4
|
||||
%gh = alloca [3 x i32], align 4
|
||||
%kl = alloca [3 x i32], align 4
|
||||
store i32 5, ptr %y, align 4
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %gh, ptr align 4 @.__const, i32 12, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %kl, ptr align 4 @.__const.1, i32 12, i1 false)
|
||||
ret void
|
||||
}
|
||||
Reference in New Issue
Block a user