Extension methods added. Some initial work on virtual.

This commit is contained in:
Christoffer Lerno
2021-07-21 19:28:02 +02:00
parent 46e39f883c
commit 198e3c369c
16 changed files with 339 additions and 52 deletions

View File

@@ -87,6 +87,8 @@ const char *decl_to_name(Decl *decl)
return "function";
case DECL_GENERIC:
return "generic";
case DECL_GENFUNC:
TODO
case DECL_INTERFACE:
return "interface";
case DECL_MACRO:
@@ -178,6 +180,7 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
case DECL_GENFUNC:
case DECL_GENERIC:
case DECL_CT_IF:
case DECL_CT_ELSE:
@@ -864,6 +867,7 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent)
{
case DECL_INTERFACE:
DUMPF("(interface %s", decl->name);
DUMPDECLS(decl->interface_decl.members);
DUMPDECLS(decl->interface_decl.functions);
DUMPEND();
case DECL_VAR:
@@ -910,6 +914,16 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent)
indent--;
DUMPAST(decl->macro_decl.body);
DUMPEND();
case DECL_GENFUNC:
DUMPF("(macro %s", decl->name);
DUMPTI(decl->macro_decl.rtype);
indent++;
DUMP("(params");
DUMPDECLS(decl->macro_decl.parameters);
DUMPE();
indent--;
DUMPAST(decl->macro_decl.body);
DUMPEND();
case DECL_FUNC:
DUMPF("(func %s", decl->name);
if (decl->func_decl.type_parent)

View File

@@ -183,6 +183,7 @@ static void register_generic_decls(Module *module, Decl **decls)
case DECL_DISTINCT:
case DECL_ENUM:
case DECL_GENERIC:
case DECL_GENFUNC:
case DECL_INTERFACE:
case DECL_ERR:
case DECL_FUNC:

View File

@@ -411,6 +411,7 @@ typedef struct
typedef struct
{
Decl **functions;
Decl **members;
} InterfaceDecl;
typedef struct
@@ -1234,7 +1235,7 @@ typedef struct Module_
Ast **files; // Asts
Decl** functions;
Decl** method_extensions;
STable symbols;
STable public_symbols;
struct Context_ **contexts;
@@ -1762,6 +1763,7 @@ const char *resolve_status_to_string(ResolveStatus status);
#define SEMA_TOKID_ERROR(_tok_id, ...) sema_error_range(source_span_from_token_id(_tok_id), __VA_ARGS__)
#define SEMA_ERROR(_node, ...) sema_error_range((_node)->span, __VA_ARGS__)
#define SEMA_PREV(_node, ...) sema_prev_at_range3((_node)->span, __VA_ARGS__)
#define SEMA_TOKID_PREV(_tok_id, ...) sema_prev_at_range3(source_span_from_token_id(_tok_id), __VA_ARGS__)
void sema_analysis_pass_process_imports(Module *module);
void sema_analysis_pass_register_globals(Module *module);
@@ -1786,6 +1788,8 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef
Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol);
Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *path);
Decl *sema_resolve_method(Context *context, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref);
Decl *sema_find_extension_method_in_module(Module *module, Type *type, const char *method_name);
Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, bool handle_error);
Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path);

View File

@@ -113,6 +113,7 @@ void context_register_global_decl(Context *context, Decl *decl)
vec_add(context->interfaces, decl);
decl_set_external_name(decl);
break;
case DECL_GENFUNC:
case DECL_GENERIC:
vec_add(context->generics, decl);
decl_set_external_name(decl);

View File

@@ -498,6 +498,7 @@ Decl *copy_decl(Decl *decl)
case DECL_INTERFACE:
copy_decl_type(copy);
MACRO_COPY_DECL_LIST(copy->interface_decl.functions);
MACRO_COPY_DECL_LIST(copy->interface_decl.members);
break;
case DECL_FUNC:
MACRO_COPY_TYPE(copy->func_decl.type_parent);
@@ -560,6 +561,7 @@ Decl *copy_decl(Decl *decl)
break;
case DECL_ARRAY_VALUE:
TODO
case DECL_GENFUNC:
case DECL_MACRO:
MACRO_COPY_TYPE(decl->macro_decl.type_parent);
MACRO_COPY_DECL_LIST(decl->macro_decl.parameters);

