Skip to content

Commit

Permalink
Make methods be available in earlier stages of analysis. Add @adhoc
Browse files Browse the repository at this point in the history
… attribute to allow types with ad hoc generic declarations.
  • Loading branch information
lerno committed Sep 25, 2024
1 parent 6f7ffbe commit da47588
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 66 deletions.
2 changes: 1 addition & 1 deletion lib/std/collections/maybe.c3
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module std::collections::maybe(<Type>);

struct Maybe
struct Maybe @adhoc
{
Type value;
bool has_value;
Expand Down
4 changes: 2 additions & 2 deletions lib/std/collections/tuple.c3
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
module std::collections::tuple(<Type1, Type2>);

struct Tuple @deprecated
struct Tuple @adhoc
{
Type1 first;
Type2 second;
}

module std::collections::triple(<Type1, Type2, Type3>);

struct Triple @deprecated
struct Triple @adhoc
{
Type1 first;
Type2 second;
Expand Down
3 changes: 2 additions & 1 deletion releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
- Allow the "self" parameter to be $/# for macro methods.
- Support compile time slicing of untyped lists.
- Allow specifying an import module using `@wasm` #1305.
- Deprecated inline generic types outside of struct definitions and macros unless marked `@adhoc`.
- Improved method detection in earlier stages of checking.

### Fixes
- Issue where a lambda wasn't correctly registered as external. #1408
- Generic methods were incorrectly registered as functions, leading to naming collisions. #1402
- Deprecated inline generic types outside of struct definitions and macros.
- Deprecated tuple / triple types.
- Converting a slice to a vector/array would copy too little data.
- Crash when reading an empty 'manifest.json'.
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/compiler_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ typedef struct Decl_
bool no_strip : 1;
bool is_cond : 1;
bool is_if : 1;
bool is_adhoc : 1;
bool attr_nopadding : 1;
bool attr_compact : 1;
bool resolved_attributes : 1;
Expand Down Expand Up @@ -1604,6 +1605,7 @@ struct CompilationUnit_
Decl **ct_includes;
Decl **vars;
Decl **macros;
Decl **methods_to_register;
Decl **methods;
Decl **macro_methods;
Decl **global_decls;
Expand Down Expand Up @@ -2273,6 +2275,7 @@ bool sema_cast_const(Expr *expr);
bool sema_expr_check_discard(SemaContext *context, Expr *expr);
bool sema_analyse_inferred_expr(SemaContext *context, Type *to, Expr *expr);
bool sema_analyse_decl(SemaContext *context, Decl *decl);
bool sema_analyse_method_register(SemaContext *context, Decl *method);
bool sema_resolve_type_structure(SemaContext *context, Type *type, SourceSpan span);
bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl);
bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local);
Expand Down
5 changes: 2 additions & 3 deletions src/compiler/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
assert(decl->name);
if (decl->func_decl.type_parent)
{
vec_add(unit->macro_methods, decl);
vec_add(unit->methods_to_register, decl);
return;
}
else
Expand All @@ -194,7 +194,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
assert(decl->name);
if (decl->func_decl.type_parent)
{
vec_add(unit->methods, decl);
vec_add(unit->methods_to_register, decl);
return;
}
else
Expand Down Expand Up @@ -233,7 +233,6 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
vec_add(unit->attributes, decl);
decl_register(decl);
break;

case DECL_FAULTVALUE:
case DECL_ENUM_CONSTANT:
case DECL_IMPORT:
Expand Down
14 changes: 10 additions & 4 deletions src/compiler/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ typedef enum
ANALYSIS_MODULE_TOP,
ANALYSIS_IMPORTS,
ANALYSIS_REGISTER_GLOBAL_DECLARATIONS,
ANALYSIS_METHODS_REGISTER,
ANALYSIS_INCLUDES,
ANALYSIS_METHODS_INCLUDES,
ANALYSIS_REGISTER_CONDITIONAL_UNITS,
ANALYSIS_REGISTER_CONDITIONAL_DECLARATIONS,
ANALYSIS_METHODS_CONDITIONAL,
ANALYSIS_POST_REGISTER,
ANALYSIS_DECLS,
ANALYSIS_CT_ECHO,
ANALYSIS_CT_ASSERT,
Expand Down Expand Up @@ -250,6 +254,7 @@ typedef enum FLAG_ATTR

