diff --git a/releasenotes.md b/releasenotes.md index 23798f64b..277920631 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -7,6 +7,7 @@ - Added `is_substruct` type property. - Scalar -> vector not implicit in call or assign. - Added `--vector-conv` to enable the old scalar->vector conversion behaviour. +- Added "weak" type aliases `def Foo = my_foo::Foo @weak;` ### Fixes None diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7efdb0a64..3a0cd3aef 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -56,6 +56,13 @@ extern const int manifest_default_keys_count; extern const char *manifest_target_keys[][2]; extern const int manifest_target_keys_count; +typedef enum BoolErr__ +{ + BOOL_ERR = -1, + BOOL_FALSE = 0, + BOOL_TRUE = 1, +} BoolErr; + typedef struct Ast_ Ast; typedef struct Decl_ Decl; typedef struct TypeInfo_ TypeInfo; @@ -601,6 +608,7 @@ typedef struct typedef struct { bool is_func : 1; + bool is_redef : 1; union { Decl *decl; @@ -1887,6 +1895,7 @@ typedef struct Decl *ambiguous_other_decl; Decl *private_decl; Decl *maybe_decl; + Decl *found; Path *path; SourceSpan span; const char *symbol; @@ -2373,7 +2382,7 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl Decl *sema_decl_stack_resolve_symbol(const char *symbol); Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name); -Decl *unit_resolve_parameterized_symbol(SemaContext *context, CompilationUnit *unit, NameResolve *name_resolve); +bool unit_resolve_parameterized_symbol(SemaContext *context, NameResolve *name_resolve); 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_list(Decl **extensions, Type *type, const char *method_name); @@ -2384,7 +2393,7 @@ Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path Decl *sema_find_label_symbol(SemaContext *context, const char *symbol); Decl *sema_find_label_symbol_anywhere(SemaContext *context, const char *symbol); Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span); -bool sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol); +BoolErr sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol); bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, ArraySize *len_ref); @@ -2489,7 +2498,6 @@ Type *type_find_parent_type(Type *type); bool type_is_subtype(Type *type, Type *possible_subtype); bool type_is_abi_aggregate(Type *type); bool type_is_int128(Type *type); -Decl * type_no_export(Type *type); Type *type_from_token(TokenType type); bool type_is_user_defined(Type *type); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 664d5dfda..3b6ee32d9 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1819,6 +1819,11 @@ static inline Decl *parse_def_type(ParseContext *c) decl->typedef_decl.is_func = false; decl->decl_kind = DECL_TYPEDEF; decl_add_type(decl, TYPE_TYPEDEF); + if (type_info->kind == TYPE_INFO_IDENTIFIER && type_info->resolve_status == RESOLVE_NOT_DONE + && type_info->unresolved.name == decl->name) + { + decl->typedef_decl.is_redef = true; + } if (!parse_attributes_for_global(c, decl)) return poisoned_decl; RANGE_EXTEND_PREV(decl); diff --git a/src/compiler/sema_asm.c b/src/compiler/sema_asm.c index 350cfecb3..c86f42f72 100644 --- a/src/compiler/sema_asm.c +++ b/src/compiler/sema_asm.c @@ -285,6 +285,7 @@ static inline bool sema_check_asm_var(SemaContext *context, AsmInlineBlock *bloc const char *name = arg->ident.name; Decl *decl = sema_resolve_symbol(context, name, NULL, expr->span); if (!decl) return false; + assert(arg->kind == ASM_ARG_REGVAR); arg->ident.ident_decl = decl; if (decl->decl_kind != DECL_VAR) @@ -382,13 +383,11 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b arg->ident.ident_decl = decl; if (decl->decl_kind != DECL_VAR) { - SEMA_ERROR(expr, "Expected a global or local variable."); - return false; + RETURN_SEMA_ERROR(expr, "Expected a global or local variable."); } if (IS_OPTIONAL(decl)) { - SEMA_ERROR(expr, "Optional variables are not allowed in asm."); - return false; + RETURN_SEMA_ERROR(expr, "Optional variables are not allowed in asm."); } bool is_write = arg_type.is_write; bool is_read = !arg_type.is_write || arg_type.is_readwrite; @@ -398,8 +397,7 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b decl->var.is_read = true; if (decl->var.out_param) { - SEMA_ERROR(expr, "An 'out' variable may not be read from."); - return false; + RETURN_SEMA_ERROR(expr, "An 'out' variable may not be read from."); } asm_reg_add_input(block, arg); } @@ -408,15 +406,13 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b decl->var.is_written = true; if (decl->var.in_param) { - SEMA_ERROR(expr, "An 'in' variable may not be written to."); - return false; + RETURN_SEMA_ERROR(expr, "An 'in' variable may not be written to."); } asm_reg_add_output(block, arg); } if (!arg_type.is_address) { - SEMA_ERROR(expr, "This slot does not accept an address."); - return false; + RETURN_SEMA_ERROR(expr, "This slot does not accept an address."); } return true; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c4be15d4e..1f5d08fbb 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2380,7 +2380,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_UNUSED] = (AttributeDomain)~(ATTR_CALL), [ATTRIBUTE_USED] = (AttributeDomain)~(ATTR_CALL), [ATTRIBUTE_WASM] = ATTR_FUNC, - [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, + [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL | ATTR_DEF, [ATTRIBUTE_WINMAIN] = ATTR_FUNC, }; // NOLINTEND(*.EnumCastOutOfRange) @@ -2628,6 +2628,14 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, decl->func_decl.signature.attrs.noreturn = true; break; case ATTRIBUTE_WEAK: + if (domain == ATTR_DEF) + { + if (decl->decl_kind != DECL_TYPEDEF) RETURN_SEMA_ERROR(attr, "'@weak' can only be used on type aliases."); + if (!decl->typedef_decl.is_redef) + { + RETURN_SEMA_ERROR(attr, "'@weak' is only allowed on type aliases with the same name, eg 'def Foo = bar::def::Foo @weak'."); + } + } decl->is_weak = true; break; case ATTRIBUTE_WASM: @@ -4032,9 +4040,9 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con .symbol = name }; - Decl *alias = unit_resolve_parameterized_symbol(NULL, c->unit, &name_resolve); - if (!decl_ok(alias)) return poisoned_decl; - + if (!unit_resolve_parameterized_symbol(c, &name_resolve)) return poisoned_decl; + Decl *alias = name_resolve.found; + assert(alias); Module *module = decl_module(alias); unsigned parameter_count = vec_size(module->parameters); assert(parameter_count > 0); @@ -4127,6 +4135,7 @@ static inline bool sema_analyse_define(SemaContext *c, Decl *decl, bool *erase_d if (decl->define_decl.define_kind == DEFINE_IDENT_ALIAS) { Decl *symbol = sema_resolve_symbol(c, decl->define_decl.ident, decl->define_decl.path, decl->define_decl.span); + if (!symbol) return false; if (!sema_analyse_decl(c, symbol)) return false; decl->type = symbol->type; decl->define_decl.alias = symbol; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5c19e6178..1124cb418 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1002,7 +1002,7 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, { decl = sema_resolve_symbol(context, expr->identifier_expr.ident, expr->identifier_expr.path, expr->span); (void)decl; - assert(!decl_ok(decl)); + assert(!decl); return false; } @@ -1081,7 +1081,7 @@ static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *e Decl *decl = sema_resolve_symbol(context, expr->ct_ident_expr.identifier, NULL, expr->span); // Already handled - if (!decl_ok(decl)) + if (!decl) { return expr_poison(expr); } @@ -1102,7 +1102,7 @@ static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Type Decl *decl = sema_resolve_symbol(context, expr->hash_ident_expr.identifier, NULL, expr->span); // Already handled - if (!decl_ok(decl)) return expr_poison(expr); + if (!decl) return expr_poison(expr); assert(decl->decl_kind == DECL_VAR); expr_replace(expr, copy_expr_single(decl->var.init_expr)); @@ -3180,7 +3180,7 @@ RETRY: { assert(child->resolve_status != RESOLVE_DONE); Decl *decl = sema_resolve_symbol(context, child->hash_ident_expr.identifier, NULL, child->span); - if (!decl_ok(decl)) return NULL; + if (!decl) return NULL; Expr *expr = copy_expr_single(decl->var.init_expr); return sema_expr_resolve_access_child(decl->var.hash_var.context, expr, missing); } @@ -5096,8 +5096,7 @@ static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Ex if (!decl) { - SEMA_ERROR(info, "'%s' is not defined in this scope yet.", info->unresolved.name); - return false; + RETURN_SEMA_ERROR(info, "'%s' is not defined in this scope yet.", info->unresolved.name); } decl->var.init_expr = right; expr->expr_kind = EXPR_NOP; @@ -8203,8 +8202,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_HASH_IDENT: { Decl *decl = sema_resolve_symbol(active_context, main_expr->hash_ident_expr.identifier, NULL, main_expr->span); - if (!decl_ok(decl)) return false; - if (!decl) RETURN_SEMA_ERROR(list[i], "No parameter '%s' found.", main_expr->hash_ident_expr.identifier); + if (!decl) return false; main_expr = copy_expr_single(decl->var.init_expr); goto RETRY; } @@ -8230,8 +8228,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_CT_IDENT: { Decl *decl = sema_resolve_symbol(active_context, main_expr->ct_ident_expr.identifier, NULL, main_expr->span); - if (!decl_ok(decl)) return false; - success = decl != NULL; + if (!decl) return false; break; } case EXPR_CALL: @@ -8794,7 +8791,7 @@ static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *ex // Only hash ident style stringify reaches here. assert(inner->expr_kind == EXPR_HASH_IDENT); Decl *decl = sema_resolve_symbol(context, inner->ct_ident_expr.identifier, NULL, inner->span); - if (!decl_ok(decl)) return false; + if (!decl) return false; const char *desc = span_to_string(decl->var.hash_var.span); if (!desc) { diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 624da2b3e..e5b7693ef 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -9,6 +9,7 @@ typedef long long int ssize_t; #endif +INLINE bool sema_resolve_ambiguity(SemaContext *context, Decl **current, Decl *candidate, Decl **ambiguous); static inline bool matches_subpath(Path *path_to_check, Path *path_to_find) { // This checks the full match. @@ -124,13 +125,13 @@ static inline Decl *sema_find_decl_in_module(Module *module, Path *path, const c return module_find_symbol(module, symbol); } -static Decl *sema_find_decl_in_private_imports(Decl **imports, NameResolve *name_resolve, bool want_generic) +static bool sema_find_decl_in_private_imports(SemaContext *context, NameResolve *name_resolve, bool want_generic) { Decl *decl = NULL; // 1. Loop over imports. Path *path = name_resolve->path; const char *symbol = name_resolve->symbol; - FOREACH(Decl *, import, imports) + FOREACH(Decl *, import, context->unit->imports) { if (import->import.module->is_generic != want_generic) continue; if (!import->import.import_private_as_public) continue; @@ -147,16 +148,8 @@ static Decl *sema_find_decl_in_private_imports(Decl **imports, NameResolve *name { if (!path) { - // Prefer already found builtin over newly found non-builtin - if (decl->is_autoimport && !found->is_autoimport) continue; - // Prefer new builtin over non-builtin - if (found->is_autoimport && !decl->is_autoimport) - { - decl = found; - name_resolve->private_decl = NULL; - name_resolve->ambiguous_other_decl = NULL; - continue; - } + if (!sema_resolve_ambiguity(context, &decl, found, &name_resolve->ambiguous_other_decl)) return false; + continue; } // 11. Then set an ambiguous match. name_resolve->ambiguous_other_decl = found; @@ -167,7 +160,8 @@ static Decl *sema_find_decl_in_private_imports(Decl **imports, NameResolve *name decl = found; name_resolve->private_decl = NULL; } - return decl; + name_resolve->found = decl; + return true; } static inline bool sema_is_path_found(Module **modules, Path *path, bool want_generic) @@ -238,13 +232,97 @@ static bool decl_is_visible(CompilationUnit *unit, Decl *decl) return false; } -static bool sema_first_is_preferred(Decl *decl, Decl *decl2) +INLINE Type *sema_fold_weak(SemaContext *context, Decl *decl) { - return (decl->is_autoimport && !decl2->is_autoimport) - || (decl2->unit->module->generic_module && !decl->unit->module->generic_module); + while (decl->is_weak) + { + if (decl->resolve_status != RESOLVE_DONE) + { + if (!sema_analyse_decl(context, decl)) return NULL; + } + Type *type = decl->typedef_decl.type_info->type; + if (type->type_kind != TYPE_TYPEDEF) return type; + decl = type->decl; + } + return decl->type; } -static Decl *sema_find_decl_in_global(CompilationUnit *unit, DeclTable *table, Module **module_list, NameResolve *name_resolve, bool want_generic) +/** + * We want to prefer abc::Foo over bcd::Foo if: + * (1) abc::Foo is autoimported and bcd::Foo isn't. + * (2) abc::Foo is from a normal module and bcd::Foo is from a generic module. + * (3) Folding bcd::Foo to it's @weak result gives the same as folding abc::Foo to its @weak type. + * + * @param context + * @param decl + * @param decl2 + * @return + */ +static BoolErr sema_first_is_preferred(SemaContext *context, Decl *decl, Decl *decl2) +{ + // (1) and (2) + if ((decl->is_autoimport && !decl2->is_autoimport) + || (decl2->unit->module->generic_module && !decl->unit->module->generic_module)) return BOOL_TRUE; + // Now analyse common parents, we only check if this is a redef. + if (decl2->decl_kind != DECL_TYPEDEF || !decl2->is_weak) return BOOL_FALSE; + + Type *weak2 = sema_fold_weak(context, decl2); + if (!weak2) return BOOL_ERR; + + // Fast path: do the types match? + if (weak2 == decl->type) return BOOL_TRUE; + + // If we can't fold the decl then we're done. + if (decl->decl_kind != DECL_TYPEDEF || !decl->is_weak) return BOOL_FALSE; + + Type *weak = sema_fold_weak(context, decl); + if (!weak) return BOOL_ERR; + + // Both fold to the same + if (weak == weak2) return BOOL_TRUE; + + // Otherwise we fail. + return BOOL_FALSE; +} + +INLINE bool sema_resolve_ambiguity(SemaContext *context, Decl **current, Decl *candidate, Decl **ambiguous) +{ + Decl *original = *current; + if (!original) + { + *current = candidate; + return true; + } + // The candidate is preferred + BoolErr preferred = sema_first_is_preferred(context, candidate, original); + if (preferred == BOOL_ERR) return false; + if (preferred == BOOL_TRUE) + { + // Clear any ambiguity + *ambiguous = NULL; + *current = candidate; + return true; + } + // We already have something ambiguous, so keep that. + if (*ambiguous) return true; + // If the original is preferred over the candidate, then we just + // keep the original and there is no ambiguity: + switch (sema_first_is_preferred(context, original, candidate)) + { + case BOOL_FALSE: + // Otherwise we have an ambiguity + *ambiguous = candidate; + FALLTHROUGH; + case BOOL_TRUE: + return true; + case BOOL_ERR: + return false; + } + UNREACHABLE; +} + +static bool sema_find_decl_in_global(SemaContext *context, DeclTable *table, Module **module_list, + NameResolve *name_resolve, bool want_generic) { const char *symbol = name_resolve->symbol; Path *path = name_resolve->path; @@ -255,21 +333,25 @@ static Decl *sema_find_decl_in_global(CompilationUnit *unit, DeclTable *table, M { // Update the path found if (path && !name_resolve->path_found) name_resolve->path_found = sema_is_path_found(module_list, path, want_generic); - return NULL; + name_resolve->found = NULL; + return true; } Decl *decls = declptr(decl_ids); // There might just be a single match. if (decls->decl_kind != DECL_DECLARRAY) { - if (path && !matches_subpath(decl_module(decls)->name, path)) return NULL; - if (!decl_is_visible(unit, decls)) + if (path && !matches_subpath(decl_module(decls)->name, path)) return true; + if (!decl_is_visible(context->unit, decls)) { name_resolve->maybe_decl = decls; - return NULL; } - name_resolve->private_decl = NULL; - return decls; + else + { + name_resolve->private_decl = NULL; + name_resolve->found = decls; + } + return true; } // Else go through the list @@ -278,50 +360,27 @@ static Decl *sema_find_decl_in_global(CompilationUnit *unit, DeclTable *table, M FOREACH(Decl *, candidate, decls->decl_list) { if (path && !matches_subpath(decl_module(candidate)->name, path)) continue; - if (!decl_is_visible(unit, candidate)) + if (!decl_is_visible(context->unit, candidate)) { maybe_decl = candidate; continue; } - if (ambiguous) - { - if (sema_first_is_preferred(candidate, decl)) - { - ambiguous = NULL; - decl = candidate; - } - } - else - { - ambiguous = decl; - decl = candidate; - if (ambiguous) - { - // If we have a same match but one is builtin, prefer builtin. - if (sema_first_is_preferred(decl, ambiguous)) - { - ambiguous = NULL; - } - else if (sema_first_is_preferred(ambiguous, decl)) - { - decl = ambiguous; - ambiguous = NULL; - } - } - } + if (!sema_resolve_ambiguity(context, &decl, candidate, &ambiguous)) return false; } name_resolve->ambiguous_other_decl = ambiguous; name_resolve->private_decl = NULL; + name_resolve->found = decl; name_resolve->maybe_decl = maybe_decl; - return decl; + return true; } -static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_resolve) +static bool sema_resolve_path_symbol(SemaContext *context, NameResolve *name_resolve) { Path *path = name_resolve->path; name_resolve->ambiguous_other_decl = NULL; Decl *decl = NULL; name_resolve->path_found = false; + name_resolve->found = NULL; assert(name_resolve->path && "Expected path."); const char *symbol = name_resolve->symbol; @@ -329,7 +388,8 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re if (path->module == global_context.std_module_path.module) { name_resolve->path_found = true; - return module_find_symbol(&global_context.std_module, symbol); + name_resolve->found = module_find_symbol(&global_context.std_module, symbol); + return true; } CompilationUnit *unit = context->unit; @@ -338,15 +398,17 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re if (matches_subpath(unit->module->name, path)) { // 2. If so try to locally get the symbol. - if ((decl = module_find_symbol(unit->module, symbol))) return decl; + if ((name_resolve->found = module_find_symbol(unit->module, symbol))) return true; name_resolve->path_found = true; } // 3. Loop over imports. - decl = sema_find_decl_in_private_imports(unit->imports, name_resolve, false); + if (!sema_find_decl_in_private_imports(context, name_resolve, false)) return false; // 4. Go to global search - return decl ? decl : sema_find_decl_in_global(unit, &global_context.symbols, global_context.module_list, name_resolve, false); + if (name_resolve->found) return true; + return sema_find_decl_in_global(context, &global_context.symbols, global_context.module_list, + name_resolve, false); } static inline Decl *sema_find_ct_local(SemaContext *context, const char *symbol) @@ -387,37 +449,48 @@ static inline Decl *sema_find_local(SemaContext *context, const char *symbol) return NULL; } -static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_resolve) +static bool sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_resolve) { const char *symbol = name_resolve->symbol; assert(name_resolve->path == NULL); - Decl *decl = sema_find_local(context, symbol); - if (decl) return decl; + Decl *decl; + + if ((decl = sema_find_local(context, symbol))) + { + name_resolve->found = decl; + return true; + } CompilationUnit *unit = context->unit; // Search in file scope. - decl = htable_get(&unit->local_symbols, (void *) symbol); - - if (decl) return decl; + if ((decl = htable_get(&unit->local_symbols, (void *) symbol))) + { + name_resolve->found = decl; + return true; + } // Search in the module. - decl = module_find_symbol(unit->module, symbol); + if ((decl = module_find_symbol(unit->module, symbol))) + { + name_resolve->found = decl; + return true; + } - if (decl) return decl; + if (!sema_find_decl_in_private_imports(context, name_resolve, false)) return false; + if (name_resolve->found) return true; - decl = sema_find_decl_in_private_imports(unit->imports, name_resolve, false); - - return decl ? decl : sema_find_decl_in_global(context->unit, &global_context.symbols, NULL, name_resolve, false); + return sema_find_decl_in_global(context, &global_context.symbols, NULL, name_resolve, false); } -static void sema_report_error_on_decl(SemaContext *context, Decl *found, NameResolve *name_resolve) +static void sema_report_error_on_decl(SemaContext *context, NameResolve *name_resolve) { assert(!name_resolve->suppress_error); const char *symbol = name_resolve->symbol; SourceSpan span = name_resolve->span; + Decl *found = name_resolve->found; const char *path_name = name_resolve->path ? name_resolve->path->module : NULL; if (!found && name_resolve->private_decl) { @@ -491,18 +564,17 @@ static void sema_report_error_on_decl(SemaContext *context, Decl *found, NameRes } } -INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_resolve) +INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_resolve) { name_resolve->ambiguous_other_decl = NULL; name_resolve->private_decl = NULL; name_resolve->path_found = false; - Decl *decl; if (name_resolve->path) { - decl = sema_resolve_path_symbol(context, name_resolve); - if (!decl && !name_resolve->maybe_decl && !name_resolve->path_found) + if (!sema_resolve_path_symbol(context, name_resolve)) return false; + if (!name_resolve->found && !name_resolve->maybe_decl && !name_resolve->path_found) { - if (name_resolve->suppress_error) return NULL; + if (name_resolve->suppress_error) return true; bool path_found = false; Module *module_with_path = NULL; FOREACH(Module *, module, global_context.module_list) @@ -519,42 +591,39 @@ INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_ { if (matches_subpath(module->name, name_resolve->path)) { - SEMA_ERROR(name_resolve->path, "%s is a generic module, did you forget to add the generic parameter(s) (<...>) after '%s'?", - module->name->module, name_resolve->symbol); - return poisoned_decl; + RETURN_SEMA_ERROR(name_resolve->path, + "%s is a generic module, did you forget to add the generic parameter(s) (<...>) after '%s'?", + module->name->module, name_resolve->symbol); } } } if (module_with_path) { - SEMA_ERROR(name_resolve, "'%s' could not be found in %s.", name_resolve->symbol, module_with_path->name->module); + RETURN_SEMA_ERROR(name_resolve, "'%s' could not be found in %s.", name_resolve->symbol, module_with_path->name->module); } - else - { - SEMA_ERROR(name_resolve->path, "Unknown module '%.*s', did you type it right?", name_resolve->path->len, name_resolve->path->module); - } - return poisoned_decl; + RETURN_SEMA_ERROR(name_resolve->path, "Unknown module '%.*s', did you type it right?", name_resolve->path->len, name_resolve->path->module); } } else { - decl = sema_resolve_no_path_symbol(context, name_resolve); + if (!sema_resolve_no_path_symbol(context, name_resolve)) return false; } - if (!decl || name_resolve->ambiguous_other_decl) + Decl *found = name_resolve->found; + if (!found || name_resolve->ambiguous_other_decl) { - if (name_resolve->suppress_error) return NULL; - sema_report_error_on_decl(context, decl, name_resolve); - return poisoned_decl; + if (name_resolve->suppress_error) return name_resolve->found = NULL, true; + sema_report_error_on_decl(context, name_resolve); + return false; } - unit_register_external_symbol(context, decl); - if (decl->is_if && context->call_env.in_if_resolution.a) + unit_register_external_symbol(context, found); + if (found->is_if && context->call_env.in_if_resolution.a) { - sema_error_at(context, context->call_env.in_if_resolution, "This @if expression is dependent on '%s' which is also conditional.", decl->name); - SEMA_NOTE(decl, "'%s' is defined here.", decl->name); - return poisoned_decl; + sema_error_at(context, context->call_env.in_if_resolution, "This @if expression is dependent on '%s' which is also conditional.", found->name); + SEMA_NOTE(found, "'%s' is defined here.", found->name); + return false; } - return decl; + return true; } Decl *sema_find_extension_method_in_list(Decl **extensions, Type *type, const char *method_name) @@ -781,45 +850,45 @@ Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *me return found; } -Decl *unit_resolve_parameterized_symbol(SemaContext *context, CompilationUnit *unit, NameResolve *name_resolve) +bool unit_resolve_parameterized_symbol(SemaContext *context, NameResolve *name_resolve) { name_resolve->ambiguous_other_decl = NULL; name_resolve->private_decl = NULL; name_resolve->path_found = false; - Decl *decl = sema_find_decl_in_private_imports(unit->imports, name_resolve, true); - - if (!decl) + if (!sema_find_decl_in_private_imports(context, name_resolve, true)) return false; + if (!name_resolve->found) { - decl = sema_find_decl_in_global(unit, &global_context.generic_symbols, + if (!sema_find_decl_in_global(context, &global_context.generic_symbols, global_context.generic_module_list, - name_resolve, true); + name_resolve, true)) return false; } // 14. Error report - if (!decl || name_resolve->ambiguous_other_decl) + if (!name_resolve->found || name_resolve->ambiguous_other_decl) { - if (name_resolve->suppress_error) return poisoned_decl; - sema_report_error_on_decl(context, decl, name_resolve); - return poisoned_decl; + if (name_resolve->suppress_error) return name_resolve->found = NULL, false; + sema_report_error_on_decl(context, name_resolve); + return false; } - if (!decl_is_user_defined_type(decl) && !name_resolve->path) + if (!decl_is_user_defined_type(name_resolve->found) && !name_resolve->path) { - if (name_resolve->suppress_error) return poisoned_decl; - SEMA_ERROR(name_resolve, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", name_resolve->symbol); - return poisoned_decl; + if (name_resolve->suppress_error) return false; + RETURN_SEMA_ERROR(name_resolve, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", name_resolve->symbol); } - - return decl; + return true; } - +/** + * Silently find a symbol, will return NULL, Poison or the value + */ Decl *sema_find_symbol(SemaContext *context, const char *symbol) { NameResolve resolve = { .suppress_error = true, .symbol = symbol, }; - return sema_resolve_symbol_common(context, &resolve); + if (!sema_resolve_symbol_common(context, &resolve)) return poisoned_decl; + return resolve.found; } Decl *sema_find_label_symbol(SemaContext *context, const char *symbol) @@ -850,21 +919,22 @@ Decl *sema_find_label_symbol_anywhere(SemaContext *context, const char *symbol) return NULL; } -bool sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol) +BoolErr sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol) { NameResolve resolve = { .suppress_error = true, .symbol = symbol, }; - Decl *decl = sema_resolve_symbol_common(c, &resolve); + if (!sema_resolve_symbol_common(c, &resolve)) return BOOL_ERR; // Unknown symbol => not defined - if (!decl) return false; + Decl *found = resolve.found; + if (!found) return BOOL_FALSE; // Defined in the same module => defined - if (decl_module(decl) == c->unit->module) return true; + if (decl_module(found) == c->unit->module) return BOOL_TRUE; // Not a variable or function => defined - if (decl->decl_kind != DECL_VAR && decl->decl_kind != DECL_FUNC) return true; + if (found->decl_kind != DECL_VAR && found->decl_kind != DECL_FUNC) return BOOL_TRUE; // Otherwise defined only if autoimport. - return decl->is_autoimport; + return found->is_autoimport ? BOOL_TRUE : BOOL_FALSE; } Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path) @@ -874,9 +944,14 @@ Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path .symbol = symbol, .path = path }; - return sema_resolve_symbol_common(context, &resolve); + if (!sema_resolve_symbol_common(context, &resolve)) return poisoned_decl; + return resolve.found; } +/** + * Resolves a symbol, return NULL if an error was found (and signalled), + * otherwise the decl. + */ Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span) { NameResolve resolve = { @@ -884,7 +959,11 @@ Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, .path = path, .span = span }; - return sema_resolve_symbol_common(context, &resolve); + if (!sema_resolve_symbol_common(context, &resolve)) return NULL; + Decl *found = resolve.found; + assert(found); + if (!decl_ok(found)) return NULL; + return resolve.found; } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9bb374e07..f27cb816e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -738,7 +738,9 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) // 1. Check if we are doing an implicit declaration. if (!var_type && ident->expr_kind == EXPR_IDENTIFIER) { - implicit_declaration = !sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); + BoolErr res = sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); + if (res == BOOL_ERR) return false; + implicit_declaration = res == BOOL_FALSE; } // 2. If we have a type for the variable, resolve it. @@ -885,7 +887,9 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) } if (!type && ident->expr_kind == EXPR_IDENTIFIER) { - implicit_declaration = !sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); + BoolErr res = sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); + if (res == BOOL_ERR) return false; + implicit_declaration = res == BOOL_FALSE; } if (!type && !implicit_declaration) @@ -993,8 +997,7 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond case EXPR_CATCH_UNWRAP: if (cond_type != COND_TYPE_UNWRAP_BOOL && cond_type != COND_TYPE_UNWRAP) { - SEMA_ERROR(expr, "Catch unwrapping is only allowed inside of a 'while' or 'if' conditional, maybe '@catch()' will do what you need?"); - return false; + RETURN_SEMA_ERROR(expr, "Catch unwrapping is only allowed inside of a 'while' or 'if' conditional, maybe '@catch()' will do what you need?"); } return sema_analyse_catch_unwrap(context, expr); default: @@ -1013,7 +1016,9 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond // Does the identifier exist in the parent scope? // then again it can't be an any unwrap. - if (sema_symbol_is_defined_in_scope(context, left->identifier_expr.ident)) goto NORMAL_EXPR; + BoolErr defined_in_scope = sema_symbol_is_defined_in_scope(context, left->identifier_expr.ident); + if (defined_in_scope == BOOL_ERR) return false; + if (defined_in_scope == BOOL_TRUE) goto NORMAL_EXPR; Expr *right = exprptr(expr->binary_expr.right); bool is_deref = right->expr_kind == EXPR_UNARY && right->unary_expr.operator == UNARYOP_DEREF; diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 0f679bdb3..83b73fdaf 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -202,12 +202,8 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in } Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name, type_info->unresolved.path, type_info->span); - assert(decl); // Already handled - if (!decl_ok(decl)) - { - return type_info_poison(type_info); - } + if (!decl) return type_info_poison(type_info); decl = decl_flatten(decl); switch (decl->decl_kind) diff --git a/src/compiler/types.c b/src/compiler/types.c index 6b9d83890..7b74f4efb 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -384,58 +384,6 @@ bool type_flat_is_boolintlike(Type *type) return kind == TYPE_BOOL || (kind >= TYPE_INTEGER_FIRST && kind <= TYPE_INTEGER_LAST); } -Decl *type_no_export(Type *type) -{ - RETRY: - switch (type->type_kind) - { - case TYPE_POISONED: - case TYPE_UNTYPED_LIST: - case TYPE_WILDCARD: - case TYPE_MEMBER: - case TYPE_TYPEINFO: - return NULL; - case TYPE_VOID: - case TYPE_BOOL: - case ALL_INTS: - case ALL_FLOATS: - case TYPE_ANY: - case TYPE_INTERFACE: - case TYPE_ANYFAULT: - case TYPE_TYPEID: - return NULL; - case TYPE_POINTER: - type = type->pointer; - goto RETRY; - case TYPE_FUNC_PTR: - type = type->pointer; - FALLTHROUGH; - case TYPE_FUNC_RAW: - case TYPE_ENUM: - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_FAULTTYPE: - case TYPE_DISTINCT: - case TYPE_BITSTRUCT: - case TYPE_TYPEDEF: - if (!type->decl || type->decl->is_export) return NULL; - return type->decl; - case TYPE_SLICE: - case TYPE_ARRAY: - case TYPE_FLEXIBLE_ARRAY: - case TYPE_INFERRED_ARRAY: - type = type->array.base; - goto RETRY; - case TYPE_VECTOR: - case TYPE_INFERRED_VECTOR: - return NULL; - case TYPE_OPTIONAL: - type = type->optional; - goto RETRY; - } - UNREACHABLE -} - bool type_is_int128(Type *type) { diff --git a/test/test_suite/define/weak_alias_fails.c3 b/test/test_suite/define/weak_alias_fails.c3 new file mode 100644 index 000000000..522da97e6 --- /dev/null +++ b/test/test_suite/define/weak_alias_fails.c3 @@ -0,0 +1,32 @@ +module abc; +import std::math; +def Vec2f = std::math::vector::Vec2f @weak; +fn Vec2f foo(Vec2f a) { return a * 2; } + +module gog; +import std::math; +def Vec2f = std::math::vector::Vec2f; + +module deef; +import gog; + +def Vec2f = gog::Vec2f @weak; +fn Vec2f foo(Vec2f a) { return a * 2; } + +module test; +import abc; +import std::math; +fn void test() +{ + Vec2f a; + abc::foo(a); +} + +module test2; +import abc; +import deef; +fn void test2() +{ + Vec2f a; // #error: abc::Vec2f or deef::Vec2f + abc::foo(a); +} \ No newline at end of file diff --git a/test/test_suite/define/weak_aliases.c3 b/test/test_suite/define/weak_aliases.c3 new file mode 100644 index 000000000..591b8c029 --- /dev/null +++ b/test/test_suite/define/weak_aliases.c3 @@ -0,0 +1,32 @@ +module abc; +import std::math; +def Vec2f = std::math::vector::Vec2f @weak; +fn Vec2f foo(Vec2f a) { return a * 2; } + +module gog; +import std::math; +def Vec2f = std::math::vector::Vec2f @weak; + +module deef; +import gog; + +def Vec2f = gog::Vec2f @weak; +fn Vec2f foo(Vec2f a) { return a * 2; } + +module test; +import abc; +import std::math; +fn void test() +{ + Vec2f a; + abc::foo(a); +} + +module test2; +import abc; +import deef; +fn void test2() +{ + Vec2f a; + abc::foo(a); +} \ No newline at end of file diff --git a/test/test_suite/visibility/ambiguous_var.c3t b/test/test_suite/visibility/ambiguous_var.c3t index 711123b8f..9e27bb0c0 100644 --- a/test/test_suite/visibility/ambiguous_var.c3t +++ b/test/test_suite/visibility/ambiguous_var.c3t @@ -23,5 +23,5 @@ fn void test2() c = foo::b; c = bar::b; c = foo::a; - c = b; // #error: global variable needs a path prefix (e.g. 'bar::b') + c = b; // #error: global variable needs a path prefix (e.g. 'foo::b') }