View File

@@ -149,6 +149,7 @@ typedef enum
DECL_INTERFACE,
DECL_LABEL,
DECL_MACRO,
DECL_GENFUNC,
DECL_STRUCT,
DECL_TYPEDEF,
DECL_UNION,
@@ -158,7 +159,7 @@ typedef enum
#define NON_TYPE_DECLS DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: \
case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \
case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \
case DECL_DEFINE: case DECL_CT_ASSERT
case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENFUNC
typedef enum
{

View File

@@ -48,7 +48,7 @@ static bool context_next_is_type_and_not_ident(Context *context)
{
if (context->tok.type == TOKEN_IDENT)
{
if (context->next_tok.type != TOKEN_COLON) return false;
if (context->next_tok.type != TOKEN_SCOPE) return false;
return context_next_is_type_with_path_prefix(context);
}
return token_is_any_type(context->tok.type);
@@ -1948,7 +1948,7 @@ static inline Decl *parse_interface_declaration(Context *context, Visibility vis
return poisoned_decl;
}
Decl *function = TRY_DECL_OR(parse_func_definition(context, visibility, true), poisoned_decl);
vec_add(decl->interface_decl.functions, function);
vec_add(decl->methods, function);
}
CONSUME_OR(TOKEN_RBRACE, poisoned_decl);

View File

@@ -464,6 +464,43 @@ bool cast_may_explicit(Type *from_type, Type *to_type)
UNREACHABLE
}
static bool may_cast_to_virtual(Type *virtual, Type *from)
{
assert(from->canonical == from);
// 1. We need a pointer, we can't cast from a non pointer.
if (from->type_kind != TYPE_POINTER) return false;
// 2. Virtual* converts to anything, including ints
if (virtual->type_kind == TYPE_VIRTUAL_ANY) return true;
// 3. Get the data.
Decl *virtual_decl = virtual->decl;
Decl **methods = virtual_decl->interface_decl.functions;
// 4. No variables nor members? Then this is essentially a virtual*
if (!vec_size(methods) && !vec_size(virtual_decl->strukt.members)) return true;
// 5. Look at the pointer.
Type *pointee = from->pointer;
// 6. Is this an array, if so it doesn't have any functions,
// so we implicitly lower to the first element.
if (pointee->type_kind == TYPE_ARRAY)
{
pointee = pointee->array.base;
}
// Do this: create a function that returns a matching interface method.
// store this decl.
// Same with looking at members -> store the Decl.
// Later, generating the table we provide the decl backend ref and the offset.
// Note that matching types should take into account the first element.
// Also go recursively into substructs structs
// Note that this resolution cannot be cached completely due to the module import lookup
TODO;
}
/**
* Can the conversion occur implicitly?
*/
@@ -581,8 +618,7 @@ bool cast_may_implicit(Type *from_type, Type *to_type)
// 10. Virtual cast
if (to->type_kind == TYPE_VIRTUAL)
{
TODO
//
return may_cast_to_virtual(to, from);
}
// 11. Substruct cast, if the first member is inline, see if we can cast to this member.

View File