typedef enum
{
ATTRIBUTE_ADHOC,
ATTRIBUTE_ALIGN,
ATTRIBUTE_BENCHMARK,
ATTRIBUTE_BIGENDIAN,
Expand Down Expand Up @@ -988,10 +993,11 @@ typedef enum
typedef enum
{
RESOLVE_TYPE_DEFAULT,
RESOLVE_TYPE_ALLOW_INFER = 0x01,
RESOLVE_TYPE_ALLOW_FLEXIBLE = 0x02,
RESOLVE_TYPE_MACRO_METHOD = RESOLVE_TYPE_ALLOW_INFER,
RESOLVE_TYPE_FUNC_METHOD = RESOLVE_TYPE_DEFAULT
RESOLVE_TYPE_ALLOW_INFER = 0x01,
RESOLVE_TYPE_ALLOW_FLEXIBLE = 0x02,
RESOLVE_TYPE_NO_CHECK_DISTINCT = 0x04,
RESOLVE_TYPE_MACRO_METHOD = RESOLVE_TYPE_ALLOW_INFER | RESOLVE_TYPE_NO_CHECK_DISTINCT,
RESOLVE_TYPE_FUNC_METHOD = RESOLVE_TYPE_NO_CHECK_DISTINCT,
} ResolveTypeKind;

typedef enum FLAG_ATTR
Expand Down
111 changes: 70 additions & 41 deletions src/compiler/sema_decls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1780,15 +1780,6 @@ INLINE SourceSpan method_find_overload_span(Decl *method)

static inline bool unit_add_base_extension_method(SemaContext *context, CompilationUnit *unit, Type *parent_type, Decl *method)
{
// We don't support operator overloading on base types, because
// there seems little use for it frankly.
if (method->operator)
{
sema_error_at(context, method_find_overload_span(method),
"Only user-defined types support operator oveloading.");
return false;
}

// Add it to the right list of extensions.
switch (method->visibility)
{
Expand Down Expand Up @@ -1841,10 +1832,20 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type
// Check it's valid for the operator type.
if (!sema_check_operator_method_validity(context, method)) return false;

// We don't support operator overloading on base types, because
// there seems little use for it frankly.
if (!type_is_user_defined(parent_type))
{
sema_error_at(context, method_find_overload_span(method),
"Only user-defined types support operator oveloading.");
return false;
}

// See if the operator has already been defined.
OperatorOverload operator = method->operator;

Decl *other = sema_find_operator(context, parent_type, operator);
if (other)
if (other != method)
{
SourceSpan span = method_find_overload_span(method);
sema_error_at(context, span, "This operator is already defined for '%s'.", parent_type->name);
Expand Down Expand Up @@ -1920,6 +1921,11 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type
// So compare the value
if (this_value != value)
{
if (operator == OVERLOAD_ELEMENT_REF)
{
value = type_get_ptr(value);
this_value = type_get_ptr(this_value);
}
SEMA_ERROR(method, "There is a mismatch of the 'value' type compared to that of another operator: expected %s but got %s.",
type_quoted_error_string(value), type_quoted_error_string(this_value));
SEMA_NOTE(other, "The other definition is here.");
Expand Down Expand Up @@ -1966,6 +1972,7 @@ static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl
return false;
}


// Is it a base extension?
if (!type_is_user_defined(parent_type)) return unit_add_base_extension_method(context, unit, parent_type, method);

Expand All @@ -1984,12 +1991,6 @@ static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl
return false;
}

// Is it an operator?
if (method->operator)
{
if (!sema_analyse_operator_method(context, parent_type, method)) return false;
}

DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method->name);

// Add it to the correct place: type methods, private extensions, local method extensions
Expand Down Expand Up @@ -2178,13 +2179,12 @@ static inline bool sema_compare_method_with_interface(SemaContext *context, Decl
*
* 1. Check that it has no init/finalizer attributes.
* 2. Check that it has no test/benchmark attributes.
* 3. Resolve the parent type.
* 4. Resolve the declaration of the parent (as needed).
* 5. Check that it has at least one parameter.
* 6. Check that this parameter is correct.
* 7. If it is dynamic, the type may not be an interface or any
* 8. If it is dynamic, make sure that it implements an interface correctly if available
* 9. Try adding the method
* 3. Resolve the declaration of the parent (as needed).
* 4. Check that it has at least one parameter.
* 5. Check that this parameter is correct.
* 6. If it is dynamic, the type may not be an interface or any
* 7. If it is dynamic, make sure that it implements an interface correctly if available
* 8. Try adding the method
*/
static inline bool sema_analyse_method(SemaContext *context, Decl *decl)
{
Expand All @@ -2202,7 +2202,7 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl)

// Resolve the parent type.
TypeInfo *parent_type = type_infoptr(decl->func_decl.type_parent);
if (!sema_resolve_type_info(context, parent_type, RESOLVE_TYPE_FUNC_METHOD)) return false;
assert(parent_type->resolve_status == RESOLVE_DONE);
Type *par_type = parent_type->type->canonical;

// Resolve declaration of parent as needed.
Expand Down Expand Up @@ -2245,7 +2245,13 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl)
decl->func_decl.interface_method = 0;
}
}
return unit_add_method(context, par_type, decl);
// Is it an operator?
if (decl->operator)
{
if (!sema_analyse_operator_method(context, par_type, decl)) return false;
}

