diff --git a/releasenotes.md b/releasenotes.md index f55aa33cc..25c7baeff 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -42,6 +42,7 @@ - Show error when declarations do not start with `fn` in interfaces. #1565 - `if (try foo)` was handled incorrectly inside a defer. - `&self` argument not implicitly null checked. #1556. +- `(uptr)&((Foo*)null).a` incorrectly inserts a null check. #1544 ### Stdlib changes - Remove unintended print of `char[]` as String diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d4db80292..451e82278 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2811,37 +2811,6 @@ INLINE const char *type_invalid_storage_type_name(Type *type) } } -static inline StorageType type_storage_type(Type *type) -{ - if (!type) return STORAGE_NORMAL; - bool is_distinct = false; - RETRY: - if (type == type_wildcard_optional) return STORAGE_WILDCARD; - switch (type->type_kind) - { - case TYPE_VOID: - return is_distinct ? STORAGE_UNKNOWN : STORAGE_VOID; - case TYPE_WILDCARD: - return STORAGE_WILDCARD; - case TYPE_MEMBER: - case TYPE_UNTYPED_LIST: - case TYPE_TYPEINFO: - case TYPE_FUNC_RAW: - return STORAGE_COMPILE_TIME; - case TYPE_OPTIONAL: - type = type->optional; - goto RETRY; - case TYPE_TYPEDEF: - type = type->canonical; - goto RETRY; - case TYPE_DISTINCT: - is_distinct = true; - type = type->decl->distinct->type; - goto RETRY; - default: - return STORAGE_NORMAL; - } -} INLINE TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span) { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 351df12b1..48b26482b 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -1014,7 +1014,8 @@ typedef enum FLAG_ATTR typedef enum { - STORAGE_NORMAL, + STORAGE_ERROR = -1, + STORAGE_NORMAL = 0, STORAGE_VOID, STORAGE_COMPILE_TIME, STORAGE_WILDCARD, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 869bfa3bf..3e4eb4d65 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -795,6 +795,7 @@ static void llvm_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, D { assert(member->resolve_status == RESOLVE_DONE); Decl *found = NULL; + do { ArrayIndex index = find_member_index(parent, member); @@ -1191,12 +1192,12 @@ static inline void llvm_emit_bitaccess(GenContext *c, BEValue *be_value, Expr *e static inline void llvm_emit_access_addr(GenContext *c, BEValue *be_value, Expr *expr) { Expr *parent = expr->access_expr.parent; - llvm_emit_expr(c, be_value, parent); - Decl *member = expr->access_expr.ref; - Type *flat_type = type_flatten(parent->type); if (flat_type->type_kind == TYPE_ENUM) { + llvm_emit_expr(c, be_value, parent); + Decl *member = expr->access_expr.ref; + llvm_value_rvalue(c, be_value); if (!flat_type->decl->backend_ref) llvm_get_typeid(c, parent->type); assert(member->backend_ref); @@ -1207,6 +1208,18 @@ static inline void llvm_emit_access_addr(GenContext *c, BEValue *be_value, Expr llvm_value_set_address(be_value, ptr, member->type, alignment); return; } + if (expr_is_deref(parent)) + { + llvm_emit_expr(c, be_value, parent->unary_expr.expr); + llvm_value_rvalue(c, be_value); + llvm_value_set_address_abi_aligned(be_value, be_value->value, parent->type); + } + else + { + llvm_emit_expr(c, be_value, parent); + } + Decl *member = expr->access_expr.ref; + llvm_emit_member_addr(c, be_value, type_lowering(parent->type)->decl, member); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 06f6b342a..62ba62dcc 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -52,7 +52,7 @@ static bool sema_check_section(SemaContext *context, Attr *attr); static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext *c, Decl *decl, bool *erase_decl); static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl); -static bool sema_analyse_decl_type(SemaContext *context, Type *type, SourceSpan span); +static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceSpan span); static inline bool sema_analyse_define(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl, bool *erase_decl); @@ -243,8 +243,10 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent TypeInfo *type_info = type_infoptr(decl->var.type_info); if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_FLEXIBLE)) return decl_poison(decl); Type *type = type_info->type; - switch (type_storage_type(type)) + switch (sema_resolve_storage_type(context, type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: break; case STORAGE_VOID: @@ -1693,8 +1695,10 @@ static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl * TypeInfo *rtype; Decl **params; if (!sema_analyse_operator_common(context, method, &rtype, ¶ms, 2)) return false; - switch (type_storage_type(rtype->type)) + switch (sema_resolve_storage_type(context, rtype->type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: break; case STORAGE_VOID: @@ -3650,10 +3654,12 @@ static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl, bo return true; } -static bool sema_analyse_decl_type(SemaContext *context, Type *type, SourceSpan span) +static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceSpan span) { - switch (type_storage_type(type)) + switch (sema_resolve_storage_type(context, type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: return true; case STORAGE_VOID: @@ -3868,8 +3874,10 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) return decl_poison(decl); } decl->type = init_expr->type; - switch (type_storage_type(init_expr->type)) + switch (sema_resolve_storage_type(context, init_expr->type)) { + case STORAGE_ERROR: + return decl_poison(decl); case STORAGE_NORMAL: break; case STORAGE_WILDCARD: @@ -3900,7 +3908,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) { if (!sema_set_alloca_alignment(context, decl->type, &decl->alignment)) return false; } - if (!sema_analyse_decl_type(context, decl->type, init_expr->span)) return decl_poison(decl); + if (!sema_analyse_variable_type(context, decl->type, init_expr->span)) return decl_poison(decl); // Skip further evaluation. goto EXIT_OK; } @@ -3911,7 +3919,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) : RESOLVE_TYPE_DEFAULT)) return decl_poison(decl); Type *type = decl->type = type_info->type; - if (!sema_analyse_decl_type(context, type, type_info->span)) return decl_poison(decl); + if (!sema_analyse_variable_type(context, type, type_info->span)) return decl_poison(decl); type = type_no_optional(type); if (type_is_user_defined(type) && type->decl) @@ -4083,8 +4091,10 @@ static bool sema_generate_parameterized_name_to_scratch(SemaContext *context, Mo if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; Type *type = type_info->type->canonical; if (type->type_kind == TYPE_OPTIONAL) RETURN_SEMA_ERROR(type_info, "Expected a non-optional type."); - switch (type_storage_type(type)) + switch (sema_resolve_storage_type(context, type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: case STORAGE_VOID: break; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a379f574b..ec1899499 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -888,13 +888,19 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_t false)) return false; } - if (type_storage_type(left->type) == STORAGE_COMPILE_TIME) + switch (sema_resolve_storage_type(context, left->type)) { - if (left->type == type_untypedlist) - { - RETURN_SEMA_ERROR(expr, "The ternary would be an 'untyped list', you need to explicitly type one or both branches to a runtime type."); - } - RETURN_SEMA_ERROR(expr, "A ternary must always return a runtime type, but it was %s.", type_quoted_error_string(left_canonical)); + case STORAGE_ERROR: + return false; + case STORAGE_COMPILE_TIME: + if (left->type == type_untypedlist) + { + RETURN_SEMA_ERROR(expr, "The ternary would be an 'untyped list', you need to explicitly type one or both branches to a runtime type."); + } + RETURN_SEMA_ERROR(expr, "A ternary must always return a runtime type, but it was %s.", type_quoted_error_string(left_canonical)); + break; + default: + break; } if (path != COND_MISSING) { @@ -1278,9 +1284,14 @@ static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, SEMA_NOTE(definition, "The definition is here."); return false; } - if (type_storage_type(type) != STORAGE_NORMAL) + switch (sema_resolve_storage_type(context, type)) { - RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type)); + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + break; + default: + RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type)); } if (type && type->canonical != arg->type->canonical) { @@ -1305,8 +1316,10 @@ static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, // foo if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref, false)) return false; if (IS_OPTIONAL(arg)) *optional_ref = true; - switch (type_storage_type(arg->type)) + switch (sema_resolve_storage_type(context, arg->type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: break; case STORAGE_VOID: @@ -1698,10 +1711,15 @@ SPLAT_NORMAL:; if (!callee->macro) { if (!sema_analyse_expr(context, arg)) return false; - if (type_storage_type(arg->type) != STORAGE_NORMAL) + switch (sema_resolve_storage_type(context, arg->type)) { - RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a raw variadic argument.", - type_quoted_error_string(arg->type)); + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + break; + default: + RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a raw variadic argument.", + type_quoted_error_string(arg->type)); } cast_promote_vararg(context, arg); } @@ -1711,10 +1729,15 @@ SPLAT_NORMAL:; case VARIADIC_ANY: if (!sema_analyse_expr(context, arg)) return false; Type *type = arg->type; - if (type_storage_type(type) != STORAGE_NORMAL) + switch (sema_resolve_storage_type(context, arg->type)) { - RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.", - type_quoted_error_string(type)); + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + break; + default: + RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.", + type_quoted_error_string(type)); } expr_insert_addr(arg); FALLTHROUGH; @@ -2137,9 +2160,19 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s type_quoted_error_string(type_get_ptr(type_info->type))); } body_arg->type = type; - if (type_info && type_storage_type(type_info->type) == STORAGE_NORMAL) + + if (type_info) { - if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false; + switch (sema_resolve_storage_type(context, type_info->type)) + { + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false; + break; + default: + break; + } } } @@ -7039,15 +7072,21 @@ static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr, boo if (!sema_analyse_expr(context, inner)) return false; Type *type = inner->type; - if (type_storage_type(type) != STORAGE_NORMAL) + + switch (sema_resolve_storage_type(context, type)) { - if (failed_ref) - { - *failed_ref = true; + case STORAGE_ERROR: return false; - } - RETURN_SEMA_ERROR(expr, "It is not possible to take the address from a value of type %s.", - type_quoted_error_string(type)); + case STORAGE_NORMAL: + break; + default: + if (failed_ref) + { + *failed_ref = true; + return false; + } + RETURN_SEMA_ERROR(expr, "It is not possible to take the address from a value of type %s.", + type_quoted_error_string(type)); } // 2. The type is the resulting type of the expression. expr->type = type_get_ptr_recurse(inner->type); @@ -7797,10 +7836,14 @@ static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr Decl *decl = sema_expr_analyse_var_path(context, main_var); if (!decl) return false; Type *type = decl->type; - if (type_storage_type(type) != STORAGE_NORMAL) + switch (sema_resolve_storage_type(context, type)) { - SEMA_ERROR(main_var, "Cannot use '$alignof' on type %s.", type_quoted_error_string(type)); - return false; + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + break; + default: + RETURN_SEMA_ERROR(main_var, "Cannot use '$alignof' on type %s.", type_quoted_error_string(type)); } AlignSize align; if (decl && !decl_is_user_defined_type(decl)) @@ -8663,9 +8706,14 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty if (!decl) { if (!sema_analyse_inferred_expr(context, infer_type, arg_expr)) return false; - if (type_storage_type(arg_expr->type) != STORAGE_NORMAL) + switch (sema_resolve_storage_type(context, arg_expr->type)) { - RETURN_SEMA_ERROR(expr, "The vararg doesn't have a valid runtime type."); + case STORAGE_ERROR: + return false; + case STORAGE_NORMAL: + break; + default: + RETURN_SEMA_ERROR(expr, "The vararg doesn't have a valid runtime type."); } decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM, arg_expr->span); decl->var.init_expr = arg_expr; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index f41bae556..e29830df1 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -209,3 +209,37 @@ static inline IndexDiff range_const_len(Range *range) if (range->start_from_end != range->end_from_end) return -1; return end_val - start_val + 1; } + +static inline StorageType sema_resolve_storage_type(SemaContext *context, Type *type) +{ + if (!type) return STORAGE_NORMAL; + bool is_distinct = false; + RETRY: + if (type == type_wildcard_optional) return STORAGE_WILDCARD; + switch (type->type_kind) + { + case TYPE_VOID: + return is_distinct ? STORAGE_UNKNOWN : STORAGE_VOID; + case TYPE_WILDCARD: + return STORAGE_WILDCARD; + case TYPE_MEMBER: + case TYPE_UNTYPED_LIST: + case TYPE_TYPEINFO: + case TYPE_FUNC_RAW: + return STORAGE_COMPILE_TIME; + case TYPE_OPTIONAL: + type = type->optional; + goto RETRY; + case TYPE_TYPEDEF: + if (!sema_analyse_decl(context, type->decl)) return false; + type = type->canonical; + goto RETRY; + case TYPE_DISTINCT: + is_distinct = true; + if (!sema_analyse_decl(context, type->decl)) return false; + type = type->decl->distinct->type; + goto RETRY; + default: + return STORAGE_NORMAL; + } +} \ No newline at end of file diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 9e1977f5d..94dcf31f1 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -291,8 +291,10 @@ INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, Res } TypeInfo *inner_type = inner->type_expr; if (!sema_resolve_type(context, inner_type, resolve_kind)) return false; - switch (type_storage_type(inner_type->type)) + switch (sema_resolve_storage_type(context, inner_type->type)) { + case STORAGE_ERROR: + return false; case STORAGE_VOID: case STORAGE_UNKNOWN: case STORAGE_NORMAL: @@ -313,8 +315,10 @@ INLINE bool sema_resolve_typeof(SemaContext *context, TypeInfo *type_info) if (!sema_analyse_expr_value(context, expr)) return false; Type *expr_type = expr->type; if (expr_type->type_kind == TYPE_FUNC_RAW) expr_type = type_get_func_ptr(expr_type); - switch (type_storage_type(expr_type)) + switch (sema_resolve_storage_type(context, expr_type)) { + case STORAGE_ERROR: + return false; case STORAGE_NORMAL: case STORAGE_VOID: case STORAGE_UNKNOWN: diff --git a/test/test_suite/expressions/deref_access_null.c3t b/test/test_suite/expressions/deref_access_null.c3t new file mode 100644 index 000000000..2ebe621b1 --- /dev/null +++ b/test/test_suite/expressions/deref_access_null.c3t @@ -0,0 +1,37 @@ +// #target: macos-x64 +// #safe: yes +module test; + +struct Foo +{ + int a; + float b; +} + +macro @offset_buggy($Type, #member) +{ + $Type *t = null; + return (usz)(uptr)&t.#member; +} + +fn int main() +{ + usz a = @offset_buggy(Foo, b); + return (int)a; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %a = alloca i64, align 8 + %t = alloca ptr, align 8 + store ptr null, ptr %t, align 8 + %0 = load ptr, ptr %t, align 8 + %ptradd = getelementptr inbounds i8, ptr %0, i64 4 + %ptrxi = ptrtoint ptr %ptradd to i64 + store i64 %ptrxi, ptr %a, align 8 + %1 = load i64, ptr %a, align 8 + %trunc = trunc i64 %1 to i32 + ret i32 %trunc +} diff --git a/test/test_suite/generic/nested_typedef.c3t b/test/test_suite/generic/nested_typedef.c3t new file mode 100644 index 000000000..5e467affb --- /dev/null +++ b/test/test_suite/generic/nested_typedef.c3t @@ -0,0 +1,30 @@ +// #target: macos-x64 +module broken(); +distinct Bar = Type; +def Bar2 = Type; +struct Baz +{ + int a; +} +module test; +import broken; + +struct Foo +{ + Bar() y; +} + +struct Foo2 +{ + Bar2() y; +} +fn int test() @if(Foo.sizeof > 2) => 1; +fn int test2() @if(Foo2.sizeof > 2) => 1; +fn void main() {} + +/* #expect: test.ll + +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo2" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +define i32 @test.test() #0 { +define i32 @test.test2() #0 {