Foreach overload is now done using attributes (@operator(elementat), @operator(elementref) and @operator(len)) rather than reserved functions.

This commit is contained in:
Christoffer Lerno
2022-01-29 16:43:43 +01:00
parent 9639ad6a73
commit ba66aaaf12
13 changed files with 151 additions and 204 deletions

View File

@@ -99,7 +99,7 @@ fn bool List.isEmpty(List *list)
return list.size;
}
fn usize List.len(List *list)
fn usize List.len(List *list) @operator(len)
{
return list.size;
}
@@ -116,12 +116,13 @@ fn void List.free(List *list)
list.size = 0;
}
macro usize List.operator_len(List &list)
{
return list.len();
}
macro Type List.operator_element_at(List &list, usize index)
macro Type List.item_at(List &list, usize index) @operator(elementat)
{
return list.entries[index];
}
macro Type* List.item_ref(List &list, usize index) @operator(elementref)
{
return &list.entries[index];
}

View File

@@ -313,6 +313,7 @@ typedef struct
{
Expr *expr;
uint32_t alignment;
OperatorOverload operator;
};
} Attr;
@@ -588,6 +589,7 @@ typedef struct Decl_
bool escaping : 1;
bool is_value : 1;
bool is_autoimport : 1;
OperatorOverload operator : 3;
union
{
void *backend_ref;
@@ -1607,10 +1609,9 @@ extern const char *kw_distinct;
extern const char *kw_ensure;
extern const char *kw_inline;
extern const char *kw_inf;
extern const char *kw_iterator;
extern const char *kw_operator_element_at;
extern const char *kw_operator_element_at_ref;
extern const char *kw_operator_len;
extern const char *kw_elementat;
extern const char *kw_elementref;
extern const char *kw_elementset;
extern const char *kw_len;
extern const char *kw_next;
extern const char *kw_nan;

View File

@@ -123,6 +123,7 @@ bool context_set_module(ParseContext *context, Path *path, TokenId *generic_para
void unit_register_external_symbol(CompilationUnit *unit, Decl *decl)
{
if (decl->decl_kind == DECL_MACRO) return;
assert(decl->external_name && "Missing external name");
Decl *prev = stable_get(&unit->external_symbols, decl->external_name);
if (prev) return;

View File

@@ -637,6 +637,14 @@ typedef enum
ATTR_MACRO = 1 << 13,
} AttributeDomain;
typedef enum
{
OVERLOAD_ELEMENT_AT = 1,
OVERLOAD_ELEMENT_REF,
OVERLOAT_ELEMENT_SET,
OVERLOAD_LEN
} OperatorOverload;
typedef enum
{
ATTRIBUTE_INLINE,
@@ -661,6 +669,7 @@ typedef enum
ATTRIBUTE_FASTCALL,
ATTRIBUTE_OVERLAP,
ATTRIBUTE_AUTOIMPORT,
ATTRIBUTE_OPERATOR,
ATTRIBUTE_NONE,
NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE,
} AttributeType;

View File

@@ -942,34 +942,47 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec
return true;
}
Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw)
Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload)
{
Decl *ambiguous = NULL;
Decl *private = NULL;
Type *type = expr->type->canonical;
Decl *method = type_may_have_sub_elements(type) ? sema_resolve_method(context->unit, type->decl, kw, &ambiguous, &private) : NULL;
if (!decl_ok(method)) return NULL;
if (!method)
if (!type_may_have_sub_elements(type)) return NULL;
Decl *def = type->decl;
Decl **funcs = def->methods;
VECEACH(funcs, i)
{
if (ambiguous)
Decl *func = funcs[i];
if (func->operator == operator_overload)
{
SEMA_ERROR(expr,
"It's not possible to find a definition for '%s' on %s that is not ambiguous.",
kw, type_quoted_error_string(expr->type));
return NULL;
unit_register_external_symbol(context->compilation_unit, func);
return func;
}
if (private)
{
SEMA_ERROR(expr,
"It's not possible to find a public definition for '%s' with '%s'.",
kw, type_quoted_error_string(expr->type));
return NULL;
}
return NULL;
}
return method;
Decl **imports = context->unit->imports;
VECEACH(imports, i)
{
Decl *import = imports[i];
Module *module = import->module;
if (module->is_generic) continue;
Decl **extensions = module->method_extensions;
VECEACH(extensions, j)
{
Decl *extension = extensions[j];
if (extension->operator == operator_overload)
{
unit_register_external_symbol(context->compilation_unit, extension);
return extension;
}
}
}
return NULL;
}
static inline bool sema_analyse_operator_element_at(Decl *method)
{
TypeInfo *rtype;
@@ -983,6 +996,19 @@ static inline bool sema_analyse_operator_element_at(Decl *method)
return true;
}
static inline bool sema_analyse_operator_element_set(Decl *method)
{
TypeInfo *rtype;
Decl **params;
if (!sema_analyse_operator_common(method, &rtype, &params, 3)) return false;
if (rtype->type->canonical != type_void)
{
SEMA_ERROR(rtype, "The return type should be 'void'.");
return false;
}
return true;
}
static inline bool sema_analyse_operator_len(Decl *method)
{
TypeInfo *rtype;
@@ -998,15 +1024,18 @@ static inline bool sema_analyse_operator_len(Decl *method)
static bool sema_check_operator_method_validity(Decl *method)
{
if (method->name == kw_operator_element_at || method->name == kw_operator_element_at_ref)
switch (method->operator)
{
return sema_analyse_operator_element_at(method);
case OVERLOAT_ELEMENT_SET:
return sema_analyse_operator_element_set(method);
case OVERLOAD_ELEMENT_AT:
case OVERLOAD_ELEMENT_REF:
return sema_analyse_operator_element_at(method);
case OVERLOAD_LEN:
return sema_analyse_operator_len(method);
default:
UNREACHABLE
}
if (method->name == kw_operator_len)
{
return sema_analyse_operator_len(method);
}
return true;
}
static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type, Decl *method_like)
@@ -1030,7 +1059,8 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type
SEMA_TOKID_PREV(method->name_token, "The previous definition was here.");
return false;
}
if (!sema_check_operator_method_validity(method_like)) return false;
if (method_like->operator && !sema_check_operator_method_validity(method_like)) return false;
REMINDER("Check multiple operator");
scratch_buffer_clear();
if (method_like->visibility <= VISIBLE_MODULE)
{
@@ -1150,6 +1180,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute
[ATTRIBUTE_NOSCOPE] = ATTR_MACRO,
[ATTRIBUTE_ESCAPING] = ATTR_MACRO,
[ATTRIBUTE_AUTOIMPORT] = ATTR_MACRO | ATTR_FUNC,
[ATTRIBUTE_OPERATOR] = ATTR_MACRO | ATTR_FUNC,
};
if ((attribute_domain[type] & domain) != domain)
@@ -1165,6 +1196,38 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute
case ATTRIBUTE_VECCALL:
case ATTRIBUTE_REGCALL:
return type;
case ATTRIBUTE_OPERATOR:
{
Expr *expr = attr->expr;
if (!expr || expr->expr_kind != EXPR_IDENTIFIER) goto FAILED_OP_TYPE;
if (expr->identifier_expr.path) goto FAILED_OP_TYPE;
TokenId tok = expr->identifier_expr.identifier;
const char *kw = TOKSTR(tok);
if (kw == kw_elementat)
{
attr->operator = OVERLOAD_ELEMENT_AT;
}
else if (kw == kw_elementref)
{
attr->operator = OVERLOAD_ELEMENT_REF;
}
else if (kw == kw_elementset)
{
attr->operator = OVERLOAT_ELEMENT_SET;
}
else if (kw == kw_len)
{
attr->operator = OVERLOAD_LEN;
}
else
{
goto FAILED_OP_TYPE;
}
return ATTRIBUTE_OPERATOR;
FAILED_OP_TYPE:
SEMA_ERROR(expr, "'operator' requires an operator type argument: '%s', '%s' or '%s'.", kw_elementat, kw_elementref, kw_len);
return ATTRIBUTE_NONE;
}
case ATTRIBUTE_ALIGN:
if (!attr->expr)
{
@@ -1389,6 +1452,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
context->unit->main_function = function;
return true;
}
static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
{
DEBUG_LOG("----Analysing function %s", decl->name);
@@ -1406,6 +1470,10 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
switch (attribute)
{
case ATTRIBUTE_OPERATOR:
had = decl->operator > 0;
decl->operator = attr->operator;
break;
case ATTRIBUTE_EXTNAME:
had = decl->extname != NULL;
decl->extname = attr->expr->const_expr.string.chars;
@@ -1559,6 +1627,10 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl)
bool had = false;
switch (attribute)
{
case ATTRIBUTE_OPERATOR:
had = decl->operator > 0;
decl->operator = attr->operator;
break;
case ATTRIBUTE_NOSCOPE:
had = decl->no_scope;
decl->no_scope = true;

View File

@@ -2572,14 +2572,14 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
if (!inner_type)
{
Decl *decl = NULL;
if (is_addr) decl = sema_find_operator(context, current_expr, kw_operator_element_at_ref);
if (is_addr) decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_REF);
if (!decl)
{
decl = sema_find_operator(context, current_expr, kw_operator_element_at);
decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_AT);
if (decl && is_addr)
{
SEMA_ERROR(expr, "'%s' is not defined for %s, so you need && to take the address of the temporary.",
kw_operator_element_at_ref, type_quoted_error_string(current_expr->type));
SEMA_ERROR(expr, "A function or macro with '@operator(%s)' is not defined for %s, so you need && to take the address of the temporary.",
kw_elementref, type_quoted_error_string(current_expr->type));
return false;
}
}

View File

@@ -66,7 +66,7 @@ void sema_analysis_pass_decls(Module *module);
void sema_analysis_pass_ct_assert(Module *module);
void sema_analysis_pass_functions(Module *module);
void sema_analyze_stage(Module *module, AnalysisStage stage);
Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw);
Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload);
bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr);
bool sema_analyse_ct_expr(SemaContext *context, Expr *expr);
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable);

