From 3f41e58dbd42c94df429c89ad3b5434ccbc075eb Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 20 Jul 2023 12:49:02 +0200 Subject: [PATCH] Fix pseudo-circular function pointer definitions. --- src/compiler/compiler_internal.h | 1 + src/compiler/sema_decls.c | 49 ++++++++++++++++----------- src/compiler/sema_expr.c | 40 ++++++++++++++-------- src/compiler/sema_internal.h | 2 +- src/compiler/sema_stmts.c | 1 + src/compiler/types.c | 10 ++++++ test/test_suite/types/non_rec_fn.c3 | 10 ++++++ test/test_suite/types/recursive_fn.c3 | 1 + 8 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 test/test_suite/types/non_rec_fn.c3 create mode 100644 test/test_suite/types/recursive_fn.c3 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9d3ef9917..02e7db35b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2362,6 +2362,7 @@ void type_func_prototype_init(uint32_t capacity); bool type_is_subtype(Type *type, Type *possible_subtype); bool type_is_abi_aggregate(Type *type); bool type_is_int128(Type *type); +Type *type_get_func_alias(const char *name, Signature *signature, Module *module); Type *type_get_func(Signature *signature, CallABI abi); Type *type_from_token(TokenType type); bool type_is_user_defined(Type *type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 5bc3d6e50..b38776c5b 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -689,6 +689,15 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase return true; } +static bool check_safe_param(TypeInfo *type_info) +{ + Type *type = type_info->type->canonical; + if (type->type_kind != TYPE_POINTER) return true; + Type *pointee = type->pointer; + if (pointee->type_kind != TYPE_FUNC) return true; + if (pointee->function.prototype) return true; + RETURN_SEMA_ERROR(type_info, "%s has a circular definition.", type_quoted_error_string(type)); +} static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent) { @@ -723,6 +732,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, return false; } } + if (!check_safe_param(rtype_info)) return false; } // We don't support more than MAX_PARAMS number of params. This makes everything sane. @@ -755,7 +765,8 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, default: goto CHECK_PARAMS; } - param->var.type_info = type_info_new_base(inferred_type, param->span); + TypeInfo *type_info = type_info_new_base(inferred_type, param->span); + param->var.type_info = type_info; } CHECK_PARAMS: @@ -885,7 +896,10 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, if (param->var.type_info) { - param->type = param->var.type_info->type; + TypeInfo *type_info = param->var.type_info; + if (!check_safe_param(type_info)) return false; + + param->type = type_info->type; if (!sema_set_abi_alignment(context, param->type, ¶m->alignment)) return false; } if (param->var.init_expr) @@ -903,12 +917,13 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, } -Type *sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Signature *signature, bool is_real_function) +bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Type *type, bool is_real_function) { // Get param count and variadic type + Signature *signature = type->function.signature; Decl **params = signature->params; - if (!sema_analyse_signature(context, signature, func_decl->func_decl.type_parent)) return NULL; + if (!sema_analyse_signature(context, signature, func_decl->func_decl.type_parent)) return false; Variadic variadic_type = signature->variadic; // Remove the last empty value. @@ -930,14 +945,10 @@ Type *sema_analyse_function_signature(SemaContext *context, Decl *func_decl, Cal vec_add(types, params[i]->type); } - if (!all_ok) return NULL; + if (!all_ok) return false; Type *raw_type = type_get_func(signature, abi); - Type *type = type_new(TYPE_FUNC, func_decl->name); - type->canonical = type; - type->function.signature = signature; - type->function.module = func_decl->unit->module; type->function.prototype = raw_type->function.prototype; - return type; + return true; } static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl) @@ -947,10 +958,10 @@ static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool * if (decl->typedef_decl.is_func) { - Type *func_type = sema_analyse_function_signature(context, decl, CALL_C, &decl->typedef_decl.function_signature, false); - if (!func_type) return false; + Type *func_type = type_get_func_alias(decl->name, &decl->typedef_decl.function_signature, decl->unit->module); decl->type->canonical = type_get_ptr(func_type); - return true; + decl->resolve_status = RESOLVE_DONE; + return sema_analyse_function_signature(context, decl, CALL_C, func_type, false); } if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false; Type *type = decl->typedef_decl.type_info->type->canonical; @@ -968,10 +979,10 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl) { if (decl->distinct_decl.typedef_decl.is_func) { - Type *func_type = sema_analyse_function_signature(context, decl, CALL_C, &decl->distinct_decl.typedef_decl.function_signature, false); - if (!func_type) return false; + Type *func_type = type_get_func_alias(decl->name, &decl->distinct_decl.typedef_decl.function_signature, decl->unit->module); decl->distinct_decl.base_type = type_get_ptr(func_type); - return true; + decl->resolve_status = RESOLVE_DONE; + return sema_analyse_function_signature(context, decl, CALL_C, func_type, false); } TypeInfo *info = decl->distinct_decl.typedef_decl.type_info; if (!sema_resolve_type_info(context, info)) return false; @@ -2490,9 +2501,9 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *era } } - Type *func_type = sema_analyse_function_signature(context, decl, sig->abi, sig, true); - decl->type = func_type; - if (!func_type) return decl_poison(decl); + Type *func_type = decl->type = type_get_func_alias(decl->name, sig, decl->unit->module); + if (!sema_analyse_function_signature(context, decl, sig->abi, func_type, true)) return decl_poison(decl); + TypeInfo *rtype_info = type_infoptr(sig->rtype); assert(rtype_info); Type *rtype = rtype_info->type->canonical; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c417609bc..09a3af59d 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1644,19 +1644,18 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type if (!sema_call_analyse_invocation(context, expr, callee, &optional)) return false; + if (!type->function.prototype) + { + RETURN_SEMA_ERROR(expr, "Circular definition of %s.", type_quoted_error_string(type)); + } Type *rtype = type->function.prototype->rtype; if (is_unused && rtype != type_void) { - if (sig->attrs.nodiscard) - { - SEMA_ERROR(expr, "The result of the function must be used."); - return false; - } + if (sig->attrs.nodiscard) RETURN_SEMA_ERROR(expr, "The result of the function must be used."); if (type_is_optional(rtype) && !sig->attrs.maydiscard) { - SEMA_ERROR(expr, "The optional result of the function must be used."); - return false; + RETURN_SEMA_ERROR(expr, "The optional result of the function must be used."); } } @@ -6961,12 +6960,20 @@ INLINE bool lambda_parameter_match(Decl **ct_lambda_params, Decl *candidate) static inline Decl *sema_find_cached_lambda(SemaContext *context, Type *func_type, Decl *original, Decl **ct_lambda_parameters) { + unsigned cached = vec_size(original->func_decl.generated_lambda); if (!cached) return NULL; // If it has a function type, then we just use that for comparison. if (func_type) { - Type *raw = func_type->canonical->pointer->function.prototype->raw_type; + FunctionPrototype *prototype = func_type->canonical->pointer->function.prototype; + if (!prototype) + { + SEMA_ERROR(original, "Circular definition of %s.", type_quoted_error_string(func_type)); + return NULL; + } + + Type *raw = prototype->raw_type; FOREACH_BEGIN(Decl *candidate, original->func_decl.generated_lambda) if (raw == candidate->type->function.prototype->raw_type && lambda_parameter_match(ct_lambda_parameters, candidate)) return candidate; @@ -6974,16 +6981,16 @@ static inline Decl *sema_find_cached_lambda(SemaContext *context, Type *func_typ return NULL; } Signature *sig = &original->func_decl.signature; - if (!sig->rtype) return false; + if (!sig->rtype) return NULL; Type *rtype = sema_evaluate_type_copy(context, type_infoptr(sig->rtype)); - if (!rtype) return false; + if (!rtype) return NULL; Type *types[200]; types[0] = rtype; FOREACH_BEGIN_IDX(i, Decl *param, sig->params) TypeInfo *info = param->var.type_info; - if (!info) return false; + if (!info) return NULL; Type *type = sema_evaluate_type_copy(context, param->var.type_info); - if (!type) return false; + if (!type) return NULL; assert(i < 198); types[i + 1] = type; FOREACH_END(); @@ -7169,9 +7176,12 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *func_typ } scratch_buffer_append("$lambda"); scratch_buffer_append_unsigned_int(++unit->lambda_count); - decl->extname = decl->name = scratch_buffer_copy(); - Type *lambda_type = sema_analyse_function_signature(context, decl, sig->abi, sig, true); - if (!lambda_type) return false; + const char *name = decl->extname = decl->name = scratch_buffer_copy(); + + Type *lambda_type = type_get_func_alias(name, sig, decl->unit->module); + decl->type = lambda_type; + if (!sema_analyse_function_signature(context, decl, sig->abi, lambda_type, true)) return false; + decl->func_decl.lambda_ct_parameters = ct_lambda_parameters; decl->type = lambda_type; decl->func_decl.is_lambda = true; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index f6ab9bd1f..8cdce6d3a 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -79,7 +79,7 @@ bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt bool sema_bit_assignment_check(Expr *right, Decl *member); int sema_check_comp_time_bool(SemaContext *context, Expr *expr); bool sema_expr_check_assign(SemaContext *c, Expr *expr); -Type *sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Signature *signature, bool is_real_function); +bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Type *type, bool is_real_function); bool cast_widen_top_down(SemaContext *context, Expr *expr, Type *type); bool cast_promote_vararg(Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 460be5667..a0435debb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -3026,6 +3026,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } Signature *signature = &func->func_decl.signature; FunctionPrototype *prototype = func->type->function.prototype; + assert(prototype); context->call_env = (CallEnv) { .current_function = func, .kind = CALL_ENV_FUNCTION, diff --git a/src/compiler/types.c b/src/compiler/types.c index 5eec71483..0aa210336 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -227,6 +227,7 @@ const char *type_to_error_string(Type *type) return scratch_buffer_copy(); } case TYPE_FUNC: + if (!type->function.prototype) return type->name; scratch_buffer_clear(); scratch_buffer_append("fn "); type_append_func_to_scratch(type->function.prototype); @@ -1369,6 +1370,15 @@ static inline Type *func_create_new_func_proto(Signature *sig, CallABI abi, uint return type; } +Type *type_get_func_alias(const char *name, Signature *signature, Module *module) +{ + Type *type = type_new(TYPE_FUNC, name); + type->canonical = type; + type->function.signature = signature; + type->function.module = module; + return type; +} + Type *type_get_func(Signature *signature, CallABI abi) { uint32_t hash = hash_function(signature); diff --git a/test/test_suite/types/non_rec_fn.c3 b/test/test_suite/types/non_rec_fn.c3 new file mode 100644 index 000000000..668f6e137 --- /dev/null +++ b/test/test_suite/types/non_rec_fn.c3 @@ -0,0 +1,10 @@ + +def BlahFn = fn int(Foo f); + +struct Foo +{ + BlahFn x; +} + + +Foo g; \ No newline at end of file diff --git a/test/test_suite/types/recursive_fn.c3 b/test/test_suite/types/recursive_fn.c3 new file mode 100644 index 000000000..6f4dde2de --- /dev/null +++ b/test/test_suite/types/recursive_fn.c3 @@ -0,0 +1 @@ +def BlahFn = fn int(BlahFn f); // #error: 'BlahFn' has a circular definition \ No newline at end of file