mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Added "weak" type aliases def Foo = my_foo::Foo @weak;
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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(<expr>)' 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(<expr>)' 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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
32
test/test_suite/define/weak_alias_fails.c3
Normal file
32
test/test_suite/define/weak_alias_fails.c3
Normal file
@@ -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);
|
||||
}
|
||||
32
test/test_suite/define/weak_aliases.c3
Normal file
32
test/test_suite/define/weak_aliases.c3
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user