View File

@@ -890,158 +890,22 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement)
}
static inline bool sema_inline_default_iterator(SemaContext *context, Expr *expr, Decl *decl)
{
Expr *inner = expr_copy(expr);
expr_insert_addr(inner);
expr->expr_kind = EXPR_CALL;
expr->call_expr = (ExprCall) {
.is_type_method = true,
};
REMINDER("Failability");
return sema_expr_analyse_general_call(context, expr, decl, inner, decl->decl_kind == DECL_MACRO, false);
}
static Decl *find_iterator(SemaContext *context, Expr *enumerator)
{
if (!type_may_have_sub_elements(enumerator->type))
{
SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type));
return NULL;
}
Decl *ambiguous = NULL;
Decl *private = NULL;
Decl *method = sema_resolve_method(context->unit, enumerator->type->decl, kw_iterator, &ambiguous, &private);
if (!decl_ok(method)) return NULL;
if (!method)
{
if (ambiguous)
{
SEMA_ERROR(enumerator,
"It's not possible to find a definition for 'iterator' on %s that is not ambiguous.",
type_quoted_error_string(enumerator->type));
return NULL;
}
if (private)
{
SEMA_ERROR(enumerator,
"It's not possible to find a public definition for 'iterator' with '%s'.",
type_quoted_error_string(enumerator->type));
return NULL;
}
SEMA_ERROR(enumerator,
"This type cannot be iterated over, implement a method or method macro called 'iterator'.",
type_to_error_string(enumerator->type));
return NULL;
}
Decl **parameters;
TypeInfo *iterator_type;
switch (method->decl_kind)
{
case DECL_GENERIC:
parameters = method->generic_decl.parameters;
iterator_type = method->generic_decl.rtype;
break;
case DECL_MACRO:
parameters = method->macro_decl.parameters;
iterator_type = method->macro_decl.rtype;
break;
case DECL_FUNC:
parameters = method->func_decl.function_signature.params;
iterator_type = method->func_decl.function_signature.returntype;
break;
default:
UNREACHABLE
}
if (vec_size(parameters) > 1)
{
SEMA_ERROR(enumerator, "'iterator()' takes parameters and can't be used for 'foreach'.");
return NULL;
}
if (!iterator_type)
{
SEMA_ERROR(enumerator, "This type has an iterator without a declared result type, this can't be used with 'foreach'.");
return NULL;
}
assert(iterator_type->resolve_status == RESOLVE_DONE);
Type *it_type = iterator_type->type->canonical;
if (it_type->type_kind != TYPE_STRUCT)
{
SEMA_ERROR(enumerator, "This type has an implementation of 'iterator()' that doesn't return a struct, so it can't be used with 'foreach'.");
return NULL;
}
return method;
}
static Decl *find_iterator_next(SemaContext *context, Expr *enumerator)
{
Type *type = enumerator->type->canonical;
assert(type->type_kind == TYPE_STRUCT);
Decl *ambiguous = NULL;
Decl *private = NULL;
Decl *method = sema_resolve_method(context->unit, type->decl, kw_next, &ambiguous, &private);
if (!decl_ok(method)) return NULL;
if (!method)
{
if (ambiguous)
{
SEMA_ERROR(enumerator, "The iterator %s has ambiguous 'next' definitions.", type_quoted_error_string(type));
return NULL;
}
if (private)
{
SEMA_ERROR(enumerator,
"The iterator %s has a private 'next' definition.",
type_quoted_error_string(type));
return NULL;
}
SEMA_ERROR(enumerator,
"The iterator %s is missing a definition for 'next()'.",
type_quoted_error_string(type));
return NULL;
}
Decl **parameters;
TypeInfo *rtype;
switch (method->decl_kind)
{
case DECL_GENERIC:
parameters = method->generic_decl.parameters;
rtype = method->generic_decl.rtype;
break;
case DECL_MACRO:
parameters = method->macro_decl.parameters;
rtype = method->macro_decl.rtype;
break;
case DECL_FUNC:
parameters = method->func_decl.function_signature.params;
rtype = method->func_decl.function_signature.returntype;
break;
default:
UNREACHABLE
}
if (vec_size(parameters) != 2)
{
SEMA_ERROR(enumerator, "An iterator with a 'next()' that take takes %d parameters can't be used for 'foreach', it should have 1.", vec_size(parameters) - 1);
return NULL;
}
if (!rtype)
{
SEMA_ERROR(enumerator, "This type has an iterator without a declared return type, this can't be used with 'foreach'.");
return NULL;
}
return method;
}
static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span, Decl *macro_decl, Expr *parent, Expr **arguments)
static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span, Decl *method_decl, Expr *parent, Expr **arguments)
{
Expr *len_call = expr_new(EXPR_CALL, span);
len_call->resolve_status = RESOLVE_RUNNING;
len_call->call_expr.func_ref = macro_decl;
len_call->call_expr.func_ref = method_decl;
len_call->call_expr.arguments = arguments;
len_call->call_expr.body = NULL;
len_call->call_expr.unsplat_last = false;
len_call->call_expr.is_type_method = true;
if (!sema_expr_analyse_macro_call(context, len_call, parent, macro_decl, false)) return poisoned_expr;
bool is_macro = method_decl->decl_kind == DECL_MACRO;
if (!is_macro)
{
if (parent->type->type_kind != TYPE_POINTER) expr_insert_addr(parent);
}
if (!sema_expr_analyse_general_call(context, len_call, method_decl, parent, is_macro, false)) return poisoned_expr;
len_call->resolve_status = RESOLVE_DONE;
return len_call;
}
@@ -1160,9 +1024,9 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
if (!value_type)
{
len = sema_find_operator(context, enumerator, kw_operator_len);
Decl *by_val = sema_find_operator(context, enumerator, kw_operator_element_at);
Decl *by_ref = sema_find_operator(context, enumerator, kw_operator_element_at_ref);
len = sema_find_operator(context, enumerator, OVERLOAD_LEN);
Decl *by_val = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_AT);
Decl *by_ref = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_REF);
if (!len || (!by_val && !by_ref))
{
SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type));