@@ -602,7 +602,67 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl)
return success;
}
static inline const char *name_by_decl(Decl *method_like)
{
switch (method_like->decl_kind)
{
case DECL_MACRO:
return "macro method";
case DECL_FUNC:
return "method";
case DECL_GENFUNC:
return "generic method";
default:
UNREACHABLE
}
}
static inline bool sema_add_method_like(Context *context, Type *parent_type, Decl *method_like)
{
assert(parent_type->canonical == parent_type);
Decl *parent = parent_type->decl;
const char *name = method_like->name;
Decl *method = sema_find_extension_method_in_module(context->module, parent_type, name);
if (method)
{
SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined in this module.", name_by_decl(method_like));
SEMA_TOKID_PREV(method->name_token, "The previous definition was here.");
return false;
}
Decl *ambiguous = NULL;
Decl *private = NULL;
method = sema_resolve_method(context, parent, name, &ambiguous, &private);
if (method)
{
SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined for '%s'.", name_by_decl(method_like), parent_type->name);
SEMA_TOKID_PREV(method->name_token, "The previous definition was here.");
return false;
}
scratch_buffer_clear();
if (method_like->visibility <= VISIBLE_MODULE)
{
scratch_buffer_append(parent->name);
scratch_buffer_append_char('.');
scratch_buffer_append(method_like->name);
}
else
{
scratch_buffer_append(parent->external_name);
scratch_buffer_append("__");
scratch_buffer_append(method_like->name);
}
method_like->external_name = scratch_buffer_interned();
DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method_like->name);
if (parent->module == context->module)
{
vec_add(parent->methods, method_like);
}
else
{
vec_add(context->module->method_extensions, method_like);
}
return true;
}
static inline bool sema_analyse_method(Context *context, Decl *decl)
{
@@ -615,35 +675,8 @@ static inline bool sema_analyse_method(Context *context, Decl *decl)
type_to_error_string(decl->func_decl.type_parent->type));
return false;
}
Decl *parent = parent_type->type->decl;
VECEACH(parent->methods, i)
{
Decl *function = parent->methods[i];
if (function->name == decl->name)
{
SEMA_ERROR(decl, "Duplicate name '%s' for method.", function->name);
SEMA_PREV(function, "Previous definition here.");
return false;
}
}
scratch_buffer_clear();
if (decl->visibility <= VISIBLE_MODULE)
{
scratch_buffer_append(parent->name);
scratch_buffer_append_char('.');
scratch_buffer_append(decl->name);
}
else
{
scratch_buffer_append(parent->external_name);
scratch_buffer_append("__");
scratch_buffer_append(decl->name);
}
decl->external_name = scratch_buffer_interned();
DEBUG_LOG("Method '%s.%s' analysed.", parent->name, decl->name);
vec_add(parent->methods, decl);
return true;
Type *type = parent_type->type->canonical;
return sema_add_method_like(context, type, decl);
}
static inline AttributeType attribute_by_name(Attr *attr)
@@ -971,21 +1004,7 @@ static bool sema_analyse_macro_method(Context *context, Decl *decl)
SEMA_ERROR(first_param, "The first parameter must be a regular value parameter.");
return false;
}
Decl *parent = parent_type->decl;
VECEACH(parent->methods, i)
{
Decl *function = parent->methods[i];
if (function->name == decl->name)
{
SEMA_ERROR(decl, "Duplicate name '%s' for macro method.", function->name);
SEMA_PREV(function, "Previous definition here.");
return false;
}
}
DEBUG_LOG("Macro method '%s.%s' analysed.", parent->name, decl->name);
vec_add(parent->methods, decl);
return true;
return sema_add_method_like(context, parent_type, decl);
}
static inline bool sema_analyse_macro(Context *context, Decl *decl)
@@ -1439,6 +1458,8 @@ bool sema_analyse_decl(Context *context, Decl *decl)
case DECL_FUNC:
if (!sema_analyse_func(context, decl)) return decl_poison(decl);
break;
case DECL_GENFUNC:
TODO
case DECL_MACRO:
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
break;

View File

