From 8184fba34bfe8af9d158dceb1cdff15c7b7d912f Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 14 Feb 2023 09:16:57 +0100 Subject: [PATCH] Delay type evaluation further. Current analysis shows that this code should be safe, but there may be some issues lurking. --- src/compiler/ast.c | 1 + src/compiler/compiler_internal.h | 4 +- src/compiler/sema_decls.c | 32 +++++++++---- src/compiler/sema_expr.c | 39 +++++++++++---- src/compiler/sema_internal.h | 19 +++++++- src/compiler/sema_name_resolution.c | 48 +++++++++++++++++++ src/compiler/sema_types.c | 17 ++----- src/compiler/types.c | 7 ++- src/version.h | 2 +- test/test_suite/generic/generic_cyclic.c3 | 17 +++++++ test/test_suite/generic/generic_recursion.c3t | 21 ++++++++ 11 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 test/test_suite/generic/generic_cyclic.c3 create mode 100644 test/test_suite/generic/generic_recursion.c3t diff --git a/src/compiler/ast.c b/src/compiler/ast.c index cdf066ec3..8f3c50188 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -275,6 +275,7 @@ Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span) decl->var.kind = kind; decl->type = type; decl->alignment = type ? type_alloca_alignment(type) : 0; + assert(!type || !type_is_user_defined(type) || type->decl->resolve_status == RESOLVE_DONE); decl->var.type_info = type_info_new_base(type, span); decl->resolve_status = RESOLVE_DONE; return decl; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2d04827c9..6e01a1b03 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2241,6 +2241,7 @@ Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); Decl *sema_find_extension_method_in_module(Decl **extensions, Type *type, const char *method_name); +bool sema_resolve_type_decl(SemaContext *context, Type *type); Decl *sema_find_symbol(SemaContext *context, const char *symbol); Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path); @@ -2305,6 +2306,7 @@ bool token_is_any_type(TokenType type); const char *token_type_to_string(TokenType type); #define IS_OPTIONAL(element_) (type_is_optional((element_)->type)) +#define IS_RESOLVED(element_) ((element_)->resolve_status == RESOLVE_DONE) bool type_is_comparable(Type *type); bool type_is_ordered(Type *type); unsigned type_get_introspection_kind(TypeKind kind); @@ -2333,7 +2335,7 @@ Type *type_get_vector(Type *vector_type, unsigned len); Type *type_get_vector_bool(Type *original_type); Type *type_int_signed_by_bitsize(BitSize bitsize); Type *type_int_unsigned_by_bitsize(BitSize bit_size); -TypeSize type_size(Type *type); +TypeSize type_size(Type *type); // Only call after all types are resolved. void type_init_cint(void); void type_func_prototype_init(uint32_t capacity); bool type_is_subtype(Type *type, Type *possible_subtype); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 718159507..77c47cbf4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -208,7 +208,8 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl, Decl ** SEMA_ERROR(member, "Scaled vector members not allowed in unions / structs."); return false; } - AlignSize member_alignment = type_abi_alignment(member->type); + AlignSize member_alignment; + if (!sema_set_abi_alignment(context, member->type, &member_alignment)) return false; ByteSize member_size = type_size(member->type); assert(member_size <= MAX_TYPE_SIZE); // Update max alignment @@ -334,7 +335,8 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl * if (!decl_ok(decl)) return false; - AlignSize member_natural_alignment = type_abi_alignment(member->type); + AlignSize member_natural_alignment; + if (!sema_set_abi_alignment(context, member->type, &member_natural_alignment)) return decl_poison(decl); AlignSize member_alignment = is_packed ? 1 : member_natural_alignment; Attr **attributes = member->attributes; @@ -800,7 +802,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) if (param->var.type_info) { param->type = param->var.type_info->type; - param->alignment = type_abi_alignment(param->type); + if (!sema_set_abi_alignment(context, param->type, ¶m->alignment)) return false; } if (param->var.init_expr) { @@ -839,6 +841,8 @@ Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallAB for (unsigned i = 0; i < param_count; i++) { + assert(IS_RESOLVED(params[i])); + assert(params[i]->type->canonical); vec_add(types, params[i]->type); } @@ -980,8 +984,7 @@ static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bo } *has_default = true; } - param->alignment = type_abi_alignment(param->type); - return true; + return sema_set_abi_alignment(context, param->type, ¶m->alignment); } static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) @@ -2315,7 +2318,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) bool pure = false; if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, NULL, &pure)) return decl_poison(decl); decl->func_decl.signature.attrs.is_pure = pure; - decl->alignment = type_alloca_alignment(decl->type); + if (!sema_set_alloca_alignment(context, decl->type, &decl->alignment)) return false; DEBUG_LOG("Function analysis done."); return true; } @@ -2621,7 +2624,10 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) } return decl_poison(decl); } - if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); + if (!decl->alignment) + { + 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); // Skip further evaluation. goto EXIT_OK; @@ -2661,7 +2667,10 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) { // Pre resolve to avoid problem with recursive definitions. decl->resolve_status = RESOLVE_DONE; - if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); + if (!decl->alignment) + { + if (!sema_set_alloca_alignment(context, decl->type, &decl->alignment)) return false; + } } CallEnvKind env_kind = context->call_env.kind; @@ -2706,7 +2715,10 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) } } EXIT_OK: - if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); + if (!decl->alignment) + { + if (!sema_set_alloca_alignment(context, decl->type, &decl->alignment)) return false; + } return true; } @@ -2911,7 +2923,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) path->span = module->name->span; path->len = scratch_buffer.len; instantiated_module = module_instantiate_generic(module, path, decl->define_decl.generic_params); - sema_analyze_stage(instantiated_module, c->unit->module->stage); + sema_analyze_stage(instantiated_module, c->unit->module->stage - 1); } if (global_context.errors_found) return decl_poison(decl); Decl *symbol = module_find_symbol(instantiated_module, name); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 80badeee0..1c09032c5 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1407,7 +1407,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call } else { - param->alignment = type_alloca_alignment(arg->type); + if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; } } break; @@ -1423,7 +1423,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call if (!param->alignment) { assert(callee.macro && "Only in the macro case should we need to insert the alignment."); - param->alignment = type_alloca_alignment(arg->type); + if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; } if (!sema_call_check_contract_param_match(context, param, arg)) return false; break; @@ -1729,7 +1729,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s return false; } } - if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type); + if (!body_arg->alignment) + { + if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false; + } } @@ -2769,8 +2772,10 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp if (!is_const) { + AlignSize align; + if (!sema_set_abi_alignment(context, parent->type, &align)) return false; if (sema_expr_rewrite_to_type_property(context, expr, canonical, type_property_by_name(name), - type_abi_alignment(parent->type), 0)) return true; + align, 0)) return true; } if (!type_may_have_sub_elements(canonical)) @@ -2779,6 +2784,8 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return false; } Decl *decl = canonical->decl; + if (!sema_analyse_decl(context, decl)) return false; + // TODO add more constants that can be inspected? // e.g. SomeEnum.values, MyUnion.x.offset etc? switch (decl->decl_kind) @@ -2825,9 +2832,11 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp if (member->decl_kind == DECL_VAR || member->decl_kind == DECL_UNION || member->decl_kind == DECL_STRUCT || member->decl_kind == DECL_BITSTRUCT) { expr->expr_kind = EXPR_CONST; + AlignSize align; + if (!sema_set_abi_alignment(context, decl->type, &align)) return false; expr->const_expr = (ExprConst) { .member.decl = member, - .member.align = type_abi_alignment(decl->type), + .member.align = align, .member.offset = decl_find_member_offset(decl, member), .const_kind = CONST_MEMBER }; @@ -3215,7 +3224,9 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp if (typeid->expr_kind == EXPR_CONST) { Type *type = typeid->const_expr.typeid; - return sema_expr_rewrite_to_type_property(context, expr, type, property, type_abi_alignment(type), 0); + AlignSize align; + if (!sema_set_abi_alignment(context, type, &align)) return false; + return sema_expr_rewrite_to_type_property(context, expr, type, property, align, 0); } switch (property) { @@ -3374,8 +3385,12 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, sema_expr_rewrite_to_type_nameof(expr, type, TOKEN_CT_QNAMEOF); return true; case TYPE_PROPERTY_ALIGNOF: - expr_rewrite_const_int(expr, type_usize, type_abi_alignment(type), true); + { + AlignSize align; + if (!sema_set_abi_alignment(context, type, &align)) return false; + expr_rewrite_const_int(expr, type_usize, align, true); return true; + } case TYPE_PROPERTY_EXTNAMEOF: if (type_is_builtin(type->type_kind)) return false; sema_expr_rewrite_to_type_nameof(expr, type, TOKEN_CT_EXTNAMEOF); @@ -6291,7 +6306,15 @@ static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr SEMA_ERROR(main_var, "Cannot use '$alignof' on type %s.", type_quoted_error_string(type)); return false; } - AlignSize align = decl && !decl_is_user_defined_type(decl) ? decl->alignment : type_abi_alignment(type); + AlignSize align; + if (decl && !decl_is_user_defined_type(decl)) + { + align = decl->alignment; + } + else + { + if (!sema_set_abi_alignment(context, type, &align)) return false; + } VECEACH(path, i) { ExprFlatElement *element = &path[i]; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index f086b99ec..a940d6b62 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -96,9 +96,24 @@ 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 *parent, CallABI abi, Signature *signature, 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); void cast_to_max_bit_size(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type); -bool cast_decay_array_pointers(SemaContext *context, Expr *expr); \ No newline at end of file +bool cast_decay_array_pointers(SemaContext *context, Expr *expr); +INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result); +INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSize *result); + +INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result) +{ + if (!sema_resolve_type_decl(context, type)) return false; + *result = type_abi_alignment(type); + return true; +} + +INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSize *result) +{ + if (!sema_resolve_type_decl(context, type)) return false; + *result = type_alloca_alignment(type); + return true; +} diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 353f30046..9565374fd 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -628,6 +628,54 @@ Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_ return sema_resolve_type_method(unit, type->type, method_name, ambiguous_ref, private_ref); } +bool sema_resolve_type_decl(SemaContext *context, Type *type) +{ + switch (type->type_kind) + { + case TYPE_OPTIONAL_ANY: + return true; + case TYPE_POISONED: + return false; + case TYPE_VOID: + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + case TYPE_ANY: + case TYPE_ANYERR: + case TYPE_TYPEID: + case TYPE_POINTER: + case TYPE_UNTYPED_LIST: + case TYPE_MEMBER: + case TYPE_INFERRED_VECTOR: + case TYPE_SCALED_VECTOR: + case TYPE_VECTOR: + case TYPE_SUBARRAY: + return true; + case TYPE_OPTIONAL: + return sema_resolve_type_decl(context, type->optional); + case TYPE_TYPEINFO: + UNREACHABLE + case TYPE_TYPEDEF: + return sema_resolve_type_decl(context, type->canonical); + case TYPE_DISTINCT: + if (!sema_analyse_decl(context, type->decl)) return false; + return sema_resolve_type_decl(context, type->decl->distinct_decl.base_type); + case TYPE_FUNC: + return true; + case TYPE_ENUM: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_BITSTRUCT: + case TYPE_FAULTTYPE: + return sema_analyse_decl(context, type->decl); + case TYPE_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + case TYPE_INFERRED_ARRAY: + return sema_resolve_type_decl(context, type->array.base); + } + UNREACHABLE +} + Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref) { assert(type == type->canonical); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 464a74531..3a7558ed0 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -32,22 +32,11 @@ bool sema_resolve_type_info(SemaContext *context, TypeInfo *type_info) bool sema_resolve_type_info_maybe_inferred(SemaContext *context, TypeInfo *type_info, bool allow_inferred_type) { - // Resolve the type non-shallow - if (!sema_resolve_type(context, type_info, allow_inferred_type, false)) return false; - - // What is the underlying non-optional type. - Type *type = type_no_optional(type_info->type); - - // usz and similar typedefs will not have a decl. - if (type->type_kind == TYPE_TYPEDEF && type->decl == NULL) return true; - - // If it is a basic type, then we're done. - if (!type_is_user_defined(type)) return true; - - // Otherwise analyse the underlying declaration. - return sema_analyse_decl(context, type->decl); + return sema_resolve_type(context, type_info, allow_inferred_type, false); } + + bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, ArraySize *len_ref) { // Get the expression describing the length. diff --git a/src/compiler/types.c b/src/compiler/types.c index ae8904e72..7fe290aee 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -270,9 +270,11 @@ RETRY: switch (type->type_kind) { case TYPE_BITSTRUCT: + assert(type->decl->resolve_status == RESOLVE_DONE); type = type->decl->bitstruct.base_type->type; goto RETRY; case TYPE_DISTINCT: + assert(type->decl->resolve_status == RESOLVE_DONE); type = type->decl->distinct_decl.base_type; goto RETRY; case TYPE_VECTOR: @@ -300,6 +302,7 @@ RETRY: type = type_iptr->canonical; goto RETRY; case TYPE_ENUM: + assert(type->decl->enums.type_info->resolve_status == RESOLVE_DONE); return type->decl->enums.type_info->type->canonical->builtin.bytesize; case TYPE_STRUCT: case TYPE_UNION: @@ -625,6 +628,7 @@ bool type_func_match(Type *fn_type, Type *rtype, unsigned arg_count, ...) va_end(ap); return true; } + AlignSize type_abi_alignment(Type *type) { RETRY: @@ -1034,9 +1038,10 @@ bool type_is_user_defined(Type *type) case TYPE_STRUCT: case TYPE_UNION: case TYPE_FAULTTYPE: - case TYPE_TYPEDEF: case TYPE_DISTINCT: return true; + case TYPE_TYPEDEF: + return type->decl != NULL; default: return false; } diff --git a/src/version.h b/src/version.h index d3429a0f6..6aa9b26df 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.61" \ No newline at end of file +#define COMPILER_VERSION "0.4.62" \ No newline at end of file diff --git a/test/test_suite/generic/generic_cyclic.c3 b/test/test_suite/generic/generic_cyclic.c3 new file mode 100644 index 000000000..8fda15c1a --- /dev/null +++ b/test/test_suite/generic/generic_cyclic.c3 @@ -0,0 +1,17 @@ +// #target: macos-x64 +module test; +import bar; + +define BazTest = Baz; + +struct Test // #error: Recursive definition of 'Test' +{ + BazTest t; +} + +module bar; + +struct Baz +{ + Test a; +} \ No newline at end of file diff --git a/test/test_suite/generic/generic_recursion.c3t b/test/test_suite/generic/generic_recursion.c3t new file mode 100644 index 000000000..2cffcd1a4 --- /dev/null +++ b/test/test_suite/generic/generic_recursion.c3t @@ -0,0 +1,21 @@ +// #target: macos-x64 +module test; +import std::io; +import std::collections::list; + +define TreeNodeList = List; + +struct TreeNode +{ + TreeNode* foo; + TreeNode* bar; + TreeNodeList list; +} + +TreeNode abc; + +/* #expect: test.ll + +%TreeNode = type { ptr, ptr, %List } +%List = type { i64, i64, ptr, ptr } +@test_abc = local_unnamed_addr global %TreeNode zeroinitializer, align 8