Restrict any -> Protocol conversion. Protocol <-> looks at parent. Detect duplicate method definitions for protocols.

This commit is contained in:
Christoffer Lerno
2023-10-08 23:43:02 +02:00
parent 3aa85cf641
commit ebddbfb416
9 changed files with 78 additions and 43 deletions

View File

@@ -627,11 +627,7 @@ typedef struct
};
} DefineDecl;
typedef struct
{
TypeInfo **parents;
Decl **protocol_methods;
} ProtocolDecl;
typedef struct
{
AstId defer;
@@ -708,6 +704,7 @@ typedef struct Decl_
TypeInfo *distinct;
// Unions, Struct use strukt
StructDecl strukt;
Decl **protocol_methods;
};
};
AttrDecl attr_decl;
@@ -724,7 +721,6 @@ typedef struct Decl_
ImportDecl import;
IncludeDecl include;
LabelDecl label;
ProtocolDecl protocol_decl;
TypedefDecl typedef_decl;
VarDecl var;
};

View File

@@ -880,8 +880,9 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
case DECL_ERASED:
break;
case DECL_PROTOCOL:
MACRO_COPY_TYPE_LIST(copy->protocol_decl.parents);
MACRO_COPY_DECL_LIST(copy->protocol_decl.protocol_methods);
MACRO_COPY_TYPE_LIST(copy->protocols);
MACRO_COPY_DECL_LIST(copy->methods);
MACRO_COPY_DECL_LIST(copy->protocol_methods);
break;
case DECL_CT_EXEC:
MACRO_COPY_EXPR(copy->exec_decl.filename);

View File

