diff --git a/releasenotes.md b/releasenotes.md index 9471eb596..9dc7dc277 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -22,6 +22,7 @@ - Fix to void expression blocks - Temporary objects may now invoke methods using ref parameters. - Delete object files after successful linking. +- Compile time subscript of constant strings and bytes. - `@if` introduced, other top level conditional compilation removed. - `@dynamic` and `@interface` for dynamic dispatch. - `$if` now uses `$if :` syntax. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index aa96b6809..721958bd0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2214,7 +2214,7 @@ INLINE void expr_rewrite_const_untyped_list(Expr *expr, Expr **elements); void expr_rewrite_to_builtin_access(Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type); void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string); void expr_rewrite_to_const_zero(Expr *expr, Type *type); -bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index); +bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index, bool from_back); void expr_rewrite_to_variable(Expr *expr, Decl *decl); void expr_rewrite_to_binary(Expr *expr_to_rewrite, Expr *left, Expr *right, BinaryOp op); diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 3ec557349..53a828514 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -8,7 +8,7 @@ static inline bool expr_binary_is_constant_eval(Expr *expr, ConstantEvalKind eva static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind); static inline bool expr_list_is_constant_eval(Expr **exprs, ConstantEvalKind eval_kind); static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind); -static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, uint32_t index); +static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, ArraySize index, bool from_back); Expr *expr_negate_expr(Expr *expr) { @@ -517,7 +517,7 @@ bool expr_is_compile_time(Expr *expr) UNREACHABLE } -static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, uint32_t index) +static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, ArraySize index, bool from_back) { switch (initializer->kind) { @@ -527,9 +527,23 @@ static inline ConstInitializer *initializer_for_index(ConstInitializer *initiali case CONST_INIT_VALUE: return initializer; case CONST_INIT_ARRAY_FULL: + { + unsigned len = vec_size(initializer->init_array_full); + if (from_back) + { + if (index > len || !index) return NULL; + index = len - index; + } return initializer->init_array_full[index]; + } case CONST_INIT_ARRAY: { + if (from_back) + { + ArraySize len = initializer->type->array.len; + if (index > len || !index) return NULL; + index = len - index; + } ConstInitializer **sub_values = initializer->init_array.elements; VECEACH(sub_values, i) { @@ -605,15 +619,19 @@ void expr_rewrite_to_const_zero(Expr *expr, Type *type) expr->type = type; } -bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index) +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); + ConstInitializer *initializer = initializer_for_index(list, index, from_back); ConstInitType kind = initializer ? initializer->kind : CONST_INIT_ZERO; switch (kind) { case CONST_INIT_ZERO: - expr_rewrite_to_const_zero(result, type_get_indexed_type(list_type)); + { + Type *indexed_type = type_get_indexed_type(list_type); + if (!indexed_type) return false; + expr_rewrite_to_const_zero(result, indexed_type); return true; + } case CONST_INIT_STRUCT: case CONST_INIT_UNION: case CONST_INIT_ARRAY: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 2712aa4aa..391a8903d 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -108,7 +108,7 @@ static inline const char *sema_addr_may_take_of_var(Expr *expr, Decl *decl); static inline const char *sema_addr_may_take_of_ident(Expr *inner); // -- subscript helpers -static bool sema_subscript_rewrite_index_const_list(Expr *const_list, Expr *index, Expr *result); +static bool sema_subscript_rewrite_index_const_list(Expr *const_list, ArraySize index, bool from_back, Expr *result); static Type *sema_subscript_find_indexable_type_recursively(Type **type, Expr **parent); // -- binary helper functions @@ -188,6 +188,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, bool was_group, Expr *identifier); static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *expr, Expr *parent, bool was_group, Expr *identifier); static inline bool sema_expr_fold_to_member(Expr *expr, Expr *parent, Decl *member); +static inline void sema_expr_flatten_const(SemaContext *context, Expr *expr); // -- implementations @@ -2416,15 +2417,10 @@ static Type *sema_subscript_find_indexable_type_recursively(Type **type, Expr ** } } -static bool sema_subscript_rewrite_index_const_list(Expr *const_list, Expr *index, Expr *result) +static bool sema_subscript_rewrite_index_const_list(Expr *const_list, ArraySize index, bool from_back, Expr *result) { - assert(expr_is_const_int(index)); - if (!int_fits(index->const_expr.ixx, TYPE_U32)) return false; - - uint32_t idx = index->const_expr.ixx.i.low; assert(const_list->const_expr.const_kind == CONST_INITIALIZER); - - return expr_rewrite_to_const_initializer_index(const_list->type, const_list->const_expr.initializer, result, idx); + return expr_rewrite_to_const_initializer_index(const_list->type, const_list->const_expr.initializer, result, index, from_back); } /** @@ -2642,7 +2638,6 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, if (remove_from_back) { start_from_end = expr->subscript_expr.range.start_from_end = false; - (void)start_from_end; } if (eval_type == SUBSCRIPT_EVAL_REF) @@ -2651,9 +2646,37 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, } else { - if (sema_flattened_expr_is_const(context, index) && sema_flattened_expr_is_const_initializer(context, current_expr)) + if (sema_flattened_expr_is_const(context, index)) { - if (sema_subscript_rewrite_index_const_list(current_expr, index, expr)) return true; + assert(expr_is_const_int(index)); + sema_expr_flatten_const(context, current_expr); + bool is_const_initializer = expr_is_const_initializer(current_expr); + if (is_const_initializer || expr_is_const_string(current_expr) || expr_is_const_bytes(current_expr)) + { + if (!int_fits(index->const_expr.ixx, TYPE_U32)) + { + RETURN_SEMA_ERROR(index, "Index is out of range."); + } + ArraySize idx = index->const_expr.ixx.i.low; + if (!is_const_initializer) + { + // Handle bytes / String + ArraySize len = current_expr->const_expr.bytes.len; + if (idx > len || (idx == len && !start_from_end) || (idx == 0 && start_from_end)) + { + RETURN_SEMA_ERROR(index, "The index (%s%llu) is out of range, the length is just %llu.", + start_from_end ? "^" : "", + (unsigned long long)idx, + (unsigned long long)current_expr->const_expr.bytes.len); + } + if (start_from_end) idx = len - idx; + unsigned char c = current_expr->const_expr.bytes.ptr[idx]; + expr_rewrite_const_int(expr, type_char, c); + expr->type = type_char; + return true; + } + if (sema_subscript_rewrite_index_const_list(current_expr, idx, start_from_end, expr)) return true; + } } } expr->subscript_expr.expr = exprid(current_expr); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index c789950df..ca2fa77a8 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2562,7 +2562,7 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state else { Expr *expr = expr_new(EXPR_CONST, collection->span); - if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i)) + if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i, false)) { SEMA_ERROR(collection, "Complex expressions are not allowed."); goto FAILED; diff --git a/src/version.h b/src/version.h index aa72b41c7..f572120c1 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.624" \ No newline at end of file +#define COMPILER_VERSION "0.4.625" \ No newline at end of file diff --git a/test/unit/regression/subscripting.c3 b/test/unit/regression/subscripting.c3 new file mode 100644 index 000000000..54e3a5a89 --- /dev/null +++ b/test/unit/regression/subscripting.c3 @@ -0,0 +1,15 @@ +module subscripting_tests @test; + +fn void subscript_ct() +{ + $if int.nameof[0] == 'i': + assert(true); + $else + assert(false); + $endif + $if int.nameof[^1] == 't': + assert(true); + $else + assert(false); + $endif +} \ No newline at end of file