return true;
}

static const char *attribute_domain_to_string(AttributeDomain domain)
Expand Down Expand Up @@ -2363,6 +2369,7 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
assert(type >= 0 && type < NUMBER_OF_ATTRIBUTES);
// NOLINTBEGIN(*.EnumCastOutOfRange)
static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = {
[ATTRIBUTE_ADHOC] = USER_DEFINED_TYPES,
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_LOCAL | ATTR_GLOBAL | ATTR_BITSTRUCT | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER, // NOLINT
[ATTRIBUTE_BENCHMARK] = ATTR_FUNC,
[ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT,
Expand Down Expand Up @@ -2526,6 +2533,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
FAILED_OP_TYPE:
RETURN_SEMA_ERROR(attr, "'operator' requires an operator type argument: '[]', '[]=', '&[]' or 'len'.");
}
case ATTRIBUTE_ADHOC:
decl->is_adhoc = true;
return true;
case ATTRIBUTE_ALIGN:
if (!expr)
{
Expand Down Expand Up @@ -3462,20 +3472,8 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
{
// Resolve the type of the method.
TypeInfo *parent_type_info = type_infoptr(decl->func_decl.type_parent);
if (!sema_resolve_type_info(context, parent_type_info, RESOLVE_TYPE_MACRO_METHOD)) return false;

// Can the type have methods?
Type *parent_type = parent_type_info->type;
if (!type_may_have_method(parent_type))
{
RETURN_SEMA_ERROR(parent_type_info, "Methods can not be associated with '%s'", type_to_error_string(parent_type));
}

// We need at least one argument (the parent type)
if (!vec_size(decl->func_decl.signature.params))
{
RETURN_SEMA_ERROR(decl, "Expected at least one parameter - of type '%s'.", type_to_error_string(parent_type));
}
assert(parent_type_info->resolve_status == RESOLVE_DONE);
Type *parent_type = parent_type_info->type->canonical;

// Check the first argument.
Decl *first_param = decl->func_decl.signature.params[0];
Expand All @@ -3484,13 +3482,19 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
RETURN_SEMA_ERROR(decl, "The first parameter to this method must be of type '%s'.", type_to_error_string(parent_type));
return false;
}
if (!sema_is_valid_method_param(context, first_param, parent_type->canonical, false)) return false;
if (!sema_is_valid_method_param(context, first_param, parent_type, false)) return false;

if (first_param->var.kind != VARDECL_PARAM_EXPR && first_param->var.kind != VARDECL_PARAM_CT && first_param->var.kind != VARDECL_PARAM_REF && first_param->var.kind != VARDECL_PARAM)
{
RETURN_SEMA_ERROR(first_param, "The first parameter must be a compile time, regular or ref (&) type.");
}
return unit_add_method(context, parent_type->canonical, decl);
// Is it an operator?
if (decl->operator)
{
if (!sema_analyse_operator_method(context, parent_type, decl)) return false;
}

return true;
}

INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters)
Expand Down Expand Up @@ -4408,6 +4412,31 @@ bool sema_resolve_type_structure(SemaContext *context, Type *type, SourceSpan sp
UNREACHABLE
}

