From ebddbfb4166c461a094cbdb42d61cd90f9733d62 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 8 Oct 2023 23:43:02 +0200 Subject: [PATCH] Restrict any -> Protocol conversion. Protocol <-> looks at parent. Detect duplicate method definitions for protocols. --- src/compiler/compiler_internal.h | 8 +-- src/compiler/copying.c | 5 +- src/compiler/parse_global.c | 4 +- src/compiler/sema_casts.c | 10 ++-- src/compiler/sema_decls.c | 55 ++++++++++--------- src/compiler/sema_expr.c | 3 +- src/version.h | 2 +- test/test_suite/dynamic/any_cast.c3 | 22 ++++++++ .../dynamic/duplicate_definition.c3 | 12 ++++ 9 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 test/test_suite/dynamic/any_cast.c3 create mode 100644 test/test_suite/dynamic/duplicate_definition.c3 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2c44a7184..f3ee6fb3b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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; }; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index fef35b05f..157354a8b 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -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); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index a2bd7614e..68a2be328 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -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; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index aa9947a1e..124f9fb47 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -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 diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index f4570edb4..6ca0bc9b4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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(); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 94de702b3..336bc394f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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); diff --git a/src/version.h b/src/version.h index effe7e474..f504d7440 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.672" \ No newline at end of file +#define COMPILER_VERSION "0.4.673" \ No newline at end of file diff --git a/test/test_suite/dynamic/any_cast.c3 b/test/test_suite/dynamic/any_cast.c3 new file mode 100644 index 000000000..c75cfa213 --- /dev/null +++ b/test/test_suite/dynamic/any_cast.c3 @@ -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' +} diff --git a/test/test_suite/dynamic/duplicate_definition.c3 b/test/test_suite/dynamic/duplicate_definition.c3 new file mode 100644 index 000000000..c77fef44f --- /dev/null +++ b/test/test_suite/dynamic/duplicate_definition.c3 @@ -0,0 +1,12 @@ +module test; + +protocol Abc +{ + fn void abc(); + fn void abc(); // #error: Duplicate definition +} +fn void! main() +{ + Abc* g; + g.abc(); +}