diff --git a/src/compiler/ast.c b/src/compiler/ast.c index ec8d71d3a..97054f4d0 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -221,6 +221,7 @@ bool expr_is_pure(Expr *expr) case EXPR_CONST_IDENTIFIER: case EXPR_IDENTIFIER: case EXPR_NOP: + case EXPR_PTR: return true; case EXPR_BITASSIGN: return false; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2918c3f2f..1a1d72a30 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1539,6 +1539,7 @@ extern Type *type_anyfail; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; extern const char *builtin_list[NUMBER_OF_BUILTINS]; + extern const char *kw_std; extern const char *kw_max; extern const char *kw_min; @@ -1562,6 +1563,7 @@ extern const char *kw_reqparse; extern const char *kw_require; extern const char *kw_pure; extern const char *kw_param; +extern const char *kw_ptr; extern const char *kw_errors; extern const char *kw___ceil; extern const char *kw___round; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 4a7db0199..bd44ba96e 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -127,6 +127,7 @@ Expr *copy_expr(Expr *source_expr) case EXPR_FAILABLE: case EXPR_GROUP: case EXPR_TYPEOFANY: + case EXPR_PTR: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_COND: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index df54939b6..1a289e757 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -202,6 +202,7 @@ typedef enum EXPR_INITIALIZER_LIST, EXPR_DESIGNATED_INITIALIZER_LIST, EXPR_LEN, + EXPR_PTR, EXPR_PLACEHOLDER, EXPR_POST_UNARY, EXPR_SCOPED_EXPR, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 75aa3f350..ee7e0ca2f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1133,14 +1133,14 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_ } break; case CAST_ANYPTR: - llvm_value_fold_failable(c, value); + llvm_emit_any_pointer(c, value, value); if (llvm_value_is_addr(value)) { - llvm_emit_any_pointer(c, value, value); + value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), ""); } else { - value->value = LLVMBuildExtractValue(c->builder, value->value, 0, ""); + value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_type(c, to_type), ""); } break; case CAST_XIERR: @@ -1177,15 +1177,7 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_ llvm_emit_arr_to_subarray_cast(c, value, to_type); break; case CAST_SAPTR: - llvm_value_fold_failable(c, value); - if (llvm_value_is_addr(value)) - { - llvm_emit_subarray_pointer(c, value, value); - } - else - { - value->value = LLVMBuildExtractValue(c->builder, value->value, 0, ""); - } + llvm_emit_subarray_pointer(c, value, value); break; case CAST_ARRPTR: TODO @@ -3836,6 +3828,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values, AlignSize alignment); + static void llvm_expand_array_to_args(GenContext *c, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values, AlignSize alignment) { LLVMTypeRef array_type = llvm_get_type(c, param_type); @@ -3985,30 +3978,33 @@ void llvm_emit_subarray_len(GenContext *c, BEValue *subarray, BEValue *len) llvm_value_set_address_align(len, len_addr, type_usize, alignment); } -void llvm_emit_subarray_pointer(GenContext *c, BEValue *subarray, BEValue *pointer) +void llvm_emit_subarray_pointer(GenContext *c, BEValue *value, BEValue *pointer) { - llvm_value_addr(c, subarray); - AlignSize alignment = 0; - LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, - subarray->value, - llvm_get_type(c, subarray->type), - 0, - subarray->alignment, - &alignment); - llvm_value_set_address_align(pointer, pointer_addr, type_get_ptr(subarray->type->array.base), alignment); + assert(value->type->type_kind == TYPE_SUBARRAY); + Type *ptr_type = type_get_ptr(value->type->array.base); + if (value->kind == BE_ADDRESS) + { + AlignSize alignment; + LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, value->value, llvm_get_type(c, value->type), 0, value->alignment, &alignment); + llvm_value_set_address_align(pointer, ptr, ptr_type, alignment); + return; + } + LLVMValueRef ptr = llvm_emit_extract_value(c, value->value, 0); + llvm_value_set(pointer, ptr, ptr_type); } -static void llvm_emit_any_pointer(GenContext *c, BEValue *any, BEValue *pointer) +static void llvm_emit_any_pointer(GenContext *c, BEValue *value, BEValue *pointer) { - llvm_value_addr(c, any); - AlignSize alignment = 0; - LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, - any->value, - llvm_get_type(c, type_voidptr), - 0, - any->alignment, - &alignment); - llvm_value_set_address_align(pointer, pointer_addr, type_voidptr, alignment); + llvm_value_fold_failable(c, value); + if (value->kind == BE_ADDRESS) + { + AlignSize alignment; + LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, value->value, llvm_get_type(c, value->type), 0, value->alignment, &alignment); + llvm_value_set_address_align(pointer, ptr, type_voidptr, alignment); + return; + } + LLVMValueRef ptr = llvm_emit_extract_value(c, value->value, 0); + llvm_value_set(pointer, ptr, type_voidptr); } void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index) @@ -5109,6 +5105,18 @@ void llvm_emit_catch_unwrap(GenContext *c, BEValue *value, Expr *expr) llvm_value_set(value, addr.value, type_anyerr); } +static inline void llvm_emit_ptr(GenContext *c, BEValue *value, Expr *expr) +{ + llvm_emit_expr(c, value, expr->inner_expr); + if (value->type == type_any) + { + llvm_emit_any_pointer(c, value, value); + return; + } + assert(value->type->type_kind == TYPE_SUBARRAY); + llvm_emit_subarray_pointer(c, value, value); +} + void llvm_emit_try_unwrap_chain(GenContext *c, BEValue *value, Expr *expr) { Expr **exprs = expr->try_unwrap_chain_expr; @@ -5186,6 +5194,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_UNDEF: // Should never reach this. UNREACHABLE + case EXPR_PTR: + llvm_emit_ptr(c, value, expr); + return; case EXPR_BUILTIN: TODO case EXPR_DECL: @@ -5291,3 +5302,4 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) UNREACHABLE } + diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 95ee1cbfd..6d6d99fd3 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -819,6 +819,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_TRY_UNWRAP_CHAIN: case EXPR_SUBSCRIPT_ADDR: case EXPR_TYPEOFANY: + case EXPR_PTR: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -971,6 +972,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_TRY_UNWRAP_CHAIN: case EXPR_SUBSCRIPT_ADDR: case EXPR_TYPEOFANY: + case EXPR_PTR: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 1085d2117..07d221e27 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -402,6 +402,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_FORCE_UNWRAP: case EXPR_TRY: case EXPR_CATCH: + case EXPR_PTR: expr = expr->inner_expr; goto RETRY; case EXPR_TYPEID: @@ -2958,6 +2959,27 @@ CHECK_DEEPER: } } + // Hard coded ptr on subarrays and variant + if (!is_macro && kw == kw_ptr) + { + if (flat_type->type_kind == TYPE_SUBARRAY) + { + expr->expr_kind = EXPR_PTR; + expr->inner_expr = parent; + expr->type = type_get_ptr(flat_type->array.base); + expr->resolve_status = RESOLVE_DONE; + return true; + } + if (flat_type->type_kind == TYPE_ANY) + { + expr->expr_kind = EXPR_PTR; + expr->inner_expr = parent; + expr->type = type_voidptr; + expr->resolve_status = RESOLVE_DONE; + return true; + } + } + // 9. At this point we may only have distinct, struct, union, error, enum if (!type_may_have_sub_elements(type)) { @@ -6655,6 +6677,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) case EXPR_TRY_UNWRAP_CHAIN: case EXPR_TRY_UNWRAP: case EXPR_CATCH_UNWRAP: + case EXPR_PTR: UNREACHABLE case EXPR_DECL: if (!sema_analyse_var_decl(context, expr->decl_expr, true)) return false; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 6a2c810b9..e1d982d95 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -57,6 +57,7 @@ const char *kw_next; const char *kw_nan; const char *kw_ordinal; const char *kw_param; +const char *kw_ptr; const char *kw_pure; const char *kw_reqparse; const char *kw_require; @@ -136,6 +137,7 @@ void symtab_init(uint32_t capacity) kw_next = KW_DEF("next"); kw_ordinal = KW_DEF("ordinal"); kw_param = KW_DEF("param"); + kw_ptr = KW_DEF("ptr"); kw_pure = KW_DEF("pure"); kw_require = KW_DEF("require"); kw_std = KW_DEF("std"); diff --git a/test/test_suite/pointers/subarray_variant_to_ptr.c3t b/test/test_suite/pointers/subarray_variant_to_ptr.c3t new file mode 100644 index 000000000..c9ffadff9 --- /dev/null +++ b/test/test_suite/pointers/subarray_variant_to_ptr.c3t @@ -0,0 +1,98 @@ +// #target: x64-darwin +module foo; + +extern fn void printf(char*, ...); + +fn void test1(variant z) +{ + int* w = z.ptr; + printf("%d\n", *w); +} + +fn void test2(int[] z) +{ + int* w = z.ptr; + printf("%d\n", *w); +} + +fn void main() +{ + int x = 123; + int y = 293483; + int[2] w = { 144, 772 }; + test1(&x); + test2(w[..]); +} + +/* #expect: foo.ll + +define void @foo.test1(i64 %0, i8* %1) #0 { +entry: + %z = alloca %variant, align 8 + %w = alloca i32*, align 8 + %pair = bitcast %variant* %z to { i64, i8* }* + %2 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 0 + store i64 %0, i64* %2, align 8 + %3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 + store i8* %1, i8** %3, align 8 + %4 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 0 + %5 = load i8*, i8** %4, align 8 + %ptrptr = bitcast i8* %5 to i32* + store i32* %ptrptr, i32** %w, align 8 + %6 = load i32*, i32** %w, align 8 + %7 = load i32, i32* %6, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %7) + ret void +} + +define void @foo.test2(i8* %0, i64 %1) #0 { +entry: + %z = alloca %"int[]", align 8 + %w = alloca i32*, align 8 + %pair = bitcast %"int[]"* %z to { i8*, i64 }* + %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %2, align 8 + %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %3, align 8 + %4 = getelementptr inbounds %"int[]", %"int[]"* %z, i32 0, i32 0 + %5 = load i32*, i32** %4, align 8 + store i32* %5, i32** %w, align 8 + %6 = load i32*, i32** %w, align 8 + %7 = load i32, i32* %6, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %7) + ret void +} + +define void @main() #0 { +entry: + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %w = alloca [2 x i32], align 4 + %taddr = alloca %variant, align 8 + %taddr1 = alloca %"int[]", align 8 + store i32 123, i32* %x, align 4 + store i32 293483, i32* %y, align 4 + %0 = bitcast [2 x i32]* %w to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast ([2 x i32]* @.__const to i8*), i32 8, i1 false) + %1 = bitcast i32* %x to i8* + %2 = insertvalue %variant undef, i8* %1, 0 + %3 = insertvalue %variant %2, i64 5, 1 + store %variant %3, %variant* %taddr, align 8 + %4 = bitcast %variant* %taddr to { i64, i8* }* + %5 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %4, i32 0, i32 0 + %lo = load i64, i64* %5, align 8 + %6 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %4, i32 0, i32 1 + %hi = load i8*, i8** %6, align 8 + call void @foo.test1(i64 %lo, i8* %hi) + %7 = getelementptr inbounds [2 x i32], [2 x i32]* %w, i64 0, i64 0 + %8 = insertvalue %"int[]" undef, i32* %7, 0 + %9 = insertvalue %"int[]" %8, i64 2, 1 + store %"int[]" %9, %"int[]"* %taddr1, align 8 + %10 = bitcast %"int[]"* %taddr1 to { i8*, i64 }* + %11 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %10, i32 0, i32 0 + %lo2 = load i8*, i8** %11, align 8 + %12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %10, i32 0, i32 1 + %hi3 = load i64, i64* %12, align 8 + call void @foo.test2(i8* %lo2, i64 %hi3) + ret void +} \ No newline at end of file