View File

@@ -45,10 +45,9 @@ const char *kw_elements;
const char *kw_errors;
const char *kw_inf;
const char *kw_inline;
const char *kw_iterator;
const char *kw_operator_element_at;
const char *kw_operator_element_at_ref;
const char *kw_operator_len;
const char *kw_elementat;
const char *kw_elementref;
const char *kw_elementset;
const char *kw_len;
const char *kw_main;
const char *kw_max;
@@ -138,10 +137,9 @@ void symtab_init(uint32_t capacity)
kw_errors = KW_DEF("errors");
kw_inf = KW_DEF("inf");
kw_inline = KW_DEF("inline");
kw_iterator = KW_DEF("iterator");
kw_operator_element_at = KW_DEF("operator_element_at");
kw_operator_element_at_ref = KW_DEF("operator_element_at_ref");
kw_operator_len = KW_DEF("operator_len");
kw_elementat = KW_DEF("elementat");
kw_elementref = KW_DEF("elementref");
kw_elementset = KW_DEF("elementset");
kw_len = KW_DEF("len");
kw_main = KW_DEF("main");
kw_max = KW_DEF("max");
@@ -213,6 +211,7 @@ void symtab_init(uint32_t capacity)
attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("regcall");
attribute_list[ATTRIBUTE_FASTCALL] = KW_DEF("fastcall");
attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("overlap");
attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("operator");
attribute_list[ATTRIBUTE_AUTOIMPORT] = KW_DEF("autoimport");
for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++)