@@ -1665,7 +1665,7 @@ INLINE bool parse_protocol_body(ParseContext *c, Decl *protocol)
ASSIGN_DECL_OR_RET(Decl *protocol_fn, parse_func_definition(c, contracts, true), false);
vec_add(fns, protocol_fn);
}
protocol->protocol_decl.protocol_methods = fns;
protocol->protocol_methods = fns;
return true;
}
/**
@@ -1685,7 +1685,7 @@ static inline Decl *parse_protocol_declaration(ParseContext *c)
vec_add(parents, type);
} while (try_consume(c, TOKEN_COMMA));
}
decl->protocol_decl.parents = parents;
decl->protocols = parents;
if (!parse_protocol_body(c, decl)) return poisoned_decl;
return decl;
}

View File

@@ -985,13 +985,13 @@ static bool rule_protocol_to_protocol(CastContext *cc, bool is_explicit, bool is
Type *from_protocol = cc->from_type->pointer;
Type *protocol = cc->to->pointer->canonical;
if (!sema_resolve_type_decl(cc->context, from_protocol)) return false;
FOREACH_BEGIN(TypeInfo *parent, from_protocol->decl->protocol_decl.parents)
FOREACH_BEGIN(TypeInfo *parent, from_protocol->decl->protocols)
if (parent->type->canonical == protocol) return true;
FOREACH_END();
if (is_silent) return false;
RETURN_SEMA_ERROR(cc->expr, "%s is not a child protocol of %s, but you can make an (unsafe) explicit cast to %s.",
type_quoted_error_string(from_protocol), type_quoted_error_string(cc->to->pointer),
type_quoted_error_string(cc->to_type));
RETURN_SEMA_ERROR(cc->expr, "%s is not a parent protocol of %s, but you can insert an explicit cast '(%s)value' to enforce the (unsafe) conversion.",
type_quoted_error_string(cc->to->pointer), type_quoted_error_string(from_protocol),
type_to_error_string(cc->to_type));
}
static bool rule_ptr_to_infer(CastContext *cc, bool is_explicit, bool is_silent)
@@ -1966,7 +1966,7 @@ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = {
{REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RARVC, RARBS, RXXDI, RARAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE}, // ARRAY
{REXPL, _NO__, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTDI, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, _NO__}, // STRUCT
{REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // UNION
{REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NA__, ROKOK, _NO__, _NO__, _NO__, _NO__, REXPL, REXPL, _NO__}, // ANY
{REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NA__, REXPL, _NO__, _NO__, _NO__, _NO__, REXPL, REXPL, _NO__}, // ANY
{REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPCPC, _NO__, _NO__, _NO__, _NO__, REXPL, REXPL, _NO__}, // PROTOCOL
{REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, REXPL, _NO__}, // FAULT
{REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // ENUM

View File

@@ -132,7 +132,7 @@ static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *curr
return true;
}
static inline bool sema_analyse_protocols(SemaContext *context, Decl *decl)
static inline bool sema_resolve_implemented_protocols(SemaContext *context, Decl *decl, bool deep)
{
TypeInfo **protocols = decl->protocols;
unsigned count = vec_size(protocols);
@@ -152,6 +152,7 @@ static inline bool sema_analyse_protocols(SemaContext *context, Decl *decl)
RETURN_SEMA_ERROR(proto, "Included protocol '%s' more than once, please remove duplicates.", proto_type->name);
}
}
if (deep && !sema_resolve_type_decl(context, proto_type)) return false;
}
return true;
}
@@ -492,7 +493,7 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *er
if (!sema_analyse_attributes(context, decl, decl->attributes, domain, erase_decl)) return decl_poison(decl);
if (*erase_decl) return true;
if (!sema_analyse_protocols(context, decl)) return decl_poison(decl);
if (!sema_resolve_implemented_protocols(context, decl, false)) return decl_poison(decl);
DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : ".anon");
bool success;
@@ -718,16 +719,8 @@ static bool sema_analyse_protocol(SemaContext *context, Decl *decl, bool *erase_
{
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_INTERFACE, erase_decl)) return decl_poison(decl);
if (*erase_decl) return true;
FOREACH_BEGIN(TypeInfo *parent, decl->protocol_decl.parents)
if (!sema_resolve_type_info(context, parent, RESOLVE_TYPE_ALLOW_ANY)) return decl_poison(decl);
if (parent->type->canonical->type_kind != TYPE_PROTOCOL)
{
SEMA_ERROR(parent, "Only protocols are allowed here.");
return decl_poison(decl);
}
if (!sema_resolve_type_decl(context, parent->type)) return decl_poison(decl);
FOREACH_END();
Decl **functions = decl->protocol_decl.protocol_methods;
if (!sema_resolve_implemented_protocols(context, decl, true)) return false;
Decl **functions = decl->protocol_methods;
unsigned count = vec_size(functions);
for (unsigned i = 0; i < count; i++)
{
@@ -754,13 +747,6 @@ static bool sema_analyse_protocol(SemaContext *context, Decl *decl, bool *erase_
vec_insert_first(method->func_decl.signature.params, first);
method->unit = context->unit;
if (!sema_analyse_func(context, method, &erase)) return decl_poison(decl);
if (!method->extname)
{
scratch_buffer_clear();
type_mangle_introspect_name_to_buffer(decl->type);
scratch_buffer_printf(".%s", method->name);
method->extname = scratch_buffer_copy();
}
if (erase)
{
vec_erase_ptr_at(functions, i);
@@ -768,13 +754,32 @@ static bool sema_analyse_protocol(SemaContext *context, Decl *decl, bool *erase_
if (i >= count) break;
goto RETRY;
}
const char *name = method->name;
for (unsigned j = 0; j < i; j++)
{
if (functions[j]->name == name)
{
SEMA_ERROR(method, "Duplicate definition of method '%s'.", name);
SEMA_NOTE(functions[j], "The previous definition was here.");
return decl_poison(decl);
return false;
}
}
vec_add(decl->methods, method);
if (!method->extname)
{
scratch_buffer_clear();
type_mangle_introspect_name_to_buffer(decl->type);
scratch_buffer_printf(".%s", name);
method->extname = scratch_buffer_copy();
}
}
return true;
}
static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase_decl)
{
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_BITSTRUCT, erase_decl)) return decl_poison(decl);
if (!sema_analyse_protocols(context, decl)) return decl_poison(decl);
if (!sema_resolve_implemented_protocols(context, decl, false)) return decl_poison(decl);
if (*erase_decl) return true;
DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : ".anon");
if (!sema_resolve_type_info(context, decl->bitstruct.base_type, RESOLVE_TYPE_DEFAULT)) return false;
@@ -1113,7 +1118,7 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl, bool
{
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_DISTINCT, erase)) return false;
if (*erase) return true;
if (!sema_analyse_protocols(context, decl)) return decl_poison(decl);
if (!sema_resolve_implemented_protocols(context, decl, false)) return decl_poison(decl);
TypeInfo *info = decl->distinct;
if (!sema_resolve_type_info(context, info, RESOLVE_TYPE_DEFAULT)) return false;
@@ -1228,7 +1233,7 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl, bool *era
{
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_ENUM, erase_decl)) return decl_poison(decl);
if (*erase_decl) return true;
if (!sema_analyse_protocols(context, decl)) return decl_poison(decl);
if (!sema_resolve_implemented_protocols(context, decl, false)) return decl_poison(decl);
// Resolve the type of the enum.
if (!sema_resolve_type_info(context, decl->enums.type_info, RESOLVE_TYPE_DEFAULT)) return false;
@@ -1376,7 +1381,7 @@ static inline bool sema_analyse_error(SemaContext *context, Decl *decl, bool *er
{
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_FAULT, erase_decl)) return decl_poison(decl);
if (*erase_decl) return true;
if (!sema_analyse_protocols(context, decl)) return decl_poison(decl);
if (!sema_resolve_implemented_protocols(context, decl, false)) return decl_poison(decl);
bool success = true;
unsigned enums = vec_size(decl->enums.values);
@@ -1676,10 +1681,10 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type
static Decl *sema_protocol_method_by_name(Decl *protocol, const char *name)
{
FOREACH_BEGIN(Decl *method, protocol->protocol_decl.protocol_methods)
FOREACH_BEGIN(Decl *method, protocol->protocol_methods)
if (method->name == name) return method;
FOREACH_END();
FOREACH_BEGIN(TypeInfo *parent_type, protocol->protocol_decl.parents)
FOREACH_BEGIN(TypeInfo *parent_type, protocol->protocols)
Decl *res = sema_protocol_method_by_name(parent_type->type->decl, name);
if (res) return res;
FOREACH_END();

View File

@@ -4103,7 +4103,7 @@ CHECK_DEEPER:
// Look at protocol parents
if (!member && decl->decl_kind == DECL_PROTOCOL)
{
FOREACH_BEGIN(TypeInfo *parent_protocol, decl->protocol_decl.parents)
FOREACH_BEGIN(TypeInfo *parent_protocol, decl->protocols)
member = sema_resolve_method(context->unit, parent_protocol->type->decl, kw, &ambiguous, &private);
if (member) break;
FOREACH_END();
@@ -4149,7 +4149,6 @@ CHECK_DEEPER:
}
if (parent->type->canonical->type_kind == TYPE_PROPTR)
{
sema_expr_analyse_access(context, expr);
RETURN_SEMA_ERROR(expr, "The '%s' protocol has no method '%s', did you spell it correctly?", parent->type->canonical->pointer->canonical->name, kw);
}
RETURN_SEMA_ERROR(expr, "There is no field or method '%s.%s'.", type_to_error_string(parent->type), kw);

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.672"
#define COMPILER_VERSION "0.4.673"

View File

@@ -0,0 +1,22 @@
module test;
protocol Abc : Def
{}
protocol Def
{}
fn void! test()
{
any* x;
Abc* d = x; // #error: cannot implicitly be converted to 'Abc*'
}
fn void! test2()
{
Abc* x;
any* d = x;
Def* e = x;
x = e; // #error: is not a parent protocol of 'Def'
}

View File

@@ -0,0 +1,12 @@
module test;
protocol Abc
{
fn void abc();
fn void abc(); // #error: Duplicate definition
}
fn void! main()
{
Abc* g;
g.abc();
}