From 312a39ee2467b2db6683b525877156d8bd6e515d Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 8 Oct 2023 01:54:30 +0200 Subject: [PATCH] Handle protocol inheritance. Allow overlapping protocol methods. Remove the need for &self in protocol declarations. Fix cast rules for protocol. Fix cast rules for bitstruct #1034. --- lib/std/io/formatter.c3 | 4 +- lib/std/math/math.random.c3 | 14 +- src/compiler/compiler_internal.h | 3 +- src/compiler/copying.c | 1 + src/compiler/enums.h | 2 + src/compiler/expr.c | 2 + src/compiler/llvm_codegen_expr.c | 3 + src/compiler/parse_global.c | 9 +- src/compiler/sema_casts.c | 54 +++- src/compiler/sema_decls.c | 61 +++-- src/compiler/sema_expr.c | 15 +- src/compiler/symtab.c | 2 + test/test_suite/cast/cast_bitstruct_etc.c3t | 39 +++ test/test_suite/dynamic/dynamic_mismatch.c3 | 2 +- test/test_suite/dynamic/inherit.c3t | 244 ++++++++++++++++++ .../dynamic/overlapping_function.c3t | 210 +++++++++++++++ test/unit/regression/liveness_any.c3 | 2 +- 17 files changed, 615 insertions(+), 52 deletions(-) create mode 100644 test/test_suite/cast/cast_bitstruct_etc.c3t create mode 100644 test/test_suite/dynamic/inherit.c3t create mode 100644 test/test_suite/dynamic/overlapping_function.c3t diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 09c9f1c55..1cfd29ca0 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -6,8 +6,8 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256; protocol Printable { - fn String to_string(&self, Allocator *using) @optional; - fn usz! to_format(&self, Formatter* formatter) @optional; + fn String to_string(Allocator *using) @optional; + fn usz! to_format(Formatter* formatter) @optional; } fault PrintFault diff --git a/lib/std/math/math.random.c3 b/lib/std/math/math.random.c3 index 69bf9fe68..ce1bc6bd4 100644 --- a/lib/std/math/math.random.c3 +++ b/lib/std/math/math.random.c3 @@ -2,13 +2,13 @@ module std::math::random; protocol Random { - fn void set_seed(&self, char[] input); - fn char next_byte(&self); - fn ushort next_short(&self); - fn uint next_int(&self); - fn ulong next_long(&self); - fn uint128 next_int128(&self); - fn void next_bytes(&self, char[] buffer); + fn void set_seed(char[] input); + fn char next_byte(); + fn ushort next_short(); + fn uint next_int(); + fn ulong next_long(); + fn uint128 next_int128(); + fn void next_bytes(char[] buffer); } macro Random.seed(&self, seed) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b05f1351b..2c44a7184 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -629,7 +629,7 @@ typedef struct typedef struct { - const char **parents; + TypeInfo **parents; Decl **protocol_methods; } ProtocolDecl; typedef struct @@ -1908,6 +1908,7 @@ extern const char *kw_out; extern const char *kw_ptr; extern const char *kw_pure; extern const char *kw_return; +extern const char *kw_self; extern const char *kw_std; extern const char *kw_type; extern const char *kw_winmain; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 4753ec8ce..fef35b05f 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -880,6 +880,7 @@ 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); break; case DECL_CT_EXEC: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 883393596..0fad9916a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -88,6 +88,8 @@ typedef enum CAST_BOOLFP, CAST_BOOLINT, CAST_BOOLVECINT, + CAST_BSINTARR, + CAST_INTARRBS, CAST_EREU, CAST_ERINT, CAST_ERPTR, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index bfc19d2b6..13c2bd71a 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -375,6 +375,8 @@ static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_ case CAST_ERINT: case CAST_PTRINT: case CAST_IDINT: + case CAST_INTARRBS: + case CAST_BSINTARR: if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false; return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 347deaf4a..a3d49998e 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1445,6 +1445,9 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *valu value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool"); value->kind = BE_BOOLEAN; break; + case CAST_BSINTARR: + case CAST_INTARRBS: + break; case CAST_BOOLINT: llvm_value_rvalue(c, value); value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "boolsi"); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 9baed5880..a2bd7614e 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1676,16 +1676,13 @@ static inline Decl *parse_protocol_declaration(ParseContext *c) advance_and_verify(c, TOKEN_PROTOCOL); Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_PROTOCOL); if (!consume_type_name(c, "protocol")) return poisoned_decl; - const char **parents = NULL; + TypeInfo **parents = NULL; if (try_consume(c, TOKEN_COLON)) { do { - vec_add(parents, symstr(c)); - if (!try_consume(c, TOKEN_TYPE_IDENT)) - { - RETURN_SEMA_ERROR_HERE("A protocol name was expected here."); - } + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), poisoned_decl); + vec_add(parents, type); } while (try_consume(c, TOKEN_COMMA)); } decl->protocol_decl.parents = parents; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index da055596d..aa9947a1e 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -978,6 +978,22 @@ static bool rule_ptr_to_protocol(CastContext *cc, bool is_explicit, bool is_sile type_quoted_error_string(cc->expr->type), type_quoted_error_string(cc->to_type)); } +static bool rule_protocol_to_protocol(CastContext *cc, bool is_explicit, bool is_silent) +{ + if (is_explicit) return true; + + 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) + 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)); +} + static bool rule_ptr_to_infer(CastContext *cc, bool is_explicit, bool is_silent) { if (cc->to->type_kind != TYPE_POINTER) return sema_cast_error(cc, false, is_silent); @@ -1443,6 +1459,26 @@ static void cast_expand_to_vec(Expr *expr, Type *type) insert_runtime_cast(expr, CAST_EXPVEC, type); } +static void cast_bitstruct_to_int_arr(Expr *expr, Type *type) +{ + if (expr->expr_kind == EXPR_CAST && expr->cast_expr.kind == CAST_INTARRBS) + { + expr_replace(expr, exprptr(expr->cast_expr.expr)); + return; + } + insert_runtime_cast(expr, CAST_BSINTARR, type); +} + +static void cast_int_arr_to_bitstruct(Expr *expr, Type *type) +{ + if (expr->expr_kind == EXPR_CAST && expr->cast_expr.kind == CAST_BSINTARR) + { + expr_replace(expr, exprptr(expr->cast_expr.expr)); + return; + } + insert_runtime_cast(expr, CAST_INTARRBS, type); +} + /** * Cast a signed or unsigned integer -> floating point, using CAST_INTFP * for runtime, otherwise do const transformation. @@ -1841,7 +1877,9 @@ static void cast_typeid_to_bool(Expr *expr, Type *to_type) } #define XX2XX &cast_retype -#define EX2VC &cast_expand_to_vec +#define BS2IA &cast_bitstruct_to_int_arr +#define IA2BS &cast_int_arr_to_bitstruct +#define EX2VC &cast_expand_to_vec #define BO2IN &cast_bool_to_int #define BO2FP &cast_bool_to_float #define IN2BO &cast_int_to_bool @@ -1911,8 +1949,8 @@ static void cast_typeid_to_bool(Expr *expr, Type *to_type) #define RSAFE &rule_sa_to_infer /* Subarray -> infer (only if subarray is constant or can infer) */ #define RVAFE &rule_vecarr_to_infer /* Vec/arr -> infer (if base matches) */ #define RPTFE &rule_ptr_to_infer /* Ptr -> infer (if pointee may infer) */ -#define RPTPR &rule_ptr_to_protocol /* Ptr -> Protocol if the pointee implements it */ - +#define RPTPC &rule_ptr_to_protocol /* Ptr -> Protocol if the pointee implements it */ +#define RPCPC &rule_protocol_to_protocol /* Protocol -> Protocol if the latter implements all of the former */ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { // void, wildc, bool, int, float, ptr, sarr, vec, bitst, distc, array, strct, union, any, prot, fault, enum, typid, afaul, voidp, arrpt, infer (to) {_NA__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // VOID (from) @@ -1920,7 +1958,7 @@ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { {REXPL, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BOOL {REXPL, _NO__, REXPL, RIFIF, RINFL, RINPT, _NO__, ROKOK, RINBS, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RINEN, _NO__, _NO__, RINPT, RINPT, _NO__}, // INT {REXPL, _NO__, REXPL, REXPL, RIFIF, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // FLOAT - {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPTPR, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE}, // PTR + {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPTPC, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE}, // PTR {REXPL, _NO__, REXPL, _NO__, _NO__, RSAPT, RSASA, RSAVA, _NO__, RXXDI, RSAVA, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RSAPT, RSAFE}, // SARRAY {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVCVC, _NO__, RXXDI, RVCAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE}, // VECTOR {REXPL, _NO__, _NO__, RBSIN, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, RBSAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BITSTRUCT @@ -1929,7 +1967,7 @@ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { {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__, ROKOK, _NA__, _NO__, _NO__, _NO__, _NO__, REXPL, REXPL, _NO__}, // PROTOCOL + {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 {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NA__, _NO__, REXPL, REXPL, _NO__}, // TYPEID @@ -1944,14 +1982,14 @@ CastFunction cast_function[CONV_LAST + 1][CONV_LAST + 1] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // VOID (from) {XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0 }, // WILDCARD {XX2VO, 0, 0, BO2IN, BO2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BOOL - {XX2VO, 0, IN2BO, IN2IN, IN2FP, IN2PT, 0, EX2VC, XX2XX, 0, 0, 0, 0, 0, 0, 0, IN2EN, 0, 0, IN2PT, IN2PT, 0 }, // INT + {XX2VO, 0, IN2BO, IN2IN, IN2FP, IN2PT, 0, EX2VC, IA2BS, 0, 0, 0, 0, 0, 0, 0, IN2EN, 0, 0, IN2PT, IN2PT, 0 }, // INT {XX2VO, 0, FP2BO, FP2IN, FP2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // FLOAT {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE }, // PTR {XX2VO, 0, SA2BO, 0, 0, SA2PT, SA2SA, SA2VA, 0, 0, SA2VA, 0, 0, 0, 0, 0, 0, 0, 0, SA2PT, SA2PT, SA2FE }, // SARRAY {XX2VO, 0, 0, 0, 0, 0, 0, VC2VC, 0, 0, VC2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE }, // VECTOR - {XX2VO, 0, 0, XX2XX, 0, 0, 0, 0, 0, 0, XX2XX, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT + {XX2VO, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DISTINCT - {XX2VO, 0, 0, 0, 0, 0, 0, AR2VC, XX2XX, 0, AR2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE }, // ARRAY + {XX2VO, 0, 0, 0, 0, 0, 0, AR2VC, IA2BS, 0, AR2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE }, // ARRAY {XX2VO, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0 }, // STRUCT {XX2VO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // UNION {XX2VO, 0, AY2BO, 0, 0, AY2PT, 0, 0, 0, 0, 0, 0, 0, PT2PT, PT2PT, 0, 0, 0, 0, AY2PT, AY2PT, 0 }, // ANY diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7ce9e3db3..f4570edb4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -718,6 +718,15 @@ 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; unsigned count = vec_size(functions); for (unsigned i = 0; i < count; i++) @@ -736,23 +745,13 @@ static bool sema_analyse_protocol(SemaContext *context, Decl *decl, bool *erase_ } method->func_decl.attr_protocol_method = true; bool erase = false; - Decl **params = method->func_decl.signature.params; - if (!vec_size(params)) - { - SEMA_ERROR(method, "A protocol method needs to contain a `&self` argument."); - return decl_poison(decl); - } - Decl *first = params[0]; - if (first->var.type_info || first->var.kind != VARDECL_PARAM_REF) - { - SEMA_ERROR(first, "The first argument must be `&self`."); - return decl_poison(decl); - } + Decl *first = decl_new_var(kw_self, decl->span, NULL, VARDECL_PARAM); first->type = type_voidptr; first->var.kind = VARDECL_PARAM; first->unit = context->unit; first->resolve_status = RESOLVE_DONE; first->alignment = type_abi_alignment(type_voidptr); + 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) @@ -1675,6 +1674,17 @@ 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) + if (method->name == name) return method; + FOREACH_END(); + FOREACH_BEGIN(TypeInfo *parent_type, protocol->protocol_decl.parents) + Decl *res = sema_protocol_method_by_name(parent_type->type->decl, name); + if (res) return res; + FOREACH_END(); + return NULL; +} static inline Decl *sema_find_protocol_for_method(SemaContext *context, Type *parent_type, Decl *method) { @@ -1694,19 +1704,20 @@ static inline Decl *sema_find_protocol_for_method(SemaContext *context, Type *pa Decl *first_match = NULL; Decl *first_protocol = NULL; FOREACH_BEGIN(TypeInfo *proto, parent_type->decl->protocols) - FOREACH_BEGIN(Decl *proto_method, proto->type->decl->protocol_decl.protocol_methods) - if (proto_method->name == name) - { - if (first_match) - { - SEMA_ERROR(method, "Both '%s' and '%s' protocols have a method matching '%s', which prevents it from being implemented.", - first_protocol->name, proto->type->name, name); - return poisoned_decl; - } - first_match = proto_method; - first_protocol = proto->type->decl; - } - FOREACH_END(); + Decl *protocol = proto->type->decl; + Decl *match = sema_protocol_method_by_name(protocol, name); + if (!match) continue; + if (first_match) + { + if (first_match->type->function.prototype->raw_type == match->type->function.prototype->raw_type) continue; + SEMA_ERROR(method, + "Both '%s' and '%s' protocols have a method matching '%s' but their signatures are different, " + "which prevents it from being implemented.", + first_protocol->name, protocol->name, name); + return NULL; + } + first_match = match; + first_protocol = protocol; FOREACH_END(); if (!first_match) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 6c741c767..94de702b3 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -4100,6 +4100,14 @@ CHECK_DEEPER: { Decl *ambiguous = NULL; member = sema_resolve_method(context->unit, decl, kw, &ambiguous, &private); + // Look at protocol parents + if (!member && decl->decl_kind == DECL_PROTOCOL) + { + FOREACH_BEGIN(TypeInfo *parent_protocol, decl->protocol_decl.parents) + member = sema_resolve_method(context->unit, parent_protocol->type->decl, kw, &ambiguous, &private); + if (member) break; + FOREACH_END(); + } if (ambiguous) { SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, it may refer to method defined in '%s' or one in '%s'", @@ -4139,7 +4147,12 @@ CHECK_DEEPER: SEMA_ERROR(expr, "The method '%s' has private visibility.", kw); return false; } - SEMA_ERROR(expr, "There is no field or method '%s.%s'.", type_to_error_string(parent->type), kw); + 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); return false; } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index ef7bba7f5..97b69a4f5 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -69,6 +69,7 @@ const char *kw_out; const char *kw_ptr; const char *kw_pure; const char *kw_return; +const char *kw_self; const char *kw_std; const char *kw_std__core; const char *kw_std__core__types; @@ -161,6 +162,7 @@ void symtab_init(uint32_t capacity) kw_ptr = KW_DEF("ptr"); kw_pure = KW_DEF("pure"); KW_DEF("returns"); + kw_self = KW_DEF("self"); kw_std = KW_DEF("std"); kw_std__core = KW_DEF("std::core"); kw_std__core__types = KW_DEF("std::core::types"); diff --git a/test/test_suite/cast/cast_bitstruct_etc.c3t b/test/test_suite/cast/cast_bitstruct_etc.c3t new file mode 100644 index 000000000..bb47fea83 --- /dev/null +++ b/test/test_suite/cast/cast_bitstruct_etc.c3t @@ -0,0 +1,39 @@ +// #target: macos-x64 +module test; + +fn void! main() +{ + Enum e = ENUM1; + ushort x = (ushort)Foo{ .x = e }; + Foo z = (Foo)(ushort)Foo { .x = e }; +} + +enum Enum : char +{ + ENUM1, +} + +bitstruct Foo : ushort +{ + Enum x : 0..7; +} + +/* #expect: test.ll + + %e = alloca i8, align 1 + %x = alloca i16, align 2 + %literal = alloca i16, align 2 + %z = alloca i16, align 2 + store i8 0, ptr %e, align 1 + %0 = load i8, ptr %e, align 1 + %zext = zext i8 %0 to i16 + %1 = and i16 %zext, 255 + store i16 %1, ptr %literal, align 2 + %2 = load i16, ptr %literal, align 2 + store i16 %2, ptr %x, align 2 + %3 = load i8, ptr %e, align 1 + %zext1 = zext i8 %3 to i16 + %4 = and i16 %zext1, 255 + store i16 %4, ptr %z, align 2 + ret i64 0 +} \ No newline at end of file diff --git a/test/test_suite/dynamic/dynamic_mismatch.c3 b/test/test_suite/dynamic/dynamic_mismatch.c3 index d4a54ef65..f64fddecf 100644 --- a/test/test_suite/dynamic/dynamic_mismatch.c3 +++ b/test/test_suite/dynamic/dynamic_mismatch.c3 @@ -1,6 +1,6 @@ protocol TestProto { - fn int test(&self, int ag); + fn int test(int ag); } diff --git a/test/test_suite/dynamic/inherit.c3t b/test/test_suite/dynamic/inherit.c3t new file mode 100644 index 000000000..dea7b70c8 --- /dev/null +++ b/test/test_suite/dynamic/inherit.c3t @@ -0,0 +1,244 @@ +// #target: macos-x64 +module inherit; +import std::io; + +protocol Base +{ + fn void tesT(); +} + +protocol TestProto2 : Base +{ +} + +protocol TestProto : Base +{ + fn void hello(); +} + +fn void Test.tesT(&self) @dynamic +{ +} + +fn void Test.hello(&self) @dynamic +{ +} + +struct Test (TestProto, TestProto2) +{ + void* abc; +} + +fn void main() +{ + TestProto* z = malloc(Test); + z.tesT(); + Base* w = z; + w.tesT(); +} + +/* #expect: inherit.ll + + +@"$ct.inherit.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@std.core.mem.thread_allocator = external thread_local global ptr, align 8 +@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.panic_msg = internal constant [37 x i8] c"Unexpected fault '%s' was unwrapped!\00", align 1 +@.file = internal constant [7 x i8] c"mem.c3\00", align 1 +@.func = internal constant [5 x i8] c"main\00", align 1 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +@.panic_msg.1 = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 +@.file.2 = internal constant [11 x i8] c"inherit.c3\00", align 1 +@std.core.builtin.panic = external global ptr, align 8 +@"$ct.dyn.inherit.Test.tesT" = global { ptr, ptr, ptr } { ptr @inherit.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8 +@"$ct.dyn.inherit.Test.hello" = global { ptr, ptr, ptr } { ptr @inherit.Test.hello, ptr @"$sel.hello", ptr null }, align 8 +@"$sel.hello" = linkonce_odr constant [6 x i8] c"hello\00", align 1 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }] + +define void @inherit.main() #0 { +entry: + %z = alloca %"any*", align 8 + %using = alloca ptr, align 8 + %error_var = alloca i64, align 8 + %using1 = alloca ptr, align 8 + %allocator = alloca ptr, align 8 + %retparam = alloca ptr, align 8 + %varargslots = alloca [1 x %"any*"], align 16 + %indirectarg = alloca %"any*[]", align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 + %w = alloca %"any*", align 8 + %.inlinecache2 = alloca ptr, align 8 + %.cachedtype3 = alloca ptr, align 8 + store ptr null, ptr %.cachedtype3, align 8 + store ptr null, ptr %.cachedtype, align 8 + %0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 + store ptr %0, ptr %using, align 8 + %1 = load ptr, ptr %using, align 8 + store ptr %1, ptr %using1, align 8 + %2 = load ptr, ptr %using1, align 8 + store ptr %2, ptr %allocator, align 8 + %3 = load ptr, ptr %allocator, align 8 + %4 = getelementptr inbounds %Allocator, ptr %3, i32 0, i32 0 + %5 = load ptr, ptr %4, align 8 + %6 = load ptr, ptr %allocator, align 8 + %7 = call i64 %5(ptr %retparam, ptr %6, i64 8, i64 0, i64 0, ptr null, i32 0) + %not_err = icmp eq i64 %7, 0 + %8 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %8, label %after_check, label %assign_optional + +assign_optional: ; preds = %entry + store i64 %7, ptr %error_var, align 8 + br label %panic_block + +after_check: ; preds = %entry + %9 = load ptr, ptr %retparam, align 8 + br label %noerr_block + +panic_block: ; preds = %assign_optional + %10 = insertvalue %"any*" undef, ptr %error_var, 0 + %11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1 + %12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0 + store %"any*" %11, ptr %12, align 16 + %13 = insertvalue %"any*[]" undef, ptr %varargslots, 0 + %"#temp#" = insertvalue %"any*[]" %13, i64 1, 1 + store %"any*[]" %"#temp#", ptr %indirectarg, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 4, i32 392, ptr byval(%"any*[]") align 8 %indirectarg) + unreachable + +noerr_block: ; preds = %after_check + %14 = insertvalue %"any*" undef, ptr %9, 0 + %15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1 + store %"any*" %15, ptr %z, align 8 + %16 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1 + %17 = load i64, ptr %16, align 8 + %18 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0 + %19 = inttoptr i64 %17 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %20 = icmp eq ptr %19, %type + br i1 %20, label %cache_hit, label %cache_miss + +cache_miss: ; preds = %noerr_block + %21 = getelementptr inbounds %.introspect, ptr %19, i32 0, i32 2 + %22 = load ptr, ptr %21, align 8 + %23 = call ptr @.dyn_search(ptr %22, ptr @"$sel.tesT") + store ptr %23, ptr %.inlinecache, align 8 + store ptr %19, ptr %.cachedtype, align 8 + br label %24 + +cache_hit: ; preds = %noerr_block + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %24 + +24: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %23, %cache_miss ] + %25 = icmp eq ptr %fn_phi, null + br i1 %25, label %missing_function, label %match + +missing_function: ; preds = %24 + %26 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %26(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 10, ptr @.func, i64 4, i32 34) + unreachable + +match: ; preds = %24 + %27 = load ptr, ptr %18, align 8 + call void %fn_phi(ptr %27) + %28 = load %"any*", ptr %z, align 8 + store %"any*" %28, ptr %w, align 8 + %29 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1 + %30 = load i64, ptr %29, align 8 + %31 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0 + %32 = inttoptr i64 %30 to ptr + %type4 = load ptr, ptr %.cachedtype3, align 8 + %33 = icmp eq ptr %32, %type4 + br i1 %33, label %cache_hit6, label %cache_miss5 + +cache_miss5: ; preds = %match + %34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2 + %35 = load ptr, ptr %34, align 8 + %36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT") + store ptr %36, ptr %.inlinecache2, align 8 + store ptr %32, ptr %.cachedtype3, align 8 + br label %37 + +cache_hit6: ; preds = %match + %cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8 + br label %37 + +37: ; preds = %cache_hit6, %cache_miss5 + %fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ] + %38 = icmp eq ptr %fn_phi8, null + br i1 %38, label %missing_function9, label %match10 + +missing_function9: ; preds = %37 + %39 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %39(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 10, ptr @.func, i64 4, i32 36) + unreachable + +match10: ; preds = %37 + %40 = load ptr, ptr %31, align 8 + call void %fn_phi8(ptr %40) + ret void +} + + +define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { +entry: + br label %check + +check: ; preds = %no_match, %entry + %2 = phi ptr [ %0, %entry ], [ %9, %no_match ] + %3 = icmp eq ptr %2, null + br i1 %3, label %missing_function, label %compare + +missing_function: ; preds = %check + ret ptr null + +compare: ; preds = %check + %4 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 1 + %5 = load ptr, ptr %4, align 8 + %6 = icmp eq ptr %5, %1 + br i1 %6, label %match, label %no_match + +match: ; preds = %compare + %7 = load ptr, ptr %2, align 8 + ret ptr %7 + +no_match: ; preds = %compare + %8 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 2 + %9 = load ptr, ptr %8, align 8 + br label %check +} + +define internal void @.static_initialize.0() { +entry: + br label %dtable_check + +dtable_check: ; preds = %dtable_next, %entry + %dtable_ref = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.inherit.Test", i32 0, i32 2), %entry ], [ %next_dtable_ref, %dtable_next ] + %dtable_ptr = load ptr, ptr %dtable_ref, align 8 + %0 = icmp eq ptr %dtable_ptr, null + br i1 %0, label %dtable_found, label %dtable_next + +dtable_next: ; preds = %dtable_check + %next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2 + br label %dtable_check + +dtable_found: ; preds = %dtable_check + store ptr @"$ct.dyn.inherit.Test.tesT", ptr %dtable_ref, align 8 + br label %dtable_check1 + +dtable_check1: ; preds = %dtable_next4, %dtable_found + %dtable_ref2 = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.inherit.Test", i32 0, i32 2), %dtable_found ], [ %next_dtable_ref5, %dtable_next4 ] + %dtable_ptr3 = load ptr, ptr %dtable_ref2, align 8 + %1 = icmp eq ptr %dtable_ptr3, null + br i1 %1, label %dtable_found6, label %dtable_next4 + +dtable_next4: ; preds = %dtable_check1 + %next_dtable_ref5 = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr3, i32 0, i32 2 + br label %dtable_check1 + +dtable_found6: ; preds = %dtable_check1 + store ptr @"$ct.dyn.inherit.Test.hello", ptr %dtable_ref2, align 8 + ret void +} diff --git a/test/test_suite/dynamic/overlapping_function.c3t b/test/test_suite/dynamic/overlapping_function.c3t new file mode 100644 index 000000000..f2eabc300 --- /dev/null +++ b/test/test_suite/dynamic/overlapping_function.c3t @@ -0,0 +1,210 @@ +// #target: macos-x64 +module overlap; +import std::io; + +protocol TestProto +{ + fn void tesT(); +} + +protocol TestProto2 +{ + fn void tesT(); +} + +fn void Test.tesT(&self) @dynamic +{ +} + +fn void Test.foo(&self) @dynamic {} + +struct Test (TestProto, TestProto2) +{ + void* abc; +} + +fn void main() +{ + TestProto* z = malloc(Test); + z.tesT(); + TestProto2* w = (TestProto2*)z; + w.tesT(); +} + +/* #expect: overlap.ll + +@"$ct.overlap.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@std.core.mem.thread_allocator = external thread_local global ptr, align 8 +@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.panic_msg = internal constant [37 x i8] c"Unexpected fault '%s' was unwrapped!\00", align 1 +@.file = internal constant [7 x i8] c"mem.c3\00", align 1 +@.func = internal constant [5 x i8] c"main\00", align 1 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +@.panic_msg.1 = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 +@.file.2 = internal constant [24 x i8] c"overlapping_function.c3\00", align 1 +@std.core.builtin.panic = external global ptr, align 8 +@"$ct.dyn.overlap.Test.tesT" = global { ptr, ptr, ptr } { ptr @overlap.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8 +@"$ct.dyn.overlap.Test.foo" = global { ptr, ptr, ptr } { ptr @overlap.Test.foo, ptr @"$sel.foo", ptr null }, align 8 +@"$sel.foo" = linkonce_odr constant [4 x i8] c"foo\00", align 1 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }] + +; Function Attrs: nounwind +define void @overlap.main() #0 { +entry: + %z = alloca %"any*", align 8 + %using = alloca ptr, align 8 + %error_var = alloca i64, align 8 + %using1 = alloca ptr, align 8 + %allocator = alloca ptr, align 8 + %retparam = alloca ptr, align 8 + %varargslots = alloca [1 x %"any*"], align 16 + %indirectarg = alloca %"any*[]", align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 + %w = alloca %"any*", align 8 + %.inlinecache2 = alloca ptr, align 8 + %.cachedtype3 = alloca ptr, align 8 + store ptr null, ptr %.cachedtype3, align 8 + store ptr null, ptr %.cachedtype, align 8 + %0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 + store ptr %0, ptr %using, align 8 + %1 = load ptr, ptr %using, align 8 + store ptr %1, ptr %using1, align 8 + %2 = load ptr, ptr %using1, align 8 + store ptr %2, ptr %allocator, align 8 + %3 = load ptr, ptr %allocator, align 8 + %4 = getelementptr inbounds %Allocator, ptr %3, i32 0, i32 0 + %5 = load ptr, ptr %4, align 8 + %6 = load ptr, ptr %allocator, align 8 + %7 = call i64 %5(ptr %retparam, ptr %6, i64 8, i64 0, i64 0, ptr null, i32 0) + %not_err = icmp eq i64 %7, 0 + %8 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %8, label %after_check, label %assign_optional + +assign_optional: ; preds = %entry + store i64 %7, ptr %error_var, align 8 + br label %panic_block + +after_check: ; preds = %entry + %9 = load ptr, ptr %retparam, align 8 + br label %noerr_block + +panic_block: ; preds = %assign_optional + %10 = insertvalue %"any*" undef, ptr %error_var, 0 + %11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1 + %12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0 + store %"any*" %11, ptr %12, align 16 + %13 = insertvalue %"any*[]" undef, ptr %varargslots, 0 + %"#temp#" = insertvalue %"any*[]" %13, i64 1, 1 + store %"any*[]" %"#temp#", ptr %indirectarg, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 4, i32 392, ptr byval(%"any*[]") align 8 %indirectarg) + unreachable + +noerr_block: ; preds = %after_check + %14 = insertvalue %"any*" undef, ptr %9, 0 + %15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.overlap.Test" to i64), 1 + store %"any*" %15, ptr %z, align 8 + %16 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1 + %17 = load i64, ptr %16, align 8 + %18 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0 + %19 = inttoptr i64 %17 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %20 = icmp eq ptr %19, %type + br i1 %20, label %cache_hit, label %cache_miss + +cache_miss: ; preds = %noerr_block + %21 = getelementptr inbounds %.introspect, ptr %19, i32 0, i32 2 + %22 = load ptr, ptr %21, align 8 + %23 = call ptr @.dyn_search(ptr %22, ptr @"$sel.tesT") + store ptr %23, ptr %.inlinecache, align 8 + store ptr %19, ptr %.cachedtype, align 8 + br label %24 + +cache_hit: ; preds = %noerr_block + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %24 + +24: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %23, %cache_miss ] + %25 = icmp eq ptr %fn_phi, null + br i1 %25, label %missing_function, label %match + +missing_function: ; preds = %24 + %26 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %26(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 23, ptr @.func, i64 4, i32 28) + unreachable + +match: ; preds = %24 + %27 = load ptr, ptr %18, align 8 + call void %fn_phi(ptr %27) + %28 = load %"any*", ptr %z, align 8 + store %"any*" %28, ptr %w, align 8 + %29 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1 + %30 = load i64, ptr %29, align 8 + %31 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0 + %32 = inttoptr i64 %30 to ptr + %type4 = load ptr, ptr %.cachedtype3, align 8 + %33 = icmp eq ptr %32, %type4 + br i1 %33, label %cache_hit6, label %cache_miss5 + +cache_miss5: ; preds = %match + %34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2 + %35 = load ptr, ptr %34, align 8 + %36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT") + store ptr %36, ptr %.inlinecache2, align 8 + store ptr %32, ptr %.cachedtype3, align 8 + br label %37 + +cache_hit6: ; preds = %match + %cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8 + br label %37 + +37: ; preds = %cache_hit6, %cache_miss5 + %fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ] + %38 = icmp eq ptr %fn_phi8, null + br i1 %38, label %missing_function9, label %match10 + +missing_function9: ; preds = %37 + %39 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %39(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 23, ptr @.func, i64 4, i32 30) + unreachable + +match10: ; preds = %37 + %40 = load ptr, ptr %31, align 8 + call void %fn_phi8(ptr %40) + ret void +} + + +define internal void @.static_initialize.0() { +entry: + br label %dtable_check + +dtable_check: ; preds = %dtable_next, %entry + %dtable_ref = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.overlap.Test", i32 0, i32 2), %entry ], [ %next_dtable_ref, %dtable_next ] + %dtable_ptr = load ptr, ptr %dtable_ref, align 8 + %0 = icmp eq ptr %dtable_ptr, null + br i1 %0, label %dtable_found, label %dtable_next + +dtable_next: ; preds = %dtable_check + %next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2 + br label %dtable_check + +dtable_found: ; preds = %dtable_check + store ptr @"$ct.dyn.overlap.Test.tesT", ptr %dtable_ref, align 8 + br label %dtable_check1 + +dtable_check1: ; preds = %dtable_next4, %dtable_found + %dtable_ref2 = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.overlap.Test", i32 0, i32 2), %dtable_found ], [ %next_dtable_ref5, %dtable_next4 ] + %dtable_ptr3 = load ptr, ptr %dtable_ref2, align 8 + %1 = icmp eq ptr %dtable_ptr3, null + br i1 %1, label %dtable_found6, label %dtable_next4 + +dtable_next4: ; preds = %dtable_check1 + %next_dtable_ref5 = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr3, i32 0, i32 2 + br label %dtable_check1 + +dtable_found6: ; preds = %dtable_check1 + store ptr @"$ct.dyn.overlap.Test.foo", ptr %dtable_ref2, align 8 + ret void +} diff --git a/test/unit/regression/liveness_any.c3 b/test/unit/regression/liveness_any.c3 index f2d3d8a36..f652fa34b 100644 --- a/test/unit/regression/liveness_any.c3 +++ b/test/unit/regression/liveness_any.c3 @@ -2,7 +2,7 @@ module liveness; protocol TestProto { - fn void tesT(&self); + fn void tesT(); } fn void Test.tesT(&self) @dynamic