mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Restrict any -> Protocol conversion. Protocol <-> looks at parent. Detect duplicate method definitions for protocols.
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.672"
|
||||
#define COMPILER_VERSION "0.4.673"
|
||||
22
test/test_suite/dynamic/any_cast.c3
Normal file
22
test/test_suite/dynamic/any_cast.c3
Normal 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'
|
||||
}
|
||||
12
test/test_suite/dynamic/duplicate_definition.c3
Normal file
12
test/test_suite/dynamic/duplicate_definition.c3
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user