bool sema_analyse_method_register(SemaContext *context, Decl *method)
{
TypeInfo *parent_type_info = type_infoptr(method->func_decl.type_parent);
if (!sema_resolve_type_info(context, parent_type_info, method->decl_kind == DECL_MACRO ? RESOLVE_TYPE_MACRO_METHOD : RESOLVE_TYPE_FUNC_METHOD)) return false;

// Can the type have methods?
Type *parent_type = parent_type_info->type;
if (!type_may_have_method(parent_type))
{
RETURN_SEMA_ERROR(parent_type_info, "Methods can not be associated with '%s'", type_to_error_string(parent_type));
}

// We need at least one argument (the parent type)
if (!vec_size(method->func_decl.signature.params))
{
RETURN_SEMA_ERROR(method, "Expected at least one parameter - of type '%s'.", type_to_error_string(parent_type));
}

// Check the first argument.
Decl *first_param = method->func_decl.signature.params[0];
if (!first_param) RETURN_SEMA_ERROR(method, "The first parameter to this method must be of type '%s'.", type_to_error_string(parent_type));

return unit_add_method(context, parent_type->canonical, method);
}

bool sema_analyse_decl(SemaContext *context, Decl *decl)
{
if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl);
Expand Down
7 changes: 2 additions & 5 deletions src/compiler/sema_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2129,7 +2129,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
type_quoted_error_string(type_get_ptr(type_info->type)));
}
body_arg->type = type;
if (type_info && type_storage_type(type_info->type))
if (type_info && type_storage_type(type_info->type) == STORAGE_NORMAL)
{
if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false;
}
Expand Down Expand Up @@ -3547,6 +3547,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp
UNREACHABLE
}


Decl *member = sema_decl_stack_find_decl_member(context, decl, name);
if (!decl_ok(member)) return false;
if (!member)
Expand Down Expand Up @@ -4507,10 +4508,6 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr,
return true;
}
case TYPE_PROPERTY_METHODSOF:
if (context->compilation_unit->module->stage <= ANALYSIS_DECLS)
{
RETURN_SEMA_ERROR(expr, "'methodsof' is not available until after declaration analysis is complete.");
}
sema_create_const_methodsof(context, expr, flat);
return true;
case TYPE_PROPERTY_PARAMSOF:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/sema_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ void sema_analysis_pass_register_global_declarations(Module *module);
void sema_analysis_pass_process_includes(Module *module);
void sema_analysis_pass_register_conditional_units(Module *module);
void sema_analysis_pass_register_conditional_declarations(Module *module);
void sema_analysis_pass_process_methods(Module *module);
void sema_analysis_pass_decls(Module *module);
void sema_analysis_pass_ct_assert(Module *module);
void sema_analysis_pass_ct_echo(Module *module);
Expand Down
Loading

0 comments on commit da47588

Please sign in to comment.