From 70ea6ce04bcf62ea798dcc083131c03f3ac393bc Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 1 Jul 2023 16:28:22 +0200 Subject: [PATCH] Fix #806, and also makes sure that things like FOO.x.a is a compile time value. --- lib/std/collections/linkedlist.c3 | 4 +- src/compiler/expr.c | 2 + src/compiler/sema_expr.c | 69 ++++++++++++++++--- src/compiler/sema_internal.h | 1 + src/compiler/types.c | 2 +- src/version.h | 2 +- .../compile_time/ct_through_constant.c3t | 23 +++++++ .../compile_time/ct_value_from_access.c3t | 40 +++++++++++ 8 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 test/test_suite/compile_time/ct_through_constant.c3t create mode 100644 test/test_suite/compile_time/ct_value_from_access.c3t diff --git a/lib/std/collections/linkedlist.c3 b/lib/std/collections/linkedlist.c3 index 93188f06e..85b1ffd15 100644 --- a/lib/std/collections/linkedlist.c3 +++ b/lib/std/collections/linkedlist.c3 @@ -82,8 +82,8 @@ fn void LinkedList.link_last(LinkedList* list, Type value) @private list.size++; } -fn Type! peek(LinkedList* list) => list.first() @inline; -fn Type! peek_last(LinkedList* list) => list.last() @inline; +fn Type! LinkedList.peek(LinkedList* list) => list.first() @inline; +fn Type! LinkedList.peek_last(LinkedList* list) => list.last() @inline; fn Type! LinkedList.first(LinkedList *list) { diff --git a/src/compiler/expr.c b/src/compiler/expr.c index d7146f3eb..54007ed26 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -406,6 +406,8 @@ static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind Expr *inner = expr->unary_expr.expr; switch (inner->expr_kind) { + case EXPR_ACCESS: + return expr_is_constant_eval(inner, eval_kind); case EXPR_CONST: case EXPR_INITIALIZER_LIST: case EXPR_DESIGNATED_INITIALIZER_LIST: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index fbb9f0916..67a986ccc 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -138,7 +138,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, static inline int sema_call_find_index_of_named_parameter(SemaContext *context, Decl **func_params, Expr *expr); static inline bool sema_call_check_contract_param_match(SemaContext *context, Decl *param, Expr *expr); static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call); - +static bool sema_flattened_expr_is_const_initializer(SemaContext *context, Expr *expr); static bool sema_slice_len_is_in_range(SemaContext *context, Type *type, Expr *len_expr, bool from_end, bool *remove_from_end); static bool sema_slice_index_is_in_range(SemaContext *context, Type *type, Expr *index_expr, bool end_index, bool from_end, bool *remove_from_end); @@ -2628,7 +2628,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, } else { - if (expr_is_const_initializer(current_expr) && expr_is_const(index)) + if (sema_flattened_expr_is_const(context, index) && sema_flattened_expr_is_const_initializer(context, current_expr)) { if (sema_subscript_rewrite_index_const_list(current_expr, index, expr)) return true; } @@ -3398,7 +3398,8 @@ static bool sema_expr_rewrite_typeid_call(Expr *expr, Expr *typeid, TypeIdInfoKi static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *expr, Expr *typeid, const char *kw) { TypeProperty property = type_property_by_name(kw); - if (typeid->expr_kind == EXPR_CONST) + + if (sema_flattened_expr_is_const(context, typeid)) { Type *type = typeid->const_expr.typeid; return sema_expr_rewrite_to_type_property(context, expr, type, property, type); @@ -3625,6 +3626,52 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E return true; } +static inline void sema_expr_flatten_const(SemaContext *context, Expr *expr) +{ + if (expr->expr_kind != EXPR_IDENTIFIER) return; + Decl *ident = expr->identifier_expr.decl; + if (ident->decl_kind != DECL_VAR) return; + switch (ident->var.kind) + { + case VARDECL_CONST: + case VARDECL_LOCAL_CT: + case VARDECL_PARAM_CT: + break; + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_PARAM: + case VARDECL_MEMBER: + case VARDECL_BITMEMBER: + case VARDECL_PARAM_REF: + case VARDECL_PARAM_EXPR: + case VARDECL_UNWRAPPED: + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_LOCAL_CT_TYPE: + return; + } + Expr *init_expr = ident->var.init_expr; + if (!init_expr) return; + sema_expr_flatten_const(context, init_expr); + if (expr_is_const(init_expr)) + { + expr_replace(expr, expr_copy(init_expr)); + } +} + +bool sema_flattened_expr_is_const(SemaContext *context, Expr *expr) +{ + sema_expr_flatten_const(context, expr); + return expr_is_const(expr); +} + +static bool sema_flattened_expr_is_const_initializer(SemaContext *context, Expr *expr) +{ + sema_expr_flatten_const(context, expr); + return expr_is_const_initializer(expr); +} + /** * Analyse "x.y" */ @@ -3741,6 +3788,7 @@ CHECK_DEEPER: if (flat_type->type_kind == TYPE_SUBARRAY) { // Handle literal "foo".len which is now a subarray. + sema_expr_flatten_const(context, parent); if (expr_is_const_string(parent)) { expr_rewrite_const_int(expr, type_isz, parent->const_expr.string.len); @@ -3802,7 +3850,7 @@ CHECK_DEEPER: } if (flat_type->type_kind == TYPE_FAULTTYPE) { - if (expr_is_const(current_parent)) + if (sema_flattened_expr_is_const(context, current_parent)) { if (current_parent->const_expr.const_kind == CONST_POINTER) { @@ -3821,7 +3869,7 @@ CHECK_DEEPER: { if (flat_type->type_kind == TYPE_ENUM) { - if (expr_is_const(current_parent)) + if (sema_flattened_expr_is_const(context, current_parent)) { expr_rewrite_to_string(expr, current_parent->const_expr.enum_err_val->name); return true; @@ -6474,7 +6522,7 @@ static inline bool sema_expr_analyse_decl_element(SemaContext *context, Designat SEMA_ERROR(inner, "Expected an integer index."); return false; } - if (!expr_is_const(inner)) + if (!sema_flattened_expr_is_const(context, inner)) { SEMA_ERROR(inner, "Expected a constant index."); return false; @@ -7337,7 +7385,7 @@ static inline bool sema_expr_analyse_retval(SemaContext *c, Expr *expr) } Expr *return_value = c->return_expr; assert(return_value); - if (expr_is_const(return_value)) + if (sema_flattened_expr_is_const(c, return_value)) { expr_replace(expr, return_value); } @@ -7576,6 +7624,11 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) SEMA_ERROR(expr, "A macro name must be followed by '('."); return false; } + // We may have kept FOO.x.y as a reference, fold it now if y is not an aggregate. + if (!type_is_abi_aggregate(expr->type) && sema_flattened_expr_is_const(context, expr->access_expr.parent)) + { + return sema_expr_fold_to_member(expr, expr->access_expr.parent, expr->access_expr.ref); + } break; case EXPR_TYPEINFO: SEMA_ERROR(expr, "A type must be followed by either (...) or '.'."); @@ -7615,7 +7668,7 @@ bool sema_analyse_ct_expr(SemaContext *context, Expr *expr) expr->type = type_typeid; } if (!sema_cast_rvalue(context, expr)) return false; - if (!expr_is_const(expr)) + if (!sema_flattened_expr_is_const(context, expr)) { SEMA_ERROR(expr, "Expected a compile time expression."); return false; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index a1e5b2661..7a70364ef 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -86,6 +86,7 @@ bool cast_promote_vararg(Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); bool sema_decl_if_cond(SemaContext *context, Decl *decl); +bool sema_flattened_expr_is_const(SemaContext *context, Expr *expr); bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span); diff --git a/src/compiler/types.c b/src/compiler/types.c index 2974afebf..ce944e1fb 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -400,7 +400,7 @@ bool type_is_abi_aggregate(Type *type) return true; case CT_TYPES: case TYPE_FLEXIBLE_ARRAY: - UNREACHABLE + return false; } UNREACHABLE } diff --git a/src/version.h b/src/version.h index 677d60213..6faa8af73 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.545" \ No newline at end of file +#define COMPILER_VERSION "0.4.546" \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_through_constant.c3t b/test/test_suite/compile_time/ct_through_constant.c3t new file mode 100644 index 000000000..52bce83ea --- /dev/null +++ b/test/test_suite/compile_time/ct_through_constant.c3t @@ -0,0 +1,23 @@ +// #target: macos-x64 +module test; + +const FOO = "123"; +int[FOO.len] x; + +fn void main() +{ + int z = x[0]; +} + +/* #expect: test.ll + +@test.FOO = local_unnamed_addr constant %"char[]" { ptr @.str, i64 3 }, align 8 +@test.x = local_unnamed_addr global [3 x i32] zeroinitializer, align 4 + +define void @test.main() #0 { +entry: + %z = alloca i32, align 4 + %0 = load i32, ptr @test.x, align 4 + store i32 %0, ptr %z, align 4 + ret void +} diff --git a/test/test_suite/compile_time/ct_value_from_access.c3t b/test/test_suite/compile_time/ct_value_from_access.c3t new file mode 100644 index 000000000..1015ca9af --- /dev/null +++ b/test/test_suite/compile_time/ct_value_from_access.c3t @@ -0,0 +1,40 @@ +// #target: macos-x64 +module test; +struct Abc +{ + int a; + int b; +} + +const Abc X = { 2, 3 }; + +const int Y = X.b; +const int* Z = &X.b; + +fn void main() +{ + Abc x = X; + int y = Y; + int* yy = &Y; + int* z = Z; +} + +/* #expect: test.ll + +@test.X = constant %Abc { i32 2, i32 3 }, align 4 +@test.Y = constant i32 3, align 4 +@test.Z = local_unnamed_addr constant ptr getelementptr inbounds (%Abc, ptr @test.X, i32 0, i32 1), align 8 + +define void @test.main() #0 { +entry: + %x = alloca %Abc, align 4 + %y = alloca i32, align 4 + %yy = alloca ptr, align 8 + %z = alloca ptr, align 8 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %x, ptr align 4 @test.X, i32 8, i1 false) + store i32 3, ptr %y, align 4 + store ptr @test.Y, ptr %yy, align 8 + store ptr getelementptr inbounds (%Abc, ptr @test.X, i32 0, i32 1), ptr %z, align 8 + ret void +} +