@@ -224,6 +224,7 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr
UNREACHABLE
case DECL_IMPORT:
case DECL_GENERIC:
case DECL_GENFUNC:
case DECL_CT_IF:
case DECL_CT_ELSE:
case DECL_CT_ELIF:
@@ -2380,6 +2381,16 @@ CHECK_DEEPER:
member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
SCOPE_END;
if (!member)
{
Decl *ambiguous = NULL;
Decl *private = NULL;
member = sema_resolve_method(context, decl, kw, &ambiguous, &private);
if (member)
{
context_register_external_symbol(context, member);
}
}
// 11. If we didn't find a match...
if (!member)
{

View File

@@ -231,6 +231,72 @@ static Decl *sema_resolve_symbol(Context *context, const char *symbol_str, Sourc
return decl;
}
Decl *sema_find_extension_method_in_module(Module *module, Type *type, const char *method_name)
{
Decl **extensions = module->method_extensions;
VECEACH(extensions, i)
{
Decl *extension = extensions[i];
if (extension->name != method_name) continue;
switch (extension->decl_kind)
{
case DECL_FUNC:
if (extension->func_decl.type_parent->type == type) return extension;
break;
case DECL_MACRO:
case DECL_GENFUNC:
if (extension->macro_decl.type_parent->type == type) return extension;
break;
default:
UNREACHABLE
}
}
return NULL;
}
Decl *sema_resolve_method(Context *context, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref)
{
// 1. Look at the previously defined ones.
VECEACH(type->methods, i)
{
Decl *func = type->methods[i];
if (method_name == func->name) return func;
}
// 2. Make a module lookup
Decl *previously_found = NULL;
Type *actual_type = type->type;
Decl *private_type = NULL;
Decl *result = NULL;
VECEACH(context->imports, i)
{
Decl *import = context->imports[i];
if (import->module->is_generic) continue;
Decl *found = sema_find_extension_method_in_module(import->module, actual_type, method_name);
if (!found) continue;
if (found->visibility <= VISIBLE_MODULE && !import->import.private)
{
private_type = found;
continue;
}
if (result)
{
*ambiguous_ref = previously_found;
*private_ref = NULL;
return NULL;
}
result = found;
}
if (result && private_type)
{
private_type = NULL;
}
return result;
}
Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *path)
{
Decl *ambiguous_other_decl = NULL;

View File

@@ -119,6 +119,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
case DECL_GENFUNC:
case DECL_GENERIC:
case DECL_LABEL:
SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "This is not a type.");

View File

@@ -0,0 +1,31 @@
module foo;
struct Bar
{
int x;
}
module baz;
import foo;
import std::io;
macro void foo::Bar.test(Bar *bar)
{
io::println("Inside of baz::Bar.test");
}
macro void Bar.test(Bar *bar) // #error: This macro method is already defined in this module
{
io::println("Inside of baz::Bar.test");
}
module abc;
import foo;
import baz;
func void main()
{
Bar bar;
bar.test();
}

View File

@@ -0,0 +1,37 @@
module foo;
struct Bar
{
int x;
}
module baz;
import foo;
import std::io;
func void foo::Bar.test(Bar *bar)
{
io::println("Inside of baz::Bar.test");
}
module abc;
import foo;
import baz;
func void main()
{
Bar bar;
bar.test();
}
// #expect: abc.ll
declare void @foo.Bar__test(%Bar*)
define void @main()
entry:
%bar = alloca %Bar, align 4
%0 = bitcast %Bar* %bar to i8*
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 4, i1 false)
call void @foo.Bar__test(%Bar* %bar)
ret void

View File

@@ -0,0 +1,30 @@
module foo;
func void Bar.test(Bar *bar)
{
io::println("Inside of baz::Bar.test");
}
struct Bar
{
int x;
}
module baz;
import foo;
import std::io;
func void foo::Bar.test(Bar *bar) // #error: This method is already defined for 'Bar'
{
io::println("Inside of baz::Bar.test");
}
module abc;
import foo;
import baz;
func void main()
{
Bar bar;
bar.test();
}

View File

@@ -0,0 +1,31 @@
module foo;
struct Bar
{
int x;
}
module baz;
import foo;
import std::io;
func void foo::Bar.test(Bar *bar)
{
io::println("Inside of baz::Bar.test");
}
func void Bar.test(Bar *bar) // #error: This method is already defined in this module
{
io::println("Inside of baz::Bar.test");
}
module abc;
import foo;
import baz;
func void main()
{
Bar bar;
bar.test();
}