View File

@@ -1 +1 @@
#define COMPILER_VERSION "PRE.20"
#define COMPILER_VERSION "PRE.21"

View File

@@ -8,17 +8,17 @@ struct Foo
extern fn void printf(char*, ...);
macro int* Foo.operator_element_at_ref(Foo &f, int a)
macro int* Foo.operator_element_at_ref(Foo &f, int a) @operator(elementref)
{
return &f.a[a];
}
macro int Foo.operator_len(Foo &f)
macro int Foo.operator_len(Foo &f) @operator(len)
{
return 3;
}
macro int Foo.operator_element_at(Foo &f, int a)
macro int Foo.operator_element_at(Foo &f, int a) @operator(elementat)
{
return f.a[a];
}

View File

@@ -6,12 +6,12 @@ struct Foo
int[] x;
}
macro int Foo.operator_element_at(Foo &foo, usize index)
macro int Foo.operator_element_at(Foo &foo, usize index) @operator(elementat)
{
return foo.x[index];
}
macro usize Foo.operator_len(Foo &foo)
macro usize Foo.operator_len(Foo &foo) @operator(len)
{
return foo.x.len;
}

View File

@@ -5,12 +5,12 @@ struct Foo
int[] x;
}
macro int Foo.operator_element_at(Foo &foo, usize index)
macro int Foo.operator_element_at(Foo &foo, usize index) @operator(elementat)
{
return foo.x[index];
}
macro usize Foo.operator_len(Foo &foo)
macro usize Foo.operator_len(Foo &foo) @operator(len)
{
return foo.x.len;
}