From b09aa74f2f9dd2fb0a6c9cf618b945268eecd970 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 4 May 2024 23:34:37 +0200 Subject: [PATCH] Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192. --- releasenotes.md | 1 + src/compiler/enums.h | 1 + src/compiler/sema_decls.c | 58 +++++++++++++++++++++++---------------- src/compiler/sema_stmts.c | 7 ++++- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/releasenotes.md b/releasenotes.md index e5c98358d..75c422f48 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -12,6 +12,7 @@ ### Fixes - Incorrect length passed to scratch buffer printf. - Casting to a bitstruct would be allowed even if the type was the wrong size. +- Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192. ### Stdlib changes - Add 'zstr' variants for `string::new_format` / `string::tformat`. diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 530329493..123800569 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -366,6 +366,7 @@ typedef enum SCOPE_ENSURE_MACRO = 1 << 2, SCOPE_EXPR_BLOCK = 1 << 3, SCOPE_MACRO = 1 << 4, + SCOPE_COND = 1 << 5, } ScopeFlags; typedef enum diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6eb78f556..4aa1aa7a6 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3471,8 +3471,38 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, return new_module; } -static bool sema_append_generate_parameterized_name(SemaContext *c, Module *module, Expr **params, bool mangled) +static bool sema_generate_parameterized_name_to_scratch(SemaContext *c, Module *module, Expr **params, bool mangled) { + // First resolve + FOREACH_BEGIN_IDX(i, Expr *param, params) + if (param->expr_kind == EXPR_TYPEINFO) + { + TypeInfo *type_info = param->type_expr; + if (!sema_resolve_type_info(c, 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."); + if (type_is_void(type)) RETURN_SEMA_ERROR(type_info, "A 'void' type cannot be used as a parameter type."); + if (type_is_invalid_storage_type(type)) RETURN_SEMA_ERROR(type_info, "Expected a runtime type."); + if (type_is_func_ptr(type)) + { + if (!sema_resolve_type_decl(c, type->pointer)) return false; + } + } + else + { + if (!sema_analyse_ct_expr(c, param)) return false; + Type *type = param->type->canonical; + bool is_enum_like = type_kind_is_enumlike(type->type_kind); + if (!type_is_integer_or_bool_kind(type) && !is_enum_like) + { + SEMA_ERROR(param, "Only integer, bool, fault and enum values may be generic arguments."); + return poisoned_decl; + } + assert(expr_is_const(param)); + } + FOREACH_END(); + + scratch_buffer_clear(); if (mangled) { scratch_buffer_append_len(module->name->module, module->name->len); @@ -3489,36 +3519,20 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu } if (param->expr_kind == EXPR_TYPEINFO) { - TypeInfo *type_info = param->type_expr; - if (!sema_resolve_type_info(c, 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."); - if (type_is_void(type)) RETURN_SEMA_ERROR(type_info, "A 'void' type cannot be used as a parameter type."); - if (type_is_invalid_storage_type(type)) RETURN_SEMA_ERROR(type_info, "Expected a runtime type."); - if (type_is_func_ptr(type)) - { - if (!sema_resolve_type_decl(c, type->pointer)) return false; - } + Type *type = param->type_expr->type->canonical; if (mangled) { type_mangle_introspect_name_to_buffer(type); } else { - scratch_buffer_append(type_info->type->name); + scratch_buffer_append(type->name); } } else { - if (!sema_analyse_ct_expr(c, param)) return false; Type *type = param->type->canonical; bool is_enum_like = type_kind_is_enumlike(type->type_kind); - if (!type_is_integer_or_bool_kind(type) && !is_enum_like) - { - SEMA_ERROR(param, "Only integer, bool, fault and enum values may be generic arguments."); - return poisoned_decl; - } - assert(expr_is_const(param)); if (type == type_bool) { if (mangled) @@ -3656,8 +3670,7 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con vec_size(params)); return poisoned_decl; } - scratch_buffer_clear(); - if (!sema_append_generate_parameterized_name(c, module, params, true)) return poisoned_decl; + if (!sema_generate_parameterized_name_to_scratch(c, module, params, true)) return poisoned_decl; TokenType ident_type = TOKEN_IDENT; const char *path_string = scratch_buffer_interned(); Module *instantiated_module = global_context_find_module(path_string); @@ -3671,8 +3684,7 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con path->span = module->name->span; path->len = scratch_buffer.len; instantiated_module = module_instantiate_generic(c, module, path, params, span); - scratch_buffer_clear(); - if (!sema_append_generate_parameterized_name(c, module, params, false)) return poisoned_decl; + if (!sema_generate_parameterized_name_to_scratch(c, module, params, false)) return poisoned_decl; if (!instantiated_module) return poisoned_decl; instantiated_module->generic_suffix = scratch_buffer_copy(); if (c->unit->module->generic_module) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 889a62ad5..5ca07f9ac 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1025,7 +1025,12 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType assert(expr->expr_kind == EXPR_COND && "Conditional expressions should always be of type EXPR_DECL_LIST"); // 1. Analyse the declaration list. - if (!sema_analyse_cond_list(context, expr, cond_type)) return false; + ScopeFlags current_flags = context->active_scope.flags; + context->active_scope.flags |= SCOPE_COND; + bool success = sema_analyse_cond_list(context, expr, cond_type); + context->active_scope.flags = current_flags; + if (!success) return false; + // 2. If we get "void", either through a void call or an empty list, // signal that.