From b65a1ced0e4c645648891f425e431910c8737bbc Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Fri, 14 Aug 2026 17:15:36 +0200 Subject: [PATCH 1/7] Snapshot conversion and codegen for unfilled witness slots Add a test fixture, witness_forward.act, with golden files for the converted AST (pass 3) and the generated C (pass 9). The fixture has a protocol PA with two methods, one static (same) and one instance method (total). Protocols PB and PC both inherit PA. Class Thing gets two extensions. The first, for PB, implements all the methods. The second, for PC, implements only PC's own method, because the methods of PA are already implemented by the first extension and may not be implemented again. The golden files record what the compiler produces today for the witness class of the second extension, PCD_Thing. In the converted AST, same and total are just signatures with no implementation. In the C code, CodeGen fills the two empty method table slots with $forward wrappers that call the corresponding methods of PBD_Thing. But the class is still treated as abstract, so no PCD_ThingG_new constructor is generated, and any program that needs to create a PCD_Thing witness fails to compile. The next commit changes both outputs. --- .../lib/test/3-types/witness_forward.input | 25 ++ .../lib/test/3-types/witness_forward.output | 40 +++ compiler/lib/test/9-codegen/witness_forward.c | 253 ++++++++++++++++++ compiler/lib/test/9-codegen/witness_forward.h | 138 ++++++++++ .../lib/test/9-codegen/witness_forward.input | 47 ++++ compiler/lib/test/ActonSpec.hs | 2 + compiler/lib/test/src/witness_forward.act | 33 +++ 7 files changed, 538 insertions(+) create mode 100644 compiler/lib/test/3-types/witness_forward.input create mode 100644 compiler/lib/test/3-types/witness_forward.output create mode 100644 compiler/lib/test/9-codegen/witness_forward.c create mode 100644 compiler/lib/test/9-codegen/witness_forward.h create mode 100644 compiler/lib/test/9-codegen/witness_forward.input create mode 100644 compiler/lib/test/src/witness_forward.act diff --git a/compiler/lib/test/3-types/witness_forward.input b/compiler/lib/test/3-types/witness_forward.input new file mode 100644 index 000000000..65c7650a6 --- /dev/null +++ b/compiler/lib/test/3-types/witness_forward.input @@ -0,0 +1,25 @@ +# recursive group: +protocol PA: + @static + same : (Self, Self) -> __builtin__.bool + total : () -> __builtin__.int +protocol PB (PA): + beta : () -> __builtin__.int +protocol PC (PA): + gamma : () -> __builtin__.int +class Thing (__builtin__.object): + n : __builtin__.int + T_13w def __init__ (self : T_5w, n : __builtin__.int) -> T_9w: + self.n = n +extension Thing (PB): + @static + T_17w def same (a : Thing, b : Thing) -> __builtin__.bool: + return a.n == b.n + T_29w def total (self : T_21w) -> T_25w: + return self.n + T_41w def beta (self : T_33w) -> T_37w: + return 1 +extension Thing (PC): + T_53w def gamma (self : T_45w) -> T_49w: + return 2 +# (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/3-types/witness_forward.output b/compiler/lib/test/3-types/witness_forward.output new file mode 100644 index 000000000..e8461ba57 --- /dev/null +++ b/compiler/lib/test/3-types/witness_forward.output @@ -0,0 +1,40 @@ +# recursive group: +class PA[G_self] (__builtin__.value): + pure def __init__ (W_self : Self) -> None: + same : (G_self, G_self) -> __builtin__.bool + total : (G_self) -> __builtin__.int +class PB[G_self] (PA[G_self], __builtin__.value): + pure def __init__ (W_self : Self) -> None: + PA.__init__@[G_self](W_self) + beta : (G_self) -> __builtin__.int +class PC[G_self] (PA[G_self], __builtin__.value): + pure def __init__ (W_self : Self) -> None: + PA.__init__@[G_self](W_self) + gamma : (G_self) -> __builtin__.int +class Thing (__builtin__.object, __builtin__.value): + pure def __get_attr__ (self : Self, name : __builtin__.str) -> ?__builtin__.value: + return None + pure def G_init (self : Self) -> None: + pass + @property + n : __builtin__.int + pure def __init__ (self : Self, n : __builtin__.int) -> None: + self.n = n +class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): + W_PA_26: __builtin__.Number[__builtin__.int] = __builtin__.IntegralD_int() + pure def __init__ (W_self : Self) -> None: + PB.__init__@[Thing](W_self) + pure def same (W_self : Self, a : Thing, b : Thing) -> __builtin__.bool: + W_PA_15: __builtin__.Eq[__builtin__.int] = __builtin__.OrdD_int() + return W_PA_15.__eq__(a.n, b.n) + pure def total (W_self : Self, self : Thing) -> __builtin__.int: + return self.n + pure def beta (W_self : Self, self : Thing) -> __builtin__.int: + return W_PA_26.__fromatom__(1) +class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): + W_PA_30: __builtin__.Number[__builtin__.int] = __builtin__.IntegralD_int() + pure def __init__ (W_self : Self) -> None: + PC.__init__@[Thing](W_self) + pure def gamma (W_self : Self, self : Thing) -> __builtin__.int: + return W_PA_30.__fromatom__(2) +# (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.c b/compiler/lib/test/9-codegen/witness_forward.c new file mode 100644 index 000000000..8ef35386a --- /dev/null +++ b/compiler/lib/test/9-codegen/witness_forward.c @@ -0,0 +1,253 @@ +/* Acton impl hash: test-hash */ +#include "rts/common.h" +#include "out/types/witness_forward.h" +B_NoneType witness_forwardQ_PAD___init__ (witness_forwardQ_PA W_self) { + return B_None; +} +void witness_forwardQ_PAD___serialize__ (witness_forwardQ_PA self, $Serial$state state) { +} +witness_forwardQ_PA witness_forwardQ_PAD___deserialize__ (witness_forwardQ_PA self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_PA)); + self->$class = &witness_forwardQ_PAG_methods; + return self; + } + self = $DNEW(witness_forwardQ_PA, state); + } + return self; +} +struct witness_forwardQ_PAG_class witness_forwardQ_PAG_methods; +B_NoneType witness_forwardQ_PBD___init__ (witness_forwardQ_PB W_self) { + ((B_NoneType (*) (witness_forwardQ_PA))witness_forwardQ_PAG_methods.__init__)(((witness_forwardQ_PA)W_self)); + return B_None; +} +void witness_forwardQ_PBD___serialize__ (witness_forwardQ_PB self, $Serial$state state) { +} +witness_forwardQ_PB witness_forwardQ_PBD___deserialize__ (witness_forwardQ_PB self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_PB)); + self->$class = &witness_forwardQ_PBG_methods; + return self; + } + self = $DNEW(witness_forwardQ_PB, state); + } + return self; +} +struct witness_forwardQ_PBG_class witness_forwardQ_PBG_methods; +B_NoneType witness_forwardQ_PCD___init__ (witness_forwardQ_PC W_self) { + ((B_NoneType (*) (witness_forwardQ_PA))witness_forwardQ_PAG_methods.__init__)(((witness_forwardQ_PA)W_self)); + return B_None; +} +void witness_forwardQ_PCD___serialize__ (witness_forwardQ_PC self, $Serial$state state) { +} +witness_forwardQ_PC witness_forwardQ_PCD___deserialize__ (witness_forwardQ_PC self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_PC)); + self->$class = &witness_forwardQ_PCG_methods; + return self; + } + self = $DNEW(witness_forwardQ_PC, state); + } + return self; +} +struct witness_forwardQ_PCG_class witness_forwardQ_PCG_methods; +B_value witness_forwardQ_ThingD___get_attr__ (witness_forwardQ_Thing self, B_str name) { + if (B_OrdD_strD___eq__(B_OrdD_strG_witness, name, to$str("n"))) { + return (B_value)toB_int(self->n); + } + return B_None; +} +B_NoneType witness_forwardQ_ThingG_init (witness_forwardQ_Thing self) { + return B_None; +} +#line 19 "test/src/witness_forward.act" +B_NoneType witness_forwardQ_ThingD___init__ (witness_forwardQ_Thing self, int64_t n) { + #line 20 "test/src/witness_forward.act" + ((witness_forwardQ_Thing)(self))->n = n; + return B_None; +} +void witness_forwardQ_ThingD___serialize__ (witness_forwardQ_Thing self, $Serial$state state) { + $val_serialize(I64_ID, &self->n, state); +} +witness_forwardQ_Thing witness_forwardQ_ThingD___deserialize__ (witness_forwardQ_Thing self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_Thing)); + self->$class = &witness_forwardQ_ThingG_methods; + return self; + } + self = $DNEW(witness_forwardQ_Thing, state); + } + $tmp = $val_deserialize(state); + memcpy(&self->n, &$tmp, sizeof(self->n)); + return self; +} +witness_forwardQ_Thing witness_forwardQ_ThingG_new(int64_t G_1) { + witness_forwardQ_Thing $tmp = acton_malloc(sizeof(struct witness_forwardQ_Thing)); + $tmp->$class = &witness_forwardQ_ThingG_methods; + witness_forwardQ_ThingG_methods.G_init($tmp); + witness_forwardQ_ThingG_methods.__init__($tmp, G_1); + return $tmp; +} +struct witness_forwardQ_ThingG_class witness_forwardQ_ThingG_methods; +B_NoneType witness_forwardQ_PBD_ThingD___init__ (witness_forwardQ_PBD_Thing W_self) { + ((B_NoneType (*) (witness_forwardQ_PB))witness_forwardQ_PBG_methods.__init__)(((witness_forwardQ_PB)W_self)); + return B_None; +} +#line 23 "test/src/witness_forward.act" +bool witness_forwardQ_PBD_ThingD_same (witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing a, witness_forwardQ_Thing b) { + bool N_tmp = (((int64_t)((witness_forwardQ_Thing)(a))->n) == ((int64_t)((witness_forwardQ_Thing)(b))->n)); + #line 25 "test/src/witness_forward.act" + return N_tmp; +} +#line 26 "test/src/witness_forward.act" +int64_t witness_forwardQ_PBD_ThingD_total (witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self) { + #line 27 "test/src/witness_forward.act" + return ((int64_t)((witness_forwardQ_Thing)(self))->n); +} +#line 28 "test/src/witness_forward.act" +int64_t witness_forwardQ_PBD_ThingD_beta (witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self) { + int64_t N_1tmp = 1LL; + #line 29 "test/src/witness_forward.act" + return N_1tmp; +} +void witness_forwardQ_PBD_ThingD___serialize__ (witness_forwardQ_PBD_Thing self, $Serial$state state) { +} +witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingD___deserialize__ (witness_forwardQ_PBD_Thing self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_PBD_Thing)); + self->$class = &witness_forwardQ_PBD_ThingG_methods; + return self; + } + self = $DNEW(witness_forwardQ_PBD_Thing, state); + } + return self; +} +witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingG_new() { + witness_forwardQ_PBD_Thing $tmp = acton_malloc(sizeof(struct witness_forwardQ_PBD_Thing)); + $tmp->$class = &witness_forwardQ_PBD_ThingG_methods; + witness_forwardQ_PBD_ThingG_methods.__init__($tmp); + return $tmp; +} +struct witness_forwardQ_PBD_ThingG_class witness_forwardQ_PBD_ThingG_methods; +B_NoneType witness_forwardQ_PCD_ThingD___init__ (witness_forwardQ_PCD_Thing W_self) { + ((B_NoneType (*) (witness_forwardQ_PC))witness_forwardQ_PCG_methods.__init__)(((witness_forwardQ_PC)W_self)); + return B_None; +} +#line 32 "test/src/witness_forward.act" +int64_t witness_forwardQ_PCD_ThingD_gamma (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing self) { + int64_t N_2tmp = 2LL; + #line 33 "test/src/witness_forward.act" + return N_2tmp; +} +static bool witness_forwardQ_PCD_ThingD_$forwardD_same(witness_forwardQ_PCD_Thing fw_0, witness_forwardQ_Thing fw_1, witness_forwardQ_Thing fw_2) { + witness_forwardQ_PBD_Thing fw_provider = (witness_forwardQ_PBD_Thing)(witness_forwardQ_PBD_ThingG_new()); + return ((bool (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))fw_provider->$class->same)(fw_provider, fw_1, fw_2); +} +static int64_t witness_forwardQ_PCD_ThingD_$forwardD_total(witness_forwardQ_PCD_Thing fw_0, witness_forwardQ_Thing fw_1) { + witness_forwardQ_PBD_Thing fw_provider = (witness_forwardQ_PBD_Thing)(witness_forwardQ_PBD_ThingG_new()); + return ((int64_t (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing))fw_provider->$class->total)(fw_provider, fw_1); +} +void witness_forwardQ_PCD_ThingD___serialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { +} +witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { + $WORD $tmp; + if (!self) { + if (!state) { + self = acton_malloc(sizeof(struct witness_forwardQ_PCD_Thing)); + self->$class = &witness_forwardQ_PCD_ThingG_methods; + return self; + } + self = $DNEW(witness_forwardQ_PCD_Thing, state); + } + return self; +} +struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_methods; +int witness_forwardQ_done$ = 0; +void witness_forwardQ___init__ () { + if (witness_forwardQ_done$) return; + witness_forwardQ_done$ = 1; + { + witness_forwardQ_PAG_methods.$GCINFO = "witness_forwardQ_PA"; + witness_forwardQ_PAG_methods.$superclass = ($SuperG_class)&B_valueG_methods; + witness_forwardQ_PAG_methods.__bool__ = (bool (*) (witness_forwardQ_PA))B_valueG_methods.__bool__; + witness_forwardQ_PAG_methods.__str__ = (B_str (*) (witness_forwardQ_PA))B_valueG_methods.__str__; + witness_forwardQ_PAG_methods.__repr__ = (B_str (*) (witness_forwardQ_PA))B_valueG_methods.__repr__; + witness_forwardQ_PAG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PA))witness_forwardQ_PAD___init__; + witness_forwardQ_PAG_methods.__serialize__ = witness_forwardQ_PAD___serialize__; + witness_forwardQ_PAG_methods.__deserialize__ = witness_forwardQ_PAD___deserialize__; + $register(&witness_forwardQ_PAG_methods); + } + { + witness_forwardQ_PBG_methods.$GCINFO = "witness_forwardQ_PB"; + witness_forwardQ_PBG_methods.$superclass = ($SuperG_class)&witness_forwardQ_PAG_methods; + witness_forwardQ_PBG_methods.__bool__ = (bool (*) (witness_forwardQ_PB))B_valueG_methods.__bool__; + witness_forwardQ_PBG_methods.__str__ = (B_str (*) (witness_forwardQ_PB))B_valueG_methods.__str__; + witness_forwardQ_PBG_methods.__repr__ = (B_str (*) (witness_forwardQ_PB))B_valueG_methods.__repr__; + witness_forwardQ_PBG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PB))witness_forwardQ_PBD___init__; + witness_forwardQ_PBG_methods.__serialize__ = witness_forwardQ_PBD___serialize__; + witness_forwardQ_PBG_methods.__deserialize__ = witness_forwardQ_PBD___deserialize__; + $register(&witness_forwardQ_PBG_methods); + } + { + witness_forwardQ_PCG_methods.$GCINFO = "witness_forwardQ_PC"; + witness_forwardQ_PCG_methods.$superclass = ($SuperG_class)&witness_forwardQ_PAG_methods; + witness_forwardQ_PCG_methods.__bool__ = (bool (*) (witness_forwardQ_PC))B_valueG_methods.__bool__; + witness_forwardQ_PCG_methods.__str__ = (B_str (*) (witness_forwardQ_PC))B_valueG_methods.__str__; + witness_forwardQ_PCG_methods.__repr__ = (B_str (*) (witness_forwardQ_PC))B_valueG_methods.__repr__; + witness_forwardQ_PCG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PC))witness_forwardQ_PCD___init__; + witness_forwardQ_PCG_methods.__serialize__ = witness_forwardQ_PCD___serialize__; + witness_forwardQ_PCG_methods.__deserialize__ = witness_forwardQ_PCD___deserialize__; + $register(&witness_forwardQ_PCG_methods); + } + { + witness_forwardQ_ThingG_methods.$GCINFO = "witness_forwardQ_Thing"; + witness_forwardQ_ThingG_methods.$superclass = ($SuperG_class)&B_objectG_methods; + witness_forwardQ_ThingG_methods.__bool__ = (bool (*) (witness_forwardQ_Thing))B_valueG_methods.__bool__; + witness_forwardQ_ThingG_methods.__str__ = (B_str (*) (witness_forwardQ_Thing))B_valueG_methods.__str__; + witness_forwardQ_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_Thing))B_valueG_methods.__repr__; + witness_forwardQ_ThingG_methods.__get_attr__ = (B_value (*) (witness_forwardQ_Thing, B_str))witness_forwardQ_ThingD___get_attr__; + witness_forwardQ_ThingG_methods.G_init = (B_NoneType (*) (witness_forwardQ_Thing))witness_forwardQ_ThingG_init; + witness_forwardQ_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_Thing, int64_t))witness_forwardQ_ThingD___init__; + witness_forwardQ_ThingG_methods.__serialize__ = witness_forwardQ_ThingD___serialize__; + witness_forwardQ_ThingG_methods.__deserialize__ = witness_forwardQ_ThingD___deserialize__; + $register(&witness_forwardQ_ThingG_methods); + } + { + witness_forwardQ_PBD_ThingG_methods.$GCINFO = "witness_forwardQ_PBD_Thing"; + witness_forwardQ_PBD_ThingG_methods.$superclass = ($SuperG_class)&witness_forwardQ_PBG_methods; + witness_forwardQ_PBD_ThingG_methods.__bool__ = (bool (*) (witness_forwardQ_PBD_Thing))B_valueG_methods.__bool__; + witness_forwardQ_PBD_ThingG_methods.__str__ = (B_str (*) (witness_forwardQ_PBD_Thing))B_valueG_methods.__str__; + witness_forwardQ_PBD_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_PBD_Thing))B_valueG_methods.__repr__; + witness_forwardQ_PBD_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PBD_Thing))witness_forwardQ_PBD_ThingD___init__; + witness_forwardQ_PBD_ThingG_methods.same = (bool (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PBD_ThingD_same; + witness_forwardQ_PBD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing))witness_forwardQ_PBD_ThingD_total; + witness_forwardQ_PBD_ThingG_methods.beta = (int64_t (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing))witness_forwardQ_PBD_ThingD_beta; + witness_forwardQ_PBD_ThingG_methods.__serialize__ = witness_forwardQ_PBD_ThingD___serialize__; + witness_forwardQ_PBD_ThingG_methods.__deserialize__ = witness_forwardQ_PBD_ThingD___deserialize__; + $register(&witness_forwardQ_PBD_ThingG_methods); + } + { + witness_forwardQ_PCD_ThingG_methods.$GCINFO = "witness_forwardQ_PCD_Thing"; + witness_forwardQ_PCD_ThingG_methods.$superclass = ($SuperG_class)&witness_forwardQ_PCG_methods; + witness_forwardQ_PCD_ThingG_methods.__bool__ = (bool (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__bool__; + witness_forwardQ_PCD_ThingG_methods.__str__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__str__; + witness_forwardQ_PCD_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__repr__; + witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_$forwardD_same; + witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_$forwardD_total; + witness_forwardQ_PCD_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PCD_Thing))witness_forwardQ_PCD_ThingD___init__; + witness_forwardQ_PCD_ThingG_methods.gamma = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_gamma; + witness_forwardQ_PCD_ThingG_methods.__serialize__ = witness_forwardQ_PCD_ThingD___serialize__; + witness_forwardQ_PCD_ThingG_methods.__deserialize__ = witness_forwardQ_PCD_ThingD___deserialize__; + $register(&witness_forwardQ_PCD_ThingG_methods); + } +} \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.h b/compiler/lib/test/9-codegen/witness_forward.h new file mode 100644 index 000000000..90682499b --- /dev/null +++ b/compiler/lib/test/9-codegen/witness_forward.h @@ -0,0 +1,138 @@ +/* Acton impl hash: test-hash */ +#pragma once +#include "builtin/builtin.h" +#include "rts/rts.h" +struct witness_forwardQ_PA; +struct witness_forwardQ_PB; +struct witness_forwardQ_PC; +struct witness_forwardQ_Thing; +struct witness_forwardQ_PBD_Thing; +struct witness_forwardQ_PCD_Thing; +typedef struct witness_forwardQ_PA *witness_forwardQ_PA; +typedef struct witness_forwardQ_PB *witness_forwardQ_PB; +typedef struct witness_forwardQ_PC *witness_forwardQ_PC; +typedef struct witness_forwardQ_Thing *witness_forwardQ_Thing; +typedef struct witness_forwardQ_PBD_Thing *witness_forwardQ_PBD_Thing; +typedef struct witness_forwardQ_PCD_Thing *witness_forwardQ_PCD_Thing; +struct witness_forwardQ_PAG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_PA); + void (*__serialize__) (witness_forwardQ_PA, $Serial$state); + witness_forwardQ_PA (*__deserialize__) (witness_forwardQ_PA, $Serial$state); + bool (*__bool__) (witness_forwardQ_PA); + B_str (*__str__) (witness_forwardQ_PA); + B_str (*__repr__) (witness_forwardQ_PA); + bool (*same) (witness_forwardQ_PA, $WORD, $WORD); + int64_t (*total) (witness_forwardQ_PA, $WORD); +}; +struct witness_forwardQ_PA { + struct witness_forwardQ_PAG_class *$class; +}; +struct witness_forwardQ_PBG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_PB); + void (*__serialize__) (witness_forwardQ_PB, $Serial$state); + witness_forwardQ_PB (*__deserialize__) (witness_forwardQ_PB, $Serial$state); + bool (*__bool__) (witness_forwardQ_PB); + B_str (*__str__) (witness_forwardQ_PB); + B_str (*__repr__) (witness_forwardQ_PB); + bool (*same) (witness_forwardQ_PB, $WORD, $WORD); + int64_t (*total) (witness_forwardQ_PB, $WORD); + int64_t (*beta) (witness_forwardQ_PB, $WORD); +}; +struct witness_forwardQ_PB { + struct witness_forwardQ_PBG_class *$class; +}; +struct witness_forwardQ_PCG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_PC); + void (*__serialize__) (witness_forwardQ_PC, $Serial$state); + witness_forwardQ_PC (*__deserialize__) (witness_forwardQ_PC, $Serial$state); + bool (*__bool__) (witness_forwardQ_PC); + B_str (*__str__) (witness_forwardQ_PC); + B_str (*__repr__) (witness_forwardQ_PC); + bool (*same) (witness_forwardQ_PC, $WORD, $WORD); + int64_t (*total) (witness_forwardQ_PC, $WORD); + int64_t (*gamma) (witness_forwardQ_PC, $WORD); +}; +struct witness_forwardQ_PC { + struct witness_forwardQ_PCG_class *$class; +}; +struct witness_forwardQ_ThingG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_Thing, int64_t); + void (*__serialize__) (witness_forwardQ_Thing, $Serial$state); + witness_forwardQ_Thing (*__deserialize__) (witness_forwardQ_Thing, $Serial$state); + bool (*__bool__) (witness_forwardQ_Thing); + B_str (*__str__) (witness_forwardQ_Thing); + B_str (*__repr__) (witness_forwardQ_Thing); + B_value (*__get_attr__) (witness_forwardQ_Thing, B_str); + B_NoneType (*G_init) (witness_forwardQ_Thing); +}; +struct witness_forwardQ_Thing { + struct witness_forwardQ_ThingG_class *$class; + int64_t n; +}; +struct witness_forwardQ_PBD_ThingG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_PBD_Thing); + void (*__serialize__) (witness_forwardQ_PBD_Thing, $Serial$state); + witness_forwardQ_PBD_Thing (*__deserialize__) (witness_forwardQ_PBD_Thing, $Serial$state); + bool (*__bool__) (witness_forwardQ_PBD_Thing); + B_str (*__str__) (witness_forwardQ_PBD_Thing); + B_str (*__repr__) (witness_forwardQ_PBD_Thing); + bool (*same) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing); + int64_t (*total) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing); + int64_t (*beta) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing); +}; +struct witness_forwardQ_PBD_Thing { + struct witness_forwardQ_PBD_ThingG_class *$class; +}; +struct witness_forwardQ_PCD_ThingG_class { + char *$GCINFO; + int $class_id; + $SuperG_class $superclass; + B_NoneType (*__init__) (witness_forwardQ_PCD_Thing); + void (*__serialize__) (witness_forwardQ_PCD_Thing, $Serial$state); + witness_forwardQ_PCD_Thing (*__deserialize__) (witness_forwardQ_PCD_Thing, $Serial$state); + bool (*__bool__) (witness_forwardQ_PCD_Thing); + B_str (*__str__) (witness_forwardQ_PCD_Thing); + B_str (*__repr__) (witness_forwardQ_PCD_Thing); + bool (*same) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing); + int64_t (*total) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing); + int64_t (*gamma) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing); +}; +struct witness_forwardQ_PCD_Thing { + struct witness_forwardQ_PCD_ThingG_class *$class; +}; +extern struct witness_forwardQ_PAG_class witness_forwardQ_PAG_methods; +B_NoneType witness_forwardQ_PAD___init__(witness_forwardQ_PA W_self); +extern struct witness_forwardQ_PBG_class witness_forwardQ_PBG_methods; +B_NoneType witness_forwardQ_PBD___init__(witness_forwardQ_PB W_self); +extern struct witness_forwardQ_PCG_class witness_forwardQ_PCG_methods; +B_NoneType witness_forwardQ_PCD___init__(witness_forwardQ_PC W_self); +extern struct witness_forwardQ_ThingG_class witness_forwardQ_ThingG_methods; +witness_forwardQ_Thing witness_forwardQ_ThingG_new(int64_t); +B_value witness_forwardQ_ThingD___get_attr__(witness_forwardQ_Thing self, B_str name); +B_NoneType witness_forwardQ_ThingG_init(witness_forwardQ_Thing self); +B_NoneType witness_forwardQ_ThingD___init__(witness_forwardQ_Thing self, int64_t n); +extern struct witness_forwardQ_PBD_ThingG_class witness_forwardQ_PBD_ThingG_methods; +witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingG_new(); +B_NoneType witness_forwardQ_PBD_ThingD___init__(witness_forwardQ_PBD_Thing W_self); +bool witness_forwardQ_PBD_ThingD_same(witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing a, witness_forwardQ_Thing b); +int64_t witness_forwardQ_PBD_ThingD_total(witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self); +int64_t witness_forwardQ_PBD_ThingD_beta(witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self); +extern struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_methods; +B_NoneType witness_forwardQ_PCD_ThingD___init__(witness_forwardQ_PCD_Thing W_self); +int64_t witness_forwardQ_PCD_ThingD_gamma(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing self); +void witness_forwardQ___init__ (); \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.input b/compiler/lib/test/9-codegen/witness_forward.input new file mode 100644 index 000000000..99f9ae481 --- /dev/null +++ b/compiler/lib/test/9-codegen/witness_forward.input @@ -0,0 +1,47 @@ +# recursive group: +class PA[G_self] (__builtin__.value): + pure def __init__ (W_self : PA[G_self]) -> None: + return None + same : (G_self, G_self) -> __builtin__.bool + total : (G_self) -> __builtin__.int +class PB[G_self] (PA[G_self], __builtin__.value): + pure def __init__ (W_self : PB[G_self]) -> None: + PA.__init__@[G_self](W_self) + return None + beta : (G_self) -> __builtin__.int +class PC[G_self] (PA[G_self], __builtin__.value): + pure def __init__ (W_self : PC[G_self]) -> None: + PA.__init__@[G_self](W_self) + return None + gamma : (G_self) -> __builtin__.int +class Thing (__builtin__.object, __builtin__.value): + pure def __get_attr__ (self : Thing, name : __builtin__.str) -> ?__builtin__.value: + return None + pure def G_init (self : Thing) -> None: + pass + return None + @property + n : __builtin__.int + pure def __init__ (self : Thing, n : UNBOXED __builtin__.int) -> None: + self.n = (UNBOX __builtin__.int n) + return None +class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): + pure def __init__ (W_self : PBD_Thing) -> None: + PB.__init__@[Thing](W_self) + return None + pure def same (W_self : PBD_Thing, a : Thing, b : Thing) -> UNBOXED __builtin__.bool: + N_tmp: UNBOXED __builtin__.bool = (a.n == b.n) + return (UNBOX __builtin__.bool N_tmp) + pure def total (W_self : PBD_Thing, self : Thing) -> UNBOXED __builtin__.int: + return self.n + pure def beta (W_self : PBD_Thing, self : Thing) -> UNBOXED __builtin__.int: + N_1tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 1) + return (UNBOX __builtin__.int N_1tmp) +class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): + pure def __init__ (W_self : PCD_Thing) -> None: + PC.__init__@[Thing](W_self) + return None + pure def gamma (W_self : PCD_Thing, self : Thing) -> UNBOXED __builtin__.int: + N_2tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 2) + return (UNBOX __builtin__.int N_2tmp) +# (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/ActonSpec.hs b/compiler/lib/test/ActonSpec.hs index dbe786bec..531df4a09 100644 --- a/compiler/lib/test/ActonSpec.hs +++ b/compiler/lib/test/ActonSpec.hs @@ -1851,6 +1851,7 @@ main = do describe "Pass 3: Types" $ do testTypes env0 ["deact"] testTypes env0 ["test_discovery"] + testTypes env0 ["witness_forward"] testAttributesInitialization env0 @@ -2107,6 +2108,7 @@ main = do testCodeGen env0 ["lines"] testCodeGen env0 ["chunking"] testCodeGen env0 ["boxparam"] + testCodeGen env0 ["witness_forward"] -- A local that is live across a for-loop must be emitted as `volatile` so it -- survives the loop's StopIteration setjmp/longjmp under optimization. testCodeGenContains env0 "forloop_volatile" ["volatile B_str marker", "if ($PUSH())"] diff --git a/compiler/lib/test/src/witness_forward.act b/compiler/lib/test/src/witness_forward.act new file mode 100644 index 000000000..8ce8c608c --- /dev/null +++ b/compiler/lib/test/src/witness_forward.act @@ -0,0 +1,33 @@ +# Extensions leaving inherited protocol slots to another witness: the +# converted witness classes must contain ordinary forwarding methods (see +# Converter.fwdImpl). PC$Thing gets forwarding defs for PA's static 'same' +# and instance 'total', routed to the earlier PB extension's witness. + +protocol PA: + @staticmethod + same : (Self,Self) -> bool + total : () -> int + +protocol PB (PA): + beta : () -> int + +protocol PC (PA): + gamma : () -> int + +class Thing(object): + n: int + def __init__(self, n: int): + self.n = n + +extension Thing (PB): + @staticmethod + def same(a: Thing, b: Thing) -> bool: + return a.n == b.n + def total(self): + return self.n + def beta(self): + return 1 + +extension Thing (PC): + def gamma(self): + return 2 From ad470e00e55f9b58465fb25d3ffd7a3d50279ea0 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Tue, 28 Jul 2026 11:42:12 +0200 Subject: [PATCH 2/7] Forward witness slots in Converter, not CodeGen When two protocols share an ancestor, the witness class for the second one inherits method slots that are already implemented by the first. Ord and Hashable both inherit Eq. Extensions are checked in source order, and the first extension that covers a protocol must implement its methods; a later extension covering the same protocol may not implement them again. So with extension int (Ord) written before extension int (Hashable), the Ord extension implements __eq__, and the Hashable witness for int gets an __eq__ slot whose implementation lives in the Ord witness. The type checker left such slots empty and CodeGen filled them in: when emitting C, it searched all classes for one with a matching method and generated a C wrapper that calls it. That repair only existed in the C output. The converted AST still had the empty slots, so every later pass had to allow for that. And it only fixed the method table: the witness class was still abstract, so no constructor was generated for it, and a program that needed one failed to compile. Now the type checker fills the slots itself when it checks the extension. A slot is left empty exactly when its protocol is already covered by an earlier witness (these are the final slots that checkAttributes finds). For each such slot the extension body gets an ordinary method that calls the same method through the protocol that declares it: def total(self): return Coll.total(self) The type checker then resolves Coll.total to the witness that implements it, exactly as it would for a call written by hand. That also handles the cases the old search could not: providers reached through a witness field of another witness (Collection through Sequence$list.W_Collection), generic providers whose type arguments must be instantiated, cyclic witnesses whose extra opts must be padded, and providers with constructor arguments. Forwarding is always a single hop, because the witness it reaches is the first one covering the protocol, and that one implements the method directly. If a slot is inherited from several parents it gets one forwarding method, and the parents' signatures must agree; disagreeing signatures are now an error instead of being silently resolved. The forwarding call inside the extension body must resolve to the earlier witness. While the body is checked, the extension's own witnesses are in scope too, one per protocol in its ancestry, and the one for the covered protocol would shadow the earlier witness the call is meant to reach. So the visible-witness skip that keeps duplicates out of the module table (hasVisibleWit) is now applied to those self-witnesses as well (tydefineInst). Witness classes come out of the type checker complete, so the forwarding machinery in CodeGen is removed, along with the never called provider search next to it and the hand-written C implementations of builtin methods that the generated forwarding methods now replace. The builtin method tables no longer contain any $forward wrappers, and the generated __eq__ forwarders compile down to plain value comparisons. --- base/builtin/bigint.c | 8 - base/builtin/complex.c | 8 - base/builtin/float.c | 8 - base/builtin/i16.c | 8 - base/builtin/i32.c | 8 - base/builtin/i8.c | 8 - base/builtin/int.c | 8 - base/builtin/str.c | 35 +-- base/builtin/u1.c | 8 - base/builtin/u16.c | 8 - base/builtin/u32.c | 8 - base/builtin/u64.c | 8 - base/builtin/u8.c | 8 - base/src/__builtin__.ext.c | 17 -- compiler/lib/src/Acton/CodeGen.hs | 258 +----------------- compiler/lib/src/Acton/TypeEnv.hs | 3 +- compiler/lib/src/Acton/Types.hs | 33 ++- .../lib/test/3-types/witness_forward.output | 5 + compiler/lib/test/9-codegen/witness_forward.c | 25 +- compiler/lib/test/9-codegen/witness_forward.h | 4 + .../lib/test/9-codegen/witness_forward.input | 9 + .../core_lang_auto/witness_forward_cyclic.act | 43 +++ .../witness_forward_diamond.act | 35 +++ .../witness_forward_diamond_clash__bf.act | 26 ++ .../witness_forward_generic.act | 36 +++ .../core_lang_auto/witness_forward_nested.act | 55 ++++ test/core_lang_auto/witness_forward_order.act | 39 +++ .../witness_forward_sibling.act | 40 +++ .../witness_forward_import/Build.act | 2 + .../witness_forward_import/src/hashpt.act | 13 + .../witness_forward_import/src/points.act | 13 + .../src/witness_forward_import.act | 27 ++ 32 files changed, 392 insertions(+), 422 deletions(-) create mode 100644 test/core_lang_auto/witness_forward_cyclic.act create mode 100644 test/core_lang_auto/witness_forward_diamond.act create mode 100644 test/core_lang_auto/witness_forward_diamond_clash__bf.act create mode 100644 test/core_lang_auto/witness_forward_generic.act create mode 100644 test/core_lang_auto/witness_forward_nested.act create mode 100644 test/core_lang_auto/witness_forward_order.act create mode 100644 test/core_lang_auto/witness_forward_sibling.act create mode 100644 test/regression_auto/witness_forward_import/Build.act create mode 100644 test/regression_auto/witness_forward_import/src/hashpt.act create mode 100644 test/regression_auto/witness_forward_import/src/points.act create mode 100644 test/regression_auto/witness_forward_import/src/witness_forward_import.act diff --git a/base/builtin/bigint.c b/base/builtin/bigint.c index 6ea8251ab..0e190ce2a 100644 --- a/base/builtin/bigint.c +++ b/base/builtin/bigint.c @@ -648,14 +648,6 @@ bool B_OrdD_bigintD___ge__ (B_OrdD_bigint wit, B_bigint a, B_bigint b) { // B_HashableD_bigint /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_bigintD___eq__(B_HashableD_bigint wit, B_bigint a, B_bigint b) { - return zz_equal(&a->val,&b->val); -} - -bool B_HashableD_bigintD___ne__(B_HashableD_bigint wit, B_bigint a, B_bigint b) { - return !zz_equal(&a->val,&b->val); -} - B_NoneType B_HashableD_bigintD_hash(B_HashableD_bigint wit, B_bigint a, B_hasher h) { long sz = a->val.size; unsigned long data; diff --git a/base/builtin/complex.c b/base/builtin/complex.c index 777ef51c3..3224ae209 100644 --- a/base/builtin/complex.c +++ b/base/builtin/complex.c @@ -142,14 +142,6 @@ bool B_EqD_complexD___ne__ (B_EqD_complex wit, B_complex a, B_complex b) { // B_HashableD_complex //////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_complexD___eq__(B_HashableD_complex wit, B_complex a, B_complex b) { - return creal(a->val) == creal(b->val) && cimag(a->val) == cimag(b->val); -} - -bool B_HashableD_complexD___ne__(B_HashableD_complex wit, B_complex a, B_complex b) { - return !B_HashableD_complexD___eq__(wit,a,b); -} - B_NoneType B_HashableD_complexD_hash(B_HashableD_complex wit, B_complex a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, to$bytesD_len((char *)&(a->val), 16)); return B_None; diff --git a/base/builtin/float.c b/base/builtin/float.c index 637a989ba..a514814c2 100644 --- a/base/builtin/float.c +++ b/base/builtin/float.c @@ -221,14 +221,6 @@ bool B_OrdD_floatD___ge__ (B_OrdD_float wit, B_float a, B_float b) { // B_HashableD_float /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_floatD___eq__(B_HashableD_float wit, B_float a, B_float b) { - return a->val == b->val; -} - -bool B_HashableD_floatD___ne__(B_HashableD_float wit, B_float a, B_float b) { - return a->val != b->val; -} - B_NoneType B_HashableD_floatD_hash(B_HashableD_float wit, B_float a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, to$bytesD_len((char *)&a, 8)); return B_None; diff --git a/base/builtin/i16.c b/base/builtin/i16.c index a1cb37985..934f9c655 100644 --- a/base/builtin/i16.c +++ b/base/builtin/i16.c @@ -266,14 +266,6 @@ bool B_OrdD_i16D___ge__ (B_OrdD_i16 wit, B_i16 a, B_i16 b) { // B_HashableD_i16 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_i16D___eq__(B_HashableD_i16 wit, B_i16 a, B_i16 b) { - return a->val == b->val; -} - -bool B_HashableD_i16D___ne__(B_HashableD_i16 wit, B_i16 a, B_i16 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_i16D_hash(B_HashableD_i16 wit, B_i16 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, to$bytesD_len((char *)&(a), 4)); return B_None; diff --git a/base/builtin/i32.c b/base/builtin/i32.c index 5ca4bab0c..c0a15cd47 100644 --- a/base/builtin/i32.c +++ b/base/builtin/i32.c @@ -266,14 +266,6 @@ bool B_OrdD_i32D___ge__ (B_OrdD_i32 wit, B_i32 a, B_i32 b) { // B_HashableD_i32 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_i32D___eq__(B_HashableD_i32 wit, B_i32 a, B_i32 b) { - return a->val == b->val; -} - -bool B_HashableD_i32D___ne__(B_HashableD_i32 wit, B_i32 a, B_i32 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_i32D_hash(B_HashableD_i32 wit, B_i32 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, to$bytesD_len((char *)&(a), 4)); return B_None; diff --git a/base/builtin/i8.c b/base/builtin/i8.c index 51a92474f..9791137b5 100644 --- a/base/builtin/i8.c +++ b/base/builtin/i8.c @@ -266,14 +266,6 @@ bool B_OrdD_i8D___ge__ (B_OrdD_i8 wit, B_i8 a, B_i8 b) { // B_HashableD_i8 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_i8D___eq__(B_HashableD_i8 wit, B_i8 a, B_i8 b) { - return a->val == b->val; -} - -bool B_HashableD_i8D___ne__(B_HashableD_i8 wit, B_i8 a, B_i8 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_i8D_hash(B_HashableD_i8 wit, B_i8 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, to$bytesD_len((char *)&(a), 4)); return B_None; diff --git a/base/builtin/int.c b/base/builtin/int.c index 84aaa1942..84783f0d4 100644 --- a/base/builtin/int.c +++ b/base/builtin/int.c @@ -269,14 +269,6 @@ bool B_OrdD_intD___ge__ (B_OrdD_int wit, B_int a, B_int b) { // B_HashableD_int /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_intD___eq__(B_HashableD_int wit, B_int a, B_int b) { - return a->val == b->val; -} - -bool B_HashableD_intD___ne__(B_HashableD_int wit, B_int a, B_int b) { - return a->val != b->val; -} - B_NoneType B_HashableD_intD_hash(B_HashableD_int wit, B_int a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/builtin/str.c b/base/builtin/str.c index 10c2064c6..317aa03c1 100644 --- a/base/builtin/str.c +++ b/base/builtin/str.c @@ -1599,14 +1599,6 @@ bool B_OrdD_strD___ge__ (B_OrdD_str wit, B_str a, B_str b) { // B_Hashable /////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_strD___eq__ (B_HashableD_str wit, B_str a, B_str b) { - return strcmp((char *)a->str,(char *)b->str) == 0; -} - -bool B_HashableD_strD___ne__ (B_HashableD_str wit, B_str a, B_str b) { - return strcmp((char *)a->str,(char *)b->str) != 0; -} - B_NoneType B_HashableD_strD_hash(B_HashableD_str wit, B_str a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytes((char *)a->str)); return B_None; @@ -2668,18 +2660,6 @@ struct B_IteratorD_bytearrayG_class B_IteratorD_bytearrayG_methods = { B_IteratorD_bytearrayD_next }; -B_Iterator B_ContainerD_bytearrayD___iter__ (B_ContainerD_bytearray wit, B_bytearray str) { - return (B_Iterator)$NEW(B_IteratorD_bytearray,str); -} - -B_bytearray B_ContainerD_bytearrayD___fromiter__ (B_ContainerD_bytearray wit, B_Iterable wit2, $WORD iter) { - return B_bytearrayD_join(toB_bytearray(""),wit2,iter); -} - -int64_t B_ContainerD_bytearrayD___len__ (B_ContainerD_bytearray wit, B_bytearray str) { - return (int64_t)str->nbytes; -} - bool B_ContainerD_bytearrayD___contains__(B_ContainerD_bytearray wit, B_bytearray self, B_int n) { bool res = false; for (int i=0; i < self->nbytes; i++) { @@ -2761,7 +2741,7 @@ B_NoneType B_SequenceD_bytearrayD_reverse(B_SequenceD_bytearray wit, B_bytearray B_Iterator B_SequenceD_bytearrayD___reversed__(B_SequenceD_bytearray wit, B_bytearray self) { B_bytearray copy = B_bytearrayD_copy(self); B_SequenceD_bytearrayD_reverse(wit,copy); - return B_ContainerD_bytearrayD___iter__ (NULL, copy); + return (B_Iterator)$NEW(B_IteratorD_bytearray,copy); } B_bytearray B_SequenceD_bytearrayD___getslice__ (B_SequenceD_bytearray wit, B_bytearray self, B_slice slc) { @@ -3901,19 +3881,6 @@ B_bytes B_TimesD_bytesD___mul__ (B_TimesD_bytes wit, B_bytes a, B_int n) { // Hashable -bool B_HashableD_bytesD___eq__ (B_HashableD_bytes wit, B_bytes a, B_bytes b) { - if (a->nbytes != b->nbytes) - return false; - for (int i=0; i < a->nbytes; i++) - if (a->str[i] != b->str[i]) - return false; - return true; -} - -bool B_HashableD_bytesD___ne__ (B_HashableD_bytes wit, B_bytes a, B_bytes b) { - return !B_HashableD_bytesD___eq__(wit,a,b); -} - B_NoneType B_HashableD_bytesD_hash(B_HashableD_bytes wit, B_bytes a, B_hasher h) { zig_hash_wyhash_update(h->_hasher, a); return B_None; diff --git a/base/builtin/u1.c b/base/builtin/u1.c index a874c3217..28d4f46d6 100644 --- a/base/builtin/u1.c +++ b/base/builtin/u1.c @@ -270,14 +270,6 @@ bool B_OrdD_u1D___ge__ (B_OrdD_u1 wit, B_u1 a, B_u1 b) { // B_HashableD_u1 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_u1D___eq__(B_HashableD_u1 wit, B_u1 a, B_u1 b) { - return a->val == b->val; -} - -bool B_HashableD_u1D___ne__(B_HashableD_u1 wit, B_u1 a, B_u1 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_u1D_hash(B_HashableD_u1 wit, B_u1 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/builtin/u16.c b/base/builtin/u16.c index 4480245f7..a1711a33a 100644 --- a/base/builtin/u16.c +++ b/base/builtin/u16.c @@ -259,14 +259,6 @@ bool B_OrdD_u16D___ge__ (B_OrdD_u16 wit, B_u16 a, B_u16 b) { // B_HashableD_u16 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_u16D___eq__(B_HashableD_u16 wit, B_u16 a, B_u16 b) { - return a->val == b->val; -} - -bool B_HashableD_u16D___ne__(B_HashableD_u16 wit, B_u16 a, B_u16 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_u16D_hash(B_HashableD_u16 wit, B_u16 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/builtin/u32.c b/base/builtin/u32.c index b531b74ae..151c35e5d 100644 --- a/base/builtin/u32.c +++ b/base/builtin/u32.c @@ -259,14 +259,6 @@ bool B_OrdD_u32D___ge__ (B_OrdD_u32 wit, B_u32 a, B_u32 b) { // B_HashableD_u32 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_u32D___eq__(B_HashableD_u32 wit, B_u32 a, B_u32 b) { - return a->val == b->val; -} - -bool B_HashableD_u32D___ne__(B_HashableD_u32 wit, B_u32 a, B_u32 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_u32D_hash(B_HashableD_u32 wit, B_u32 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/builtin/u64.c b/base/builtin/u64.c index a220a2b54..08badc876 100644 --- a/base/builtin/u64.c +++ b/base/builtin/u64.c @@ -259,14 +259,6 @@ bool B_OrdD_u64D___ge__ (B_OrdD_u64 wit, B_u64 a, B_u64 b) { // B_HashableD_u64 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_u64D___eq__(B_HashableD_u64 wit, B_u64 a, B_u64 b) { - return a->val == b->val; -} - -bool B_HashableD_u64D___ne__(B_HashableD_u64 wit, B_u64 a, B_u64 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_u64D_hash(B_HashableD_u64 wit, B_u64 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/builtin/u8.c b/base/builtin/u8.c index 5372bcf76..5385e3529 100644 --- a/base/builtin/u8.c +++ b/base/builtin/u8.c @@ -259,14 +259,6 @@ bool B_OrdD_u8D___ge__ (B_OrdD_u8 wit, B_u8 a, B_u8 b) { // B_HashableD_u8 /////////////////////////////////////////////////////////////////////////////////////////////////////// -bool B_HashableD_u8D___eq__(B_HashableD_u8 wit, B_u8 a, B_u8 b) { - return a->val == b->val; -} - -bool B_HashableD_u8D___ne__(B_HashableD_u8 wit, B_u8 a, B_u8 b) { - return a->val != b->val; -} - B_NoneType B_HashableD_u8D_hash(B_HashableD_u8 wit, B_u8 a, B_hasher h) { zig_hash_wyhash_update(h->_hasher,to$bytesD_len((char *)&(a->val),8)); return B_None; diff --git a/base/src/__builtin__.ext.c b/base/src/__builtin__.ext.c index 636c4139b..05c4c17ad 100644 --- a/base/src/__builtin__.ext.c +++ b/base/src/__builtin__.ext.c @@ -1,23 +1,6 @@ #include "rts/rts.c" void B___ext_init__() { - /* - B_HashableD_bigintG_methods.__eq__ = (B_bool (*)(B_HashableD_bigint, B_bigint, B_bigint))B_OrdD_bigintD___eq__; - B_HashableD_intG_methods.__eq__ = (B_bool (*)(B_HashableD_int, B_int, B_int))B_OrdD_intD___eq__; - B_HashableD_i32G_methods.__eq__ = (B_bool (*)(B_HashableD_i32, B_i32, B_i32))B_OrdD_i32D___eq__; - B_HashableD_i16G_methods.__eq__ = (B_bool (*)(B_HashableD_i16, B_i16, B_i16))B_OrdD_i16D___eq__; - B_HashableD_u64G_methods.__eq__ = (B_bool (*)(B_HashableD_u64, B_u64, B_u64))B_OrdD_u64D___eq__; - B_HashableD_u32G_methods.__eq__ = (B_bool (*)(B_HashableD_u32, B_u32, B_u32))B_OrdD_u32D___eq__; - B_HashableD_u16G_methods.__eq__ = (B_bool (*)(B_HashableD_u16, B_u16, B_u16))B_OrdD_u16D___eq__; - B_HashableD_floatG_methods.__eq__ = (B_bool (*)(B_HashableD_float, B_float, B_float))B_OrdD_floatD___eq__; - B_HashableD_strG_methods.__eq__ = (B_bool (*)(B_HashableD_str, B_str, B_str))B_OrdD_strD___eq__; - B_HashableD_bytesG_methods.__eq__ = (B_bool (*)(B_HashableD_bytes, B_bytes, B_bytes))B_OrdD_bytesD___eq__; - B_HashableD_complexG_methods.__eq__ = (B_bool (*)(B_HashableD_complex, B_complex, B_complex))B_HashableD_complexD___eq__; - - B_ContainerD_listG_methods.__len__ = (int64_t (*)(B_ContainerD_list, B_list))B_CollectionD_SequenceD_listD___len__; - B_ContainerD_listG_methods.__fromiter__ = (B_list (*)(B_ContainerD_list, B_Iterable, $WORD))B_CollectionD_SequenceD_listD___fromiter__; - B_ContainerD_listG_methods.__iter__ = (B_Iterator (*)(B_ContainerD_list, B_list))B_CollectionD_SequenceD_listD___iter__; - */ } B_str B_BaseExceptionD__name (B_BaseException self) { diff --git a/compiler/lib/src/Acton/CodeGen.hs b/compiler/lib/src/Acton/CodeGen.hs index 1546be8d2..58a27be6b 100644 --- a/compiler/lib/src/Acton/CodeGen.hs +++ b/compiler/lib/src/Acton/CodeGen.hs @@ -17,7 +17,6 @@ module Acton.CodeGen where import qualified Data.Set import qualified Data.HashSet as HashSet import qualified Data.List -import qualified Data.Map.Strict as Map import qualified Acton.Env import Utils import Pretty @@ -532,10 +531,8 @@ declDecl env (Def dloc n q p KwdNIL (Just t) b d fx ddoc) declDecl env (Class _ n q as b ddoc) | cDefinedClass = vcat [ declDecl env1 d{ dname = methodname n (dname d) } | Decl _ ds <- b', d@Def{} <- ds ] $+$ - forwardDecls env1 n q $+$ text "struct" <+> classname env n <+> methodtable env n <> semi | otherwise = vcat [ declDecl env1 d{ dname = methodname n (dname d) } | Decl _ ds <- b', d@Def{} <- ds ] $+$ - forwardDecls env1 n q $+$ declSerialize env1 n c props sup_c $+$ declDeserialize env1 n c props sup_c $+$ declCleanup env1 n sup_c $+$ @@ -675,14 +672,12 @@ initGlobalDoc env s = genStmt1 env s $+$ initClassBase env c q as hasCDef = methodtable env c <> dot <> gen env gcinfoKW <+> equals <+> doubleQuotes (genTopName env c) <> semi $+$ methodtable env c <> dot <> gen env superclassKW <+> equals <+> super <> semi $+$ - vcat [ inherit c' n | (c',n) <- inheritedAttrs env (NoQ c) ] $+$ - vcat [ forward n | ForwardSlot n _ _ _ _ <- forwardSlots env c q ] + vcat [ inherit c' n | (c',n) <- inheritedAttrs env (NoQ c) ] where tc = TC (NoQ c) [ tVar v | QBind v _ <- q ] super = if null as then text "NULL" else parens (gen env qnSuperClass) <> text "&" <> methodtable' env (tcname $ head as) inherit c' n | hasCDef = methodtable env c <> dot <> gen env n <+> equals <+> genTopName env (methodname c n) <> semi | otherwise = methodtable env c <> dot <> gen env n <+> equals <+> inheritedCast n <> methodtable' env c' <> dot <> gen env n <> semi - forward n = methodtable env c <> dot <> gen env n <+> equals <+> methodCast env c q n <> genTopName env (forwardName c n) <> semi inheritedCast n = case lookup n (fullAttrEnv env tc) of Just (NVar t) -> parens (gen env (vsubst [(tvSelf,tCon tc)] t)) _ -> methodCast env c q n @@ -716,201 +711,6 @@ methodCast env c q n = case lookup n (fullAttrEnv env tc) of where tc = TC (NoQ c) (map tVar $ qbound q) rt = B.rtypeOf env tc n --- Protocol witness classes can inherit abstract slots through more than one --- protocol path. If the target witness does not implement such a slot itself --- and normal inheritance did not fill it, generate a small forwarding wrapper --- that calls the same slot on a concrete provider witness. The wrapper keeps --- the target table ABI while avoiding unsafe casts of the target witness to the --- provider witness type. -data ForwardSlot = ForwardSlot Name Type TCon Doc Type - -forwardName c n = Derived c (Derived (name "$forward") n) - -forwardDecls env c q = vcat [ forwardDecl env c q f | f <- forwardSlots env c q ] - -forwardDecl env c q (ForwardSlot n targetSlot providerTc providerExpr providerSlot) - = text "static" <+> forwardResult env targetSlot <+> genTopName env (forwardName c n) <> parens (repPar env paramNames (posrow targetSlot)) <+> char '{' $+$ - nest 4 (gen env (tCon providerTc) <+> gen env providerV <+> equals <+> providerValue <> semi $+$ - text "return" <+> providerCall <> semi) $+$ - char '}' - where paramNames = take (arity $ posrow targetSlot) forwardParamNames - providerArgs = hsep $ punctuate comma $ map (gen env) (drop 1 paramNames) - providerValue = parens (gen env (tCon providerTc)) <> parens providerExpr - providerCall = parens (parens (funsig2 env Nothing providerSlot) <> gen env providerV <> text "->" <> gen env classKW <> text "->" <> gen env n) <> - parens (gen env providerV <> comma' providerArgs) - --- Missing slots are only forwarded for generated witness classes, and only --- when the provider slot has the same ABI after removing the witness argument. -forwardSlots env c q - | not $ forwardClass c = [] - | otherwise = [ ForwardSlot n targetSlot providerTc providerExpr providerSlot - | (n, i) <- fullAttrEnv env tc, - forwardTarget i, - forwardableSlot n, - n `notElem` inherited, - n `notElem` direct, - Just targetSlot <- [slotType env tc n], - Just (providerTc, providerExpr, providerSlot) <- [forwardProvider env providers tc n targetSlot] ] - where tc = TC (NoQ c) (map tVar $ qbound q) - inherited = [ n | (_, n) <- inheritedAttrs env (NoQ c) ] - direct = directNoDecMethods env (NoQ c) - providers = providerObjects env - -slotType env tc n = case lookup n (fullAttrEnv env tc) of - Just (NDef _ Static _) -> Just rt - Just (NSig _ Static _) -> Just rt - Just (NDef _ NoDec _) -> Just $ vsubst [(tvSelf, tCon tc)] $ addSelf rt (Just NoDec) - Just (NSig _ NoDec _) -> Just $ vsubst [(tvSelf, tCon tc)] $ addSelf rt (Just NoDec) - _ -> Nothing - where rt = B.rtypeOf env tc n - --- Pick the first concrete witness that already provides the requested slot for --- the same protocol/type owner. This still permits a sibling witness such as --- Ord[str] to fill an Eq[str] slot in Hashable[str], but excludes unrelated --- classes that only happen to have the same method name and ABI. -forwardProvider env providers targetTc n targetSlot - = first [ (providerTc, providerExpr, providerSlot) - | ownerTc <- slotOwners env targetTc n, - (providerTc, providerExpr) <- providers, - providerTc /= targetTc, - providerCoversOwner env providerTc ownerTc, - concreteProvider env providerTc n, - Just providerSlot <- [slotType env providerTc n], - compatibleSlots targetSlot providerSlot ] - -slotOwners env targetTc n = [ ownerTc - | (_, ownerTc) <- tail (findAncestry env targetTc), - ownsSlotDirectly env ownerTc n ] - -ownsSlotDirectly env tc n = case findAttrInfoIn n te of - Just NDef{} -> True - Just NSig{} -> True - Just NVar{} -> True - Just NSVar{} -> True - _ -> False - where (_, te) = findCon env tc - -providerCoversOwner env providerTc ownerTc - = any ((== ownerTc) . snd) (findAncestry env providerTc) - -providerObjects env = concat [ providerRoot qn q | (qn, q) <- allClasses env, nullConArgs env qn ] - where providerRoot qn q = walk [] rootExpr rootTc - where rootTc = TC qn (map tVar $ qbound q) - rootExpr = rootWitnessExpr env qn - walk seen expr tc - | tcname tc `elem` seen = [] - | otherwise = (tc, expr) : concat [ walk (tcname tc : seen) (expr <> text "->" <> gen env w) tc' - | (w, tc') <- witnessFields env tc ] - --- Not to intrude in Types.hs, we accept that we do not have the complete list of witnesses in Env --- For completing method tables, we start with all classes in the program but filter to only consider ancestor witness classes. -allClasses env = active ++ closed ++ mods - where active = [ (NoQ n, q) | (n, NClass q _ _ _) <- activeNames env ] - closed = [ (NoQ n, q) | (n, NClass q _ _ _) <- closedNames env ] - -- modulePublicTEnv resolves through each module's lookup function, so - -- entries rewritten by earlier passes are seen in converted form. - mods = [ (GName m n, q) | (m, mi) <- Map.toList (modules env), - (n, NClass q _ _ _) <- modulePublicTEnv mi ] - -nullConArgs env qn = case findQName qn env of - NClass _ _ te _ -> case lookup initKW te of - Just (NDef sc _ _) -> initArity sc == 0 - Just (NSig sc _ _) -> initArity sc == 0 - _ -> False - _ -> False - where initArity (TSchema _ _ (TFun _ _ r _ _)) = arity r - initArity _ = 1 - -rootWitnessExpr env (NoQ n) - | inBuiltin env = staticwitness env (gname env n) - | otherwise = newcon env n <> parens empty -rootWitnessExpr env qn@(GName m n) - | m == mBuiltin = staticwitness env qn - | otherwise = newcon' env qn <> parens empty -rootWitnessExpr env qn = newcon' env qn <> parens empty - -witnessFields env tc = [ (n, tc') | (n, i) <- fullAttrEnv env tc, - isWitness n, - TCon _ fieldTc <- [fieldType i], - tc' <- witnessTargets env tc fieldTc ] - where fieldType (NDef sc Property _) = sctype sc - fieldType (NSig sc Property _) = sctype sc - fieldType (NVar t) = t - fieldType (NSVar t) = t - fieldType _ = tWild - --- Witness fields are often typed as an abstract protocol witness. Prefer the --- concrete generated witness named by convention when it exists, but keep the --- declared field type as a fallback. -witnessTargets env ownerTc fieldTc = case concreteWitnessTarget env ownerTc fieldTc of - Just tc -> [tc, fieldTc] - Nothing -> [fieldTc] - -concreteWitnessTarget env ownerTc fieldTc - = case classQBinds env qn of - Just q -> Just $ TC qn (take (length $ qbound q) (tcargs ownerTc ++ tcargs fieldTc)) - Nothing -> Nothing - where qn = derivedWitnessQName (tcname ownerTc) (tcname fieldTc) - -derivedWitnessQName (NoQ owner) field - = NoQ (Derived (noq field) owner) -derivedWitnessQName (GName m owner) field - = GName m (Derived (noq field) owner) -derivedWitnessQName (QName m owner) field - = GName m (Derived (noq field) owner) - -classQBinds env (NoQ n) = case lookupName n env of - Just (NClass q _ _ _) -> Just q - _ -> Nothing -classQBinds env (GName m n) = case lookupModuleInfo m env >>= \mi -> moduleLookupName mi n of - Just (NClass q _ _ _) -> Just q - _ -> Nothing -classQBinds env (QName m n) = classQBinds env (GName m n) - -concreteProvider env tc n = case lookup n (fullAttrEnv env tc) of - Just (NDef _ _ _) -> True - Just (NVar _) -> True - Just (NSVar _) -> True - _ -> False - -compatibleSlots (TFun _ fx1 p1 k1 t1) (TFun _ fx2 p2 k2 t2) - = fx1 == fx2 && k1 == k2 && t1 == t2 && dropFirstRow p1 == dropFirstRow p2 -compatibleSlots _ _ = False - -dropFirstRow (TRow _ _ _ _ r) = r -dropFirstRow r = r - -forwardResult env (TFun _ fx _ _ t) = repType env (exposeMsg fx t) -forwardResult _ _ = empty - -repPar env (n : ns) (TRow _ _ _ t r@TRow{}) - = repType env t <+> gen env n <> comma <+> repPar env ns r -repPar env (n : _) (TRow _ _ _ t TNil{}) - = repType env t <+> gen env n -repPar env (n : _) (TRow _ _ _ t TVar{}) - = repType env t <+> gen env n -repPar _ _ TNil{} = empty -repPar _ _ _ = empty - -forwardTarget (NDef _ NoDec _) = True -forwardTarget (NSig _ NoDec _) = True -forwardTarget _ = False - -forwardableSlot n = n `notElem` ([initKW, serializeKW, deserializeKW] ++ valueKWs) - -forwardClass Derived{} = True -forwardClass _ = False - -directNoDecMethods env qn = case findQName qn env of - NClass _ _ te _ -> [ n | (n, NDef _ NoDec _) <- te ] - _ -> [] - -forwardParamNames = [ name ("fw_" ++ show i) | i <- [(0 :: Int)..] ] -providerV = name "fw_provider" -first (x:_) = Just x -first [] = Nothing - - initFlag = name "done$" methodname c n = Derived c n @@ -1501,21 +1301,6 @@ containsGeneratedMethodCallComp env (CompIf _ e c) containsGeneratedMethodCallComp _ NoComp = False -genDirectMethodCall env targetTc n rt (PosArg w p) - = callee <> parens (witness <> comma' (genCallPosArgs env r p)) - where TFun _ _ r _ _ = rt - slot = staticWitnessSlotType env targetTc n rt - witness = staticWitnessObject env targetTc w - callee = case staticWitnessMethodImpl env [] targetTc n of - Just implTc -> staticWitnessDirectCallee env slot implTc n - Nothing -> staticWitnessTableCallee env slot witness n -genDirectMethodCall _ _ n _ p = error ("Internal error: direct witness call for " ++ show n ++ " has arguments " ++ show p) - -staticWitnessObject env tc e = case staticWitnessName e of - (Just obj, path) -> parens (gen env (tCon tc)) <> foldl field (staticwitness env (unalias env obj)) path - _ -> error ("Internal error: direct witness call with non-static witness " ++ show e) - where field d n = d <> text "->" <> gen env n - genGeneratedMethodCall env n ts (PosArg w p) | Just (tc, _) <- generatedMethodClass env n ts = gen env (generatedMethodQName n) <> parens (witness <> comma' (gen env p)) @@ -1528,47 +1313,6 @@ staticWitnessValue env e = case staticWitnessName e of _ -> gen env e where field d n = d <> text "->" <> gen env n --- Direct witness calls name the target table slot. Defaults may be supplied --- by an ancestor protocol class, so the concrete implementation is resolved --- here and cast back to the target slot ABI. -staticWitnessSlotType env tc n rt - | Just slot <- slotType env tc n = slot - | otherwise = addWitness rt - where addWitness (TFun l fx r k t)= TFun l fx (posRow (tCon tc) r) k t - addWitness t = t - -staticWitnessDirectCallee env slot implTc n - = parens (parens (funsig2 env Nothing slot) <> staticWitnessMethodName env implTc n) - -staticWitnessTableCallee env slot witness n - = parens (parens (funsig2 env Nothing slot) <> parens witness <> text "->" <> gen env classKW <> text "->" <> gen env n) - -staticWitnessMethodName env (TC (GName m c) _) n - = gen env (GName m (methodname c n)) -staticWitnessMethodName env (TC (QName m c) _) n - = gen env (GName m (methodname c n)) -staticWitnessMethodName env (TC (NoQ c) _) n - = genTopName env (methodname c n) - -staticWitnessMethodImpl env seen tc n - | tcname tc `elem` seen = Nothing - | directMethodImpl env (tcname tc) n - = Just tc - | otherwise = do provider <- first [ provider | (provider, n') <- inheritedAttrs env (tcname tc), n' == n ] - staticWitnessMethodImpl env (tcname tc : seen) (schematicClass env provider) n - -directMethodImpl env qn n = case findQName qn env of - NClass _ _ te _ -> direct te - NProto _ _ te _ -> direct te - _ -> False - where direct te = case findAttrInfoIn n te of - Just NDef{} -> True - _ -> False - -schematicClass env qn = case classQBinds env qn of - Just q -> TC qn (map tVar $ qbound q) - Nothing -> TC qn [] - -- Compute the C-facing callable type used for argument rendering. Public -- polymorphic callables are matched against a wildcard instantiation so -- polymorphic positions keep their boxed ABI; internal/generated functions and diff --git a/compiler/lib/src/Acton/TypeEnv.hs b/compiler/lib/src/Acton/TypeEnv.hs index 92786c789..2c9cde51b 100644 --- a/compiler/lib/src/Acton/TypeEnv.hs +++ b/compiler/lib/src/Acton/TypeEnv.hs @@ -312,7 +312,8 @@ tydefineVars q env = modX env1 (\x -> foldl' addvarinfo x tvs) tydefineInst :: TCon -> [WTCon] -> Name -> Env -> Env tydefineInst c ps w env = modX env (\x -> foldl' addActiveWit x wits) - where wits = [ WInst [] (tCon c) p (NoQ w) ws | (ws,p) <- ps ] + where wits = [ wit | wit <- wits0, not (hasVisibleWit env wit) ] + wits0 = [ WInst [] (tCon c) p (NoQ w) ws | (ws,p) <- ps ] addActiveWit :: TypeX -> Witness -> TypeX addActiveWit x wit diff --git a/compiler/lib/src/Acton/Types.hs b/compiler/lib/src/Acton/Types.hs index a5e469946..7a28a4ccd 100644 --- a/compiler/lib/src/Acton/Types.hs +++ b/compiler/lib/src/Acton/Types.hs @@ -23,7 +23,7 @@ import Control.Monad import Control.Monad.Except (runExceptT) import Control.Monad.State.Strict (runState) import Data.Maybe (isJust) -import Data.List (nub, intersect, sort) +import Data.List (nub, nubBy, intersect, sort) import Pretty import qualified Control.Exception import Debug.Trace @@ -1047,7 +1047,7 @@ instance InfEnv Decl where popFX when (not $ null cs) $ err (loc n) "Deprecated class syntax" checkClassAttributesInitialized n l env as' b - (nterms,asigs,_) <- checkAttributes [] te' te + (nterms,asigs,_,_) <- checkAttributes [] te' te let (te2,b2) = if notImplBody b then let te1 = unSig asigs in (te++te1, addImpl te1 b1) else if null asigs && initKW `notElem` dom te then relayInit te b1 else (te,b1) @@ -1095,7 +1095,7 @@ instance InfEnv Decl where (cs,te,b') <- infEnv env1 b popFX when (not $ null cs) $ err (loc n) "Deprecated protocol syntax" - (nterms,_,sigs) <- checkAttributes [] te' te + (nterms,_,_,sigs) <- checkAttributes [] te' te let noself = [ n | (n, NSig sc Static _) <- te, tvSelf `notElem` vfree sc ] when (notImplBody b) $ err0 (notImpls b) "A protocol body cannot be NotImplemented" when (not $ null nterms) $ err2 (dom nterms) "Method/attribute lacks signature:" @@ -1124,22 +1124,32 @@ instance InfEnv Decl where (cs,te,b1) <- infEnv env1 b popFX when (not $ null cs) $ err (loc n) "Deprecated extension syntax" - (nterms,asigs,sigs) <- checkAttributes final te' te + (nterms,asigs,fsigs,sigs) <- checkAttributes (dom finals) te' te when (not $ null nterms) $ err2 (dom nterms) "Method/attribute not in listed protocols:" when (not $ null sigs) $ err2 sigs "Extension with new methods/attributes not supported" when (not (null asigs || notImplBody b)) $ err3 l (dom asigs) "Protocol method/attribute lacks implementation:" let te1 = unSig $ selfSubst n q asigs - te2 = te ++ te1 - b2 = addImpl te1 b1 + fwds = selfSubst n q $ nubBy (\a b -> fst a == fst b) fsigs + te2 = te ++ te1 ++ unSig fwds + b2 = addImpl te1 b1 ++ map fwdDef fwds return ([], [(extensionName us c, NExt q c ps te2 [] ddoc)], Extension l q c us b2 ddoc) where TC n ts = c env1 = define (toSigs te') $ reserve (assigned b) $ tydefineVars (stripQual q') $ setInClass env witsearch = findWitness env (tCon c) u u = head us ps = selfSubst n q $ mro1 env us -- TODO: check that ps doesn't contradict any previous extension mro for c - final = concat [ conAttrs env (tcname p) | (_,p) <- tail ps, hasWitness env (tCon c) p ] + finals = [ (a, p) | (_,p) <- tail ps, hasWitness env (tCon c) p, a <- conAttrs env (tcname p) ] te' = parentTEnv env ps q' = selfQuant n q + -- A final slot is already implemented by an earlier witness for its protocol. + -- It becomes a method that calls the slot through that protocol, which the type + -- checker resolves to the covering witness like any other call. + fwdDef (a, NSig sc dec _) = sDecl [Def NoLoc a (scbind sc) pp kp (Just $ restype t) [sReturn call] dec (effect t) Nothing] + where t = addSelf (sctype sc) (Just dec) + pp = pPar pNames $ posrow t + kp = kPar attrKW $ kwdrow t + Just owner = lookup a finals + call = Call NoLoc (eDot (eQVar $ tcname owner) a) (pArg pp) (kArg kp) -------------------------------------------------------------------------------------------------------------------------- @@ -1147,13 +1157,18 @@ checkAttributes final te' te | not $ null dupsigs = err2 dupsigs "Duplicate signatures for" | not $ null props = err2 props "Property attributes cannot have class-level definitions:" | not $ null nodef = err2 nodef "Methods finalized in a previous extension cannot be overridden:" - | otherwise = return (nterms, abssigs, dom sigs) + | not $ null clashes = err2 clashes "Conflicting inherited signatures for" + | otherwise = return (nterms, abssigs, finalsigs, dom sigs) where (sigs,terms) = sigTerms te (sigs',terms') = sigTerms te' (allsigs,allterms) = (sigs ++ sigs', terms ++ terms') dupsigs = duplicates (dom sigs) nterms = exclude terms (dom allsigs) - abssigs = allsigs `exclude` (dom allterms ++ final) + misssigs = allsigs `exclude` dom allterms + abssigs = misssigs `exclude` final + finalsigs = misssigs `restrict` final + clashes = nub [ n | (n, NSig sc dec _) <- finalsigs, (n', NSig sc' dec' _) <- finalsigs, + n == n', sctype sc' /= sctype sc || dec' /= dec ] props = dom terms `intersect` dom (propSigs allsigs) nodef = dom terms `intersect` final diff --git a/compiler/lib/test/3-types/witness_forward.output b/compiler/lib/test/3-types/witness_forward.output index e8461ba57..cb77a9f70 100644 --- a/compiler/lib/test/3-types/witness_forward.output +++ b/compiler/lib/test/3-types/witness_forward.output @@ -32,9 +32,14 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): pure def beta (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_26.__fromatom__(1) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): + W_PA_34: PA[Thing] = PBD_Thing() W_PA_30: __builtin__.Number[__builtin__.int] = __builtin__.IntegralD_int() pure def __init__ (W_self : Self) -> None: PC.__init__@[Thing](W_self) pure def gamma (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_30.__fromatom__(2) + pure def total (W_self : Self, G_1p : Thing) -> __builtin__.int: + return W_PA_34.total(G_1p) + pure def same (W_self : Self, G_1p : Thing, G_2p : Thing) -> __builtin__.bool: + return W_PA_34.same(G_1p, G_2p) # (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.c b/compiler/lib/test/9-codegen/witness_forward.c index 8ef35386a..76c9b3cbe 100644 --- a/compiler/lib/test/9-codegen/witness_forward.c +++ b/compiler/lib/test/9-codegen/witness_forward.c @@ -140,6 +140,7 @@ witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingG_new() { } struct witness_forwardQ_PBD_ThingG_class witness_forwardQ_PBD_ThingG_methods; B_NoneType witness_forwardQ_PCD_ThingD___init__ (witness_forwardQ_PCD_Thing W_self) { + ((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34 = ((witness_forwardQ_PA)witness_forwardQ_PBD_ThingG_new()); ((B_NoneType (*) (witness_forwardQ_PC))witness_forwardQ_PCG_methods.__init__)(((witness_forwardQ_PC)W_self)); return B_None; } @@ -149,15 +150,16 @@ int64_t witness_forwardQ_PCD_ThingD_gamma (witness_forwardQ_PCD_Thing W_self, wi #line 33 "test/src/witness_forward.act" return N_2tmp; } -static bool witness_forwardQ_PCD_ThingD_$forwardD_same(witness_forwardQ_PCD_Thing fw_0, witness_forwardQ_Thing fw_1, witness_forwardQ_Thing fw_2) { - witness_forwardQ_PBD_Thing fw_provider = (witness_forwardQ_PBD_Thing)(witness_forwardQ_PBD_ThingG_new()); - return ((bool (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))fw_provider->$class->same)(fw_provider, fw_1, fw_2); +int64_t witness_forwardQ_PCD_ThingD_total (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p) { + int64_t N_3tmp = ((int64_t (*) ($WORD, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->total)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p); + return N_3tmp; } -static int64_t witness_forwardQ_PCD_ThingD_$forwardD_total(witness_forwardQ_PCD_Thing fw_0, witness_forwardQ_Thing fw_1) { - witness_forwardQ_PBD_Thing fw_provider = (witness_forwardQ_PBD_Thing)(witness_forwardQ_PBD_ThingG_new()); - return ((int64_t (*) (witness_forwardQ_PBD_Thing, witness_forwardQ_Thing))fw_provider->$class->total)(fw_provider, fw_1); +bool witness_forwardQ_PCD_ThingD_same (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p) { + bool N_4tmp = ((bool (*) ($WORD, witness_forwardQ_Thing, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->same)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p, G_2p); + return N_4tmp; } void witness_forwardQ_PCD_ThingD___serialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { + $step_serialize(self->W_PA_34, state); } witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { $WORD $tmp; @@ -169,8 +171,15 @@ witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_ } self = $DNEW(witness_forwardQ_PCD_Thing, state); } + self->W_PA_34 = $step_deserialize(state); return self; } +witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new() { + witness_forwardQ_PCD_Thing $tmp = acton_malloc(sizeof(struct witness_forwardQ_PCD_Thing)); + $tmp->$class = &witness_forwardQ_PCD_ThingG_methods; + witness_forwardQ_PCD_ThingG_methods.__init__($tmp); + return $tmp; +} struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_methods; int witness_forwardQ_done$ = 0; void witness_forwardQ___init__ () { @@ -242,10 +251,10 @@ void witness_forwardQ___init__ () { witness_forwardQ_PCD_ThingG_methods.__bool__ = (bool (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__bool__; witness_forwardQ_PCD_ThingG_methods.__str__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__str__; witness_forwardQ_PCD_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__repr__; - witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_$forwardD_same; - witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_$forwardD_total; witness_forwardQ_PCD_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PCD_Thing))witness_forwardQ_PCD_ThingD___init__; witness_forwardQ_PCD_ThingG_methods.gamma = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_gamma; + witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_total; + witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_same; witness_forwardQ_PCD_ThingG_methods.__serialize__ = witness_forwardQ_PCD_ThingD___serialize__; witness_forwardQ_PCD_ThingG_methods.__deserialize__ = witness_forwardQ_PCD_ThingD___deserialize__; $register(&witness_forwardQ_PCD_ThingG_methods); diff --git a/compiler/lib/test/9-codegen/witness_forward.h b/compiler/lib/test/9-codegen/witness_forward.h index 90682499b..b44a95501 100644 --- a/compiler/lib/test/9-codegen/witness_forward.h +++ b/compiler/lib/test/9-codegen/witness_forward.h @@ -114,6 +114,7 @@ struct witness_forwardQ_PCD_ThingG_class { }; struct witness_forwardQ_PCD_Thing { struct witness_forwardQ_PCD_ThingG_class *$class; + witness_forwardQ_PA W_PA_34; }; extern struct witness_forwardQ_PAG_class witness_forwardQ_PAG_methods; B_NoneType witness_forwardQ_PAD___init__(witness_forwardQ_PA W_self); @@ -133,6 +134,9 @@ bool witness_forwardQ_PBD_ThingD_same(witness_forwardQ_PBD_Thing W_self, witness int64_t witness_forwardQ_PBD_ThingD_total(witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self); int64_t witness_forwardQ_PBD_ThingD_beta(witness_forwardQ_PBD_Thing W_self, witness_forwardQ_Thing self); extern struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_methods; +witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new(); B_NoneType witness_forwardQ_PCD_ThingD___init__(witness_forwardQ_PCD_Thing W_self); int64_t witness_forwardQ_PCD_ThingD_gamma(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing self); +int64_t witness_forwardQ_PCD_ThingD_total(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p); +bool witness_forwardQ_PCD_ThingD_same(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p); void witness_forwardQ___init__ (); \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.input b/compiler/lib/test/9-codegen/witness_forward.input index 99f9ae481..caed9e7e9 100644 --- a/compiler/lib/test/9-codegen/witness_forward.input +++ b/compiler/lib/test/9-codegen/witness_forward.input @@ -38,10 +38,19 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): N_1tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 1) return (UNBOX __builtin__.int N_1tmp) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): + @property + W_PA_34 : PA[Thing] pure def __init__ (W_self : PCD_Thing) -> None: + W_self.W_PA_34 = PBD_Thing() PC.__init__@[Thing](W_self) return None pure def gamma (W_self : PCD_Thing, self : Thing) -> UNBOXED __builtin__.int: N_2tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 2) return (UNBOX __builtin__.int N_2tmp) + pure def total (W_self : PCD_Thing, G_1p : Thing) -> UNBOXED __builtin__.int: + N_3tmp: UNBOXED __builtin__.int = W_self.W_PA_34.total(G_1p) + return (UNBOX __builtin__.int N_3tmp) + pure def same (W_self : PCD_Thing, G_1p : Thing, G_2p : Thing) -> UNBOXED __builtin__.bool: + N_4tmp: UNBOXED __builtin__.bool = W_self.W_PA_34.same(G_1p, G_2p) + return (UNBOX __builtin__.bool N_4tmp) # (recursive group) \ No newline at end of file diff --git a/test/core_lang_auto/witness_forward_cyclic.act b/test/core_lang_auto/witness_forward_cyclic.act new file mode 100644 index 000000000..316a127d6 --- /dev/null +++ b/test/core_lang_auto/witness_forward_cyclic.act @@ -0,0 +1,43 @@ +# Forwarding into a cyclic witness path: the Eq witnesses for Node and Edge +# construct each other (a WitKnots cycle, broken with optional parameters), +# and Hashable[Node] forwards __eq__ into that cyclic Eq witness. + +class Node(object): + v: int + r: ?Edge + def __init__(self, v: int, r: ?Edge): + self.v = v + self.r = r + +class Edge(object): + w: int + l: ?Node + def __init__(self, w: int, l: ?Node): + self.w = w + self.l = l + +extension Node (Eq): + def __eq__(a: Node, b: Node) -> bool: + return a.v == b.v and a.r == b.r + +extension Edge (Eq): + def __eq__(a: Edge, b: Edge) -> bool: + return a.w == b.w and a.l == b.l + +extension Node (Hashable): + def hash(self, h): + self.v.hash(h) + +actor main(env): + a = Node(1, Edge(2, None)) + b = Node(1, Edge(2, None)) + c = Node(1, Edge(3, None)) + if a != b or a == c: + print("FAIL: cyclic Eq witnesses") + await async env.exit(1) + d: dict[Node, str] = {} + d[a] = "hit" + if b not in d or c in d: + print("FAIL: dict through __eq__ forwarded into a cyclic witness") + await async env.exit(1) + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_diamond.act b/test/core_lang_auto/witness_forward_diamond.act new file mode 100644 index 000000000..08717130c --- /dev/null +++ b/test/core_lang_auto/witness_forward_diamond.act @@ -0,0 +1,35 @@ +# A slot inherited through two parents with agreeing signatures must produce +# exactly one forwarding method per generated witness class (main and +# sibling), routed to the extension that implements it. + +protocol Alpha: + f : (int) -> int + +protocol Beta: + f : (int) -> int + +protocol AlphaBeta (Alpha, Beta): + pass + +class Thing(object): + def __init__(self): + pass + +extension Thing (Alpha): + def f(self, x: int) -> int: + return x + +extension Thing (AlphaBeta): + pass + +def via_beta[T(Beta)](x: T, k: int) -> int: + return Beta.f(x, k) + +actor main(env): + if AlphaBeta.f(Thing(), 1) != 1: + print("FAIL: forwarded diamond slot in the main witness class") + await async env.exit(1) + if via_beta(Thing(), 5) != 5: + print("FAIL: forwarded diamond slot in the sibling witness class") + await async env.exit(1) + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_diamond_clash__bf.act b/test/core_lang_auto/witness_forward_diamond_clash__bf.act new file mode 100644 index 000000000..33388fcc5 --- /dev/null +++ b/test/core_lang_auto/witness_forward_diamond_clash__bf.act @@ -0,0 +1,26 @@ +# A slot inherited through two parents with incompatible signatures is +# rejected by ordinary type checking; witness forwarding must not paper over +# the clash. + +protocol Alpha: + f : (int) -> int + +protocol Beta: + f : (str) -> str + +protocol AlphaBeta (Alpha, Beta): + pass + +class Thing(object): + def __init__(self): + pass + +extension Thing (Alpha): + def f(self, x: int) -> int: + return x + +extension Thing (AlphaBeta): + pass + +actor main(env): + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_generic.act b/test/core_lang_auto/witness_forward_generic.act new file mode 100644 index 000000000..4172a7e01 --- /dev/null +++ b/test/core_lang_auto/witness_forward_generic.act @@ -0,0 +1,36 @@ +# Forwarding between generic extensions: Hashable[Box[A]] forwards __eq__ to +# Ord[Box[A]]. Generic slots pass boxed arguments, and the witness class must +# come out complete enough to instantiate for dict keys. + +class Box[X](object): + item: X + def __init__(self, item: X): + self.item = item + +extension Box[A(Ord)] (Ord): + def __eq__(a: Box[A], b: Box[A]) -> bool: + return a.item == b.item + def __lt__(a: Box[A], b: Box[A]) -> bool: + return a.item < b.item + +extension Box[A(Ord,Hashable)] (Hashable): + def hash(self, h): + self.item.hash(h) + +actor main(env): + d: dict[Box[int], str] = {} + d[Box(1)] = "one" + d[Box(2)] = "two" + if d[Box(1)] != "one" or Box(2) not in d or Box(3) in d: + print("FAIL: dict through forwarded generic __eq__") + await async env.exit(1) + l = sorted([Box(3), Box(1), Box(2)]) + if l[0].item != 1 or l[1].item != 2 or l[2].item != 3: + print("FAIL: sorted through generic Ord witness") + await async env.exit(1) + s: dict[Box[str], int] = {} + s[Box("a")] = 1 + if Box("a") not in s: + print("FAIL: str instantiation of forwarded generic __eq__") + await async env.exit(1) + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_nested.act b/test/core_lang_auto/witness_forward_nested.act new file mode 100644 index 000000000..80a690d76 --- /dev/null +++ b/test/core_lang_auto/witness_forward_nested.act @@ -0,0 +1,55 @@ +# Forwarding through a nested witness path: Coll is a second (sibling) base of +# Seq2, so the provider for Cont's inherited Coll slots is the sibling witness +# reached through Seq2$Vec's witness field. Covers an instance slot (total), +# an inherited instance slot (Src.total via Coll) and a static slot with Self +# in result position (make), both statically and through a generic witness +# parameter. + +protocol Src: + total : () -> int + +protocol Coll (Src): + @staticmethod + make : (int) -> Self + +protocol Extra: + tag : () -> int + +protocol Seq2 (Extra, Coll): + step : () -> int + +protocol Cont (Coll): + has : (int) -> bool + +class Vec(object): + n: int + def __init__(self, n: int): + self.n = n + +extension Vec (Seq2): + def tag(self): + return 7 + def step(self): + return 1 + def total(self): + return self.n + @staticmethod + def make(k: int) -> Vec: + return Vec(k) + +extension Vec (Cont): + def has(self, i: int) -> bool: + return i < self.n + +def use_cont[T(Cont)](x: T) -> int: + y: T = Cont.make(x.total() + 1) + return y.total() + +actor main(env): + if use_cont(Vec(5)) != 6: + print("FAIL: forwarded slots through generic Cont witness") + await async env.exit(1) + if Cont.make(3).total() != 3: + print("FAIL: forwarded static make at concrete type") + await async env.exit(1) + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_order.act b/test/core_lang_auto/witness_forward_order.act new file mode 100644 index 000000000..11fd1b894 --- /dev/null +++ b/test/core_lang_auto/witness_forward_order.act @@ -0,0 +1,39 @@ +# Extension order decides the provider: here Hashable comes first and defines +# __eq__ directly (deliberately as parity equality), so the later Ord extension +# gets a forwarding __eq__ that must route to Hashable's direct implementation. +# Direct implementations take precedence; forwarding only fills missing slots. + +class Par(object): + x: int + def __init__(self, x: int): + self.x = x + +extension Par (Hashable): + def __eq__(a: Par, b: Par) -> bool: + return a.x % 2 == b.x % 2 + def hash(self, h): + (self.x % 2).hash(h) + +extension Par (Ord): + def __lt__(a: Par, b: Par) -> bool: + return a.x < b.x + +actor main(env): + # The canonical Eq witness is Hashable's direct (parity) implementation. + if not (Par(2) == Par(4)) or Par(2) == Par(3): + print("FAIL: language-level == must use the direct implementation") + await async env.exit(1) + # Ord's inherited __le__ default calls __lt__ and the forwarded __eq__: + # 3 < 1 is False, but 3 == 1 holds under parity equality. + if not (Par(3) <= Par(1)): + print("FAIL: __le__ default through Ord's forwarded __eq__") + await async env.exit(1) + if not (Par(1) < Par(2)) or Par(2) < Par(1): + print("FAIL: __lt__ through Ord witness") + await async env.exit(1) + d: dict[Par, str] = {} + d[Par(2)] = "even" + if Par(4) not in d or Par(5) in d: + print("FAIL: dict through direct parity __eq__") + await async env.exit(1) + await async env.exit(0) diff --git a/test/core_lang_auto/witness_forward_sibling.act b/test/core_lang_auto/witness_forward_sibling.act new file mode 100644 index 000000000..418c3808b --- /dev/null +++ b/test/core_lang_auto/witness_forward_sibling.act @@ -0,0 +1,40 @@ +# A witness class with an inherited protocol slot implemented by a sibling +# witness gets an ordinary forwarding method: Hashable[Pt] lacks __eq__, which +# is provided by the earlier Ord[Pt] extension. The dict/set runtime calls +# __eq__ through the Hashable witness, so lookups with equal but distinct +# key objects exercise the forwarded slot. + +class Pt(object): + x: int + def __init__(self, x: int): + self.x = x + +extension Pt (Ord): + def __eq__(a: Pt, b: Pt) -> bool: + return a.x == b.x + def __lt__(a: Pt, b: Pt) -> bool: + return a.x < b.x + +extension Pt (Hashable): + def hash(self, h): + self.x.hash(h) + +actor main(env): + d: dict[Pt, str] = {} + d[Pt(1)] = "one" + d[Pt(2)] = "two" + if d[Pt(1)] != "one": + print("FAIL: dict lookup through forwarded __eq__") + await async env.exit(1) + if Pt(2) not in d or Pt(3) in d: + print("FAIL: dict membership through forwarded __eq__") + await async env.exit(1) + s = {Pt(4), Pt(4), Pt(5)} + if len(s) != 2: + print("FAIL: set dedup through forwarded __eq__") + await async env.exit(1) + # Protocol default methods are inherited, not forwarded. + if Pt(1) != Pt(1) or not (Pt(1) <= Pt(1)): + print("FAIL: inherited default methods") + await async env.exit(1) + await async env.exit(0) diff --git a/test/regression_auto/witness_forward_import/Build.act b/test/regression_auto/witness_forward_import/Build.act new file mode 100644 index 000000000..3481b82a3 --- /dev/null +++ b/test/regression_auto/witness_forward_import/Build.act @@ -0,0 +1,2 @@ +name = "witness_forward_import" +fingerprint = 0x969df9b892763322 diff --git a/test/regression_auto/witness_forward_import/src/hashpt.act b/test/regression_auto/witness_forward_import/src/hashpt.act new file mode 100644 index 000000000..6fe6b6cec --- /dev/null +++ b/test/regression_auto/witness_forward_import/src/hashpt.act @@ -0,0 +1,13 @@ +# The extending module: Hashable[Pt] leaves __eq__ to the imported Ord +# witness. Since Eq was finalized elsewhere, this extension registers no +# Eq witness of its own; uses here and in importing modules resolve to +# points' canonical one. + +import points + +extension points.Pt (Hashable): + def hash(self, h): + self.x.hash(h) + +def same_here(a: points.Pt, b: points.Pt) -> bool: + return a == b diff --git a/test/regression_auto/witness_forward_import/src/points.act b/test/regression_auto/witness_forward_import/src/points.act new file mode 100644 index 000000000..a536abce5 --- /dev/null +++ b/test/regression_auto/witness_forward_import/src/points.act @@ -0,0 +1,13 @@ +# The provider module: defines the class and its Ord extension. An importing +# module's Hashable extension must forward __eq__ to this witness. + +class Pt(object): + x: int + def __init__(self, x: int): + self.x = x + +extension Pt (Ord): + def __eq__(a: Pt, b: Pt) -> bool: + return a.x == b.x + def __lt__(a: Pt, b: Pt) -> bool: + return a.x < b.x diff --git a/test/regression_auto/witness_forward_import/src/witness_forward_import.act b/test/regression_auto/witness_forward_import/src/witness_forward_import.act new file mode 100644 index 000000000..9f7a9718e --- /dev/null +++ b/test/regression_auto/witness_forward_import/src/witness_forward_import.act @@ -0,0 +1,27 @@ +# A third module importing both the provider (points: class + Ord) and the +# extender (hashpt: Hashable, with __eq__ finalized by points). Eq[Pt] must +# resolve to points' canonical witness here even though hashpt's extension +# also covers Eq through its ancestry. + +import points +import hashpt + +actor main(env): + a = points.Pt(1) + b = points.Pt(1) + if not (a == b) or a == points.Pt(2): + print("FAIL: == through the imported Ord witness") + await async env.exit(1) + if not hashpt.same_here(a, b): + print("FAIL: == inside the extending module") + await async env.exit(1) + d: dict[points.Pt, str] = {} + d[a] = "one" + if b not in d or points.Pt(7) in d: + print("FAIL: dict through __eq__ forwarded to the imported witness") + await async env.exit(1) + l = sorted([points.Pt(3), points.Pt(1), points.Pt(2)]) + if l[0].x != 1 or l[1].x != 2 or l[2].x != 3: + print("FAIL: sorted through the imported Ord witness") + await async env.exit(1) + await async env.exit(0) From 04a56fb4be324827bd19b0e26808f37c314097c0 Mon Sep 17 00:00:00 2001 From: Johan Nordlander Date: Wed, 19 Aug 2026 10:33:16 +0200 Subject: [PATCH 3/7] Align parentTEnv order with other TEnvs --- compiler/lib/src/Acton/Env.hs | 2 +- compiler/lib/src/Acton/Types.hs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/lib/src/Acton/Env.hs b/compiler/lib/src/Acton/Env.hs index b60d82877..0842e7448 100644 --- a/compiler/lib/src/Acton/Env.hs +++ b/compiler/lib/src/Acton/Env.hs @@ -848,7 +848,7 @@ fullAttrEnv = attributes f where f wp i n = Just (n,i) parentTEnv :: EnvF x -> [WTCon] -> TEnv -parentTEnv env us = [ (n,i) | (_,c) <- us, let (_,te) = findCon env c, (n,i) <- reverse te ] -- in override order +parentTEnv env us = [ (n,i) | (_,c) <- reverse us, let (_,te) = findCon env c, (n,i) <- te ] -- in program order findAttr :: EnvF x -> TCon -> Name -> Maybe (Expr->Expr, TSchema, Maybe Deco) findAttr env tc n = go (findAncestry env tc) diff --git a/compiler/lib/src/Acton/Types.hs b/compiler/lib/src/Acton/Types.hs index 7a28a4ccd..76fb6ee44 100644 --- a/compiler/lib/src/Acton/Types.hs +++ b/compiler/lib/src/Acton/Types.hs @@ -1053,7 +1053,7 @@ instance InfEnv Decl where else (te,b1) return ([], [(n, NClass q as' (te0++te2) ddoc)], Class l n q us (props te0 ++ b2) ddoc) _ -> illegalRedef n - where env1 = define (exclude (toSigs te') [initKW]) $ reserve (assigned b0) $ tydefineVars (stripQual q') $ setInClass env + where env1 = define (toSigs te' `exclude` [initKW]) $ reserve (assigned b0) $ tydefineVars (stripQual q') $ setInClass env (as,ps) = mro2 env us as' = if null as && not (inBuiltin env && n == nValue) then leftpath [cValue] else as te' = parentTEnv env as' @@ -1078,7 +1078,7 @@ instance InfEnv Decl where userMethods = [ dname d | Decl _ ds <- b, d@Def{} <- ds ] getAttrDef = sDef getAttrKW (pospar [(selfKW,tSelf),(nameKW,tStr)]) (tOpt tValue) [sReturn eNone] fxPure relayInit te b = --trace ("####### Creating relayInit for class " ++ prstr n) $ - case lookup initKW te' of + case lookup initKW (reverse te') of Just ni@(NDef sc _ _) -> ((initKW,ni):te, sDecl [Def NoLoc initKW [] pp kp Nothing body NoDec fx Nothing]:b) where t = addSelf (sctype sc) (Just NoDec) @@ -1163,7 +1163,7 @@ checkAttributes final te' te (sigs',terms') = sigTerms te' (allsigs,allterms) = (sigs ++ sigs', terms ++ terms') dupsigs = duplicates (dom sigs) - nterms = exclude terms (dom allsigs) + nterms = terms `exclude` dom allsigs misssigs = allsigs `exclude` dom allterms abssigs = misssigs `exclude` final finalsigs = misssigs `restrict` final From f087c9d666f18f2a41f933f0daffffb277e389fd Mon Sep 17 00:00:00 2001 From: Johan Nordlander Date: Wed, 19 Aug 2026 10:35:45 +0200 Subject: [PATCH 4/7] Extend clash test to cover all inherited signatures --- compiler/lib/src/Acton/Types.hs | 2 +- test/core_lang_auto/conflicting_signatures__bf.act | 10 ++++++++++ .../mro_inconsistent_instances__bf.act | 13 +++++++++++++ .../mro_inconsistent_resolution__bf.act | 10 ++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/core_lang_auto/conflicting_signatures__bf.act create mode 100644 test/core_lang_auto/mro_inconsistent_instances__bf.act create mode 100644 test/core_lang_auto/mro_inconsistent_resolution__bf.act diff --git a/compiler/lib/src/Acton/Types.hs b/compiler/lib/src/Acton/Types.hs index 76fb6ee44..33daed77c 100644 --- a/compiler/lib/src/Acton/Types.hs +++ b/compiler/lib/src/Acton/Types.hs @@ -1167,7 +1167,7 @@ checkAttributes final te' te misssigs = allsigs `exclude` dom allterms abssigs = misssigs `exclude` final finalsigs = misssigs `restrict` final - clashes = nub [ n | (n, NSig sc dec _) <- finalsigs, (n', NSig sc' dec' _) <- finalsigs, + clashes = nub [ n | (n, NSig sc dec _) <- sigs', n /= initKW, (n', NSig sc' dec' _) <- sigs', n == n', sctype sc' /= sctype sc || dec' /= dec ] props = dom terms `intersect` dom (propSigs allsigs) nodef = dom terms `intersect` final diff --git a/test/core_lang_auto/conflicting_signatures__bf.act b/test/core_lang_auto/conflicting_signatures__bf.act new file mode 100644 index 000000000..1cc2c9373 --- /dev/null +++ b/test/core_lang_auto/conflicting_signatures__bf.act @@ -0,0 +1,10 @@ +# A slot inherited through two parents with incompatible signatures is rejected. + +protocol Alpha: + f : (int) -> int + +protocol Beta: + f : (str) -> str + +protocol AlphaBeta (Alpha, Beta): + pass diff --git a/test/core_lang_auto/mro_inconsistent_instances__bf.act b/test/core_lang_auto/mro_inconsistent_instances__bf.act new file mode 100644 index 000000000..7470b34e2 --- /dev/null +++ b/test/core_lang_auto/mro_inconsistent_instances__bf.act @@ -0,0 +1,13 @@ +# A common ancestor inherited via differently instantiated paths is rejected. + +protocol Zero[T]: + f : (T) -> T + +protocol Alpha (Zero[int]): + pass + +protocol Beta (Zero[str]): + pass + +protocol AlphaBeta (Alpha, Beta): + pass diff --git a/test/core_lang_auto/mro_inconsistent_resolution__bf.act b/test/core_lang_auto/mro_inconsistent_resolution__bf.act new file mode 100644 index 000000000..6d4c21c47 --- /dev/null +++ b/test/core_lang_auto/mro_inconsistent_resolution__bf.act @@ -0,0 +1,10 @@ +# A contradictory ancestry order is rejected. + +protocol Alpha: + f : (int) -> int + +protocol Beta (Alpha): + pass + +protocol AlphaBeta (Alpha, Beta): + pass From d8f89d3e2ca681ac8125d793dbfa2e8e388946b9 Mon Sep 17 00:00:00 2001 From: Johan Nordlander Date: Wed, 19 Aug 2026 10:40:59 +0200 Subject: [PATCH 5/7] Update golden test w.r.t. changed parentTEnv order --- compiler/lib/test/3-types/witness_forward.output | 8 ++++---- compiler/lib/test/9-codegen/witness_forward.c | 16 ++++++++-------- compiler/lib/test/9-codegen/witness_forward.h | 4 ++-- .../lib/test/9-codegen/witness_forward.input | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/lib/test/3-types/witness_forward.output b/compiler/lib/test/3-types/witness_forward.output index cb77a9f70..5acf114f3 100644 --- a/compiler/lib/test/3-types/witness_forward.output +++ b/compiler/lib/test/3-types/witness_forward.output @@ -32,14 +32,14 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): pure def beta (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_26.__fromatom__(1) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): - W_PA_34: PA[Thing] = PBD_Thing() + W_PA_43: PA[Thing] = PBD_Thing() W_PA_30: __builtin__.Number[__builtin__.int] = __builtin__.IntegralD_int() pure def __init__ (W_self : Self) -> None: PC.__init__@[Thing](W_self) pure def gamma (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_30.__fromatom__(2) - pure def total (W_self : Self, G_1p : Thing) -> __builtin__.int: - return W_PA_34.total(G_1p) pure def same (W_self : Self, G_1p : Thing, G_2p : Thing) -> __builtin__.bool: - return W_PA_34.same(G_1p, G_2p) + return W_PA_43.same(G_1p, G_2p) + pure def total (W_self : Self, G_1p : Thing) -> __builtin__.int: + return W_PA_43.total(G_1p) # (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.c b/compiler/lib/test/9-codegen/witness_forward.c index 76c9b3cbe..0bd77444b 100644 --- a/compiler/lib/test/9-codegen/witness_forward.c +++ b/compiler/lib/test/9-codegen/witness_forward.c @@ -140,7 +140,7 @@ witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingG_new() { } struct witness_forwardQ_PBD_ThingG_class witness_forwardQ_PBD_ThingG_methods; B_NoneType witness_forwardQ_PCD_ThingD___init__ (witness_forwardQ_PCD_Thing W_self) { - ((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34 = ((witness_forwardQ_PA)witness_forwardQ_PBD_ThingG_new()); + ((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43 = ((witness_forwardQ_PA)witness_forwardQ_PBD_ThingG_new()); ((B_NoneType (*) (witness_forwardQ_PC))witness_forwardQ_PCG_methods.__init__)(((witness_forwardQ_PC)W_self)); return B_None; } @@ -150,16 +150,16 @@ int64_t witness_forwardQ_PCD_ThingD_gamma (witness_forwardQ_PCD_Thing W_self, wi #line 33 "test/src/witness_forward.act" return N_2tmp; } -int64_t witness_forwardQ_PCD_ThingD_total (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p) { - int64_t N_3tmp = ((int64_t (*) ($WORD, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->total)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p); +bool witness_forwardQ_PCD_ThingD_same (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p) { + bool N_3tmp = ((bool (*) ($WORD, witness_forwardQ_Thing, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43))->$class->same)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43, G_1p, G_2p); return N_3tmp; } -bool witness_forwardQ_PCD_ThingD_same (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p) { - bool N_4tmp = ((bool (*) ($WORD, witness_forwardQ_Thing, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->same)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p, G_2p); +int64_t witness_forwardQ_PCD_ThingD_total (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p) { + int64_t N_4tmp = ((int64_t (*) ($WORD, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43))->$class->total)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43, G_1p); return N_4tmp; } void witness_forwardQ_PCD_ThingD___serialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { - $step_serialize(self->W_PA_34, state); + $step_serialize(self->W_PA_43, state); } witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { $WORD $tmp; @@ -171,7 +171,7 @@ witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_ } self = $DNEW(witness_forwardQ_PCD_Thing, state); } - self->W_PA_34 = $step_deserialize(state); + self->W_PA_43 = $step_deserialize(state); return self; } witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new() { @@ -253,8 +253,8 @@ void witness_forwardQ___init__ () { witness_forwardQ_PCD_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__repr__; witness_forwardQ_PCD_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PCD_Thing))witness_forwardQ_PCD_ThingD___init__; witness_forwardQ_PCD_ThingG_methods.gamma = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_gamma; - witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_total; witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_same; + witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_total; witness_forwardQ_PCD_ThingG_methods.__serialize__ = witness_forwardQ_PCD_ThingD___serialize__; witness_forwardQ_PCD_ThingG_methods.__deserialize__ = witness_forwardQ_PCD_ThingD___deserialize__; $register(&witness_forwardQ_PCD_ThingG_methods); diff --git a/compiler/lib/test/9-codegen/witness_forward.h b/compiler/lib/test/9-codegen/witness_forward.h index b44a95501..f95a04065 100644 --- a/compiler/lib/test/9-codegen/witness_forward.h +++ b/compiler/lib/test/9-codegen/witness_forward.h @@ -114,7 +114,7 @@ struct witness_forwardQ_PCD_ThingG_class { }; struct witness_forwardQ_PCD_Thing { struct witness_forwardQ_PCD_ThingG_class *$class; - witness_forwardQ_PA W_PA_34; + witness_forwardQ_PA W_PA_43; }; extern struct witness_forwardQ_PAG_class witness_forwardQ_PAG_methods; B_NoneType witness_forwardQ_PAD___init__(witness_forwardQ_PA W_self); @@ -137,6 +137,6 @@ extern struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_meth witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new(); B_NoneType witness_forwardQ_PCD_ThingD___init__(witness_forwardQ_PCD_Thing W_self); int64_t witness_forwardQ_PCD_ThingD_gamma(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing self); -int64_t witness_forwardQ_PCD_ThingD_total(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p); bool witness_forwardQ_PCD_ThingD_same(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p); +int64_t witness_forwardQ_PCD_ThingD_total(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p); void witness_forwardQ___init__ (); \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.input b/compiler/lib/test/9-codegen/witness_forward.input index caed9e7e9..ebfb0a4c8 100644 --- a/compiler/lib/test/9-codegen/witness_forward.input +++ b/compiler/lib/test/9-codegen/witness_forward.input @@ -39,18 +39,18 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): return (UNBOX __builtin__.int N_1tmp) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): @property - W_PA_34 : PA[Thing] + W_PA_43 : PA[Thing] pure def __init__ (W_self : PCD_Thing) -> None: - W_self.W_PA_34 = PBD_Thing() + W_self.W_PA_43 = PBD_Thing() PC.__init__@[Thing](W_self) return None pure def gamma (W_self : PCD_Thing, self : Thing) -> UNBOXED __builtin__.int: N_2tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 2) return (UNBOX __builtin__.int N_2tmp) - pure def total (W_self : PCD_Thing, G_1p : Thing) -> UNBOXED __builtin__.int: - N_3tmp: UNBOXED __builtin__.int = W_self.W_PA_34.total(G_1p) - return (UNBOX __builtin__.int N_3tmp) pure def same (W_self : PCD_Thing, G_1p : Thing, G_2p : Thing) -> UNBOXED __builtin__.bool: - N_4tmp: UNBOXED __builtin__.bool = W_self.W_PA_34.same(G_1p, G_2p) - return (UNBOX __builtin__.bool N_4tmp) + N_3tmp: UNBOXED __builtin__.bool = W_self.W_PA_43.same(G_1p, G_2p) + return (UNBOX __builtin__.bool N_3tmp) + pure def total (W_self : PCD_Thing, G_1p : Thing) -> UNBOXED __builtin__.int: + N_4tmp: UNBOXED __builtin__.int = W_self.W_PA_43.total(G_1p) + return (UNBOX __builtin__.int N_4tmp) # (recursive group) \ No newline at end of file From e963b34de264d629a898d9576013e61e368afa01 Mon Sep 17 00:00:00 2001 From: Johan Nordlander Date: Wed, 19 Aug 2026 12:07:40 +0200 Subject: [PATCH 6/7] Adapt to previous parentTEnv reversal --- compiler/lib/src/Acton/Types.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/lib/src/Acton/Types.hs b/compiler/lib/src/Acton/Types.hs index 33daed77c..300f97f91 100644 --- a/compiler/lib/src/Acton/Types.hs +++ b/compiler/lib/src/Acton/Types.hs @@ -1129,7 +1129,7 @@ instance InfEnv Decl where when (not $ null sigs) $ err2 sigs "Extension with new methods/attributes not supported" when (not (null asigs || notImplBody b)) $ err3 l (dom asigs) "Protocol method/attribute lacks implementation:" let te1 = unSig $ selfSubst n q asigs - fwds = selfSubst n q $ nubBy (\a b -> fst a == fst b) fsigs + fwds = selfSubst n q $ nubBy (\a b -> fst a == fst b) $ reverse fsigs te2 = te ++ te1 ++ unSig fwds b2 = addImpl te1 b1 ++ map fwdDef fwds return ([], [(extensionName us c, NExt q c ps te2 [] ddoc)], Extension l q c us b2 ddoc) @@ -1837,7 +1837,7 @@ instance Check Decl where checkEnv' env (Extension l q c us b ddoc) | isActor env n = notYet (loc n) "Extension of an actor" | isProto env n = notYet (loc n) "Extension of a protocol" - | otherwise = do --traceM ("## checkEnv extension " ++ prstr n ++ "(" ++ prstrs us ++ ")") + | otherwise = do --traceM ("## checkEnv extension " ++ prstr n' ++ "(" ++ prstrs us ++ ")") pushFX fxPure tNone wellformed env1 q (csu,wmap) <- wellformedProtos env1 us From c89a9a88ef893e49923b68d77ae93f62fd3e5b24 Mon Sep 17 00:00:00 2001 From: Johan Nordlander Date: Wed, 19 Aug 2026 12:23:05 +0200 Subject: [PATCH 7/7] Update golden files again --- compiler/lib/test/3-types/witness_forward.output | 8 ++++---- compiler/lib/test/9-codegen/witness_forward.c | 16 ++++++++-------- compiler/lib/test/9-codegen/witness_forward.h | 4 ++-- .../lib/test/9-codegen/witness_forward.input | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/lib/test/3-types/witness_forward.output b/compiler/lib/test/3-types/witness_forward.output index 5acf114f3..cb77a9f70 100644 --- a/compiler/lib/test/3-types/witness_forward.output +++ b/compiler/lib/test/3-types/witness_forward.output @@ -32,14 +32,14 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): pure def beta (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_26.__fromatom__(1) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): - W_PA_43: PA[Thing] = PBD_Thing() + W_PA_34: PA[Thing] = PBD_Thing() W_PA_30: __builtin__.Number[__builtin__.int] = __builtin__.IntegralD_int() pure def __init__ (W_self : Self) -> None: PC.__init__@[Thing](W_self) pure def gamma (W_self : Self, self : Thing) -> __builtin__.int: return W_PA_30.__fromatom__(2) - pure def same (W_self : Self, G_1p : Thing, G_2p : Thing) -> __builtin__.bool: - return W_PA_43.same(G_1p, G_2p) pure def total (W_self : Self, G_1p : Thing) -> __builtin__.int: - return W_PA_43.total(G_1p) + return W_PA_34.total(G_1p) + pure def same (W_self : Self, G_1p : Thing, G_2p : Thing) -> __builtin__.bool: + return W_PA_34.same(G_1p, G_2p) # (recursive group) \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.c b/compiler/lib/test/9-codegen/witness_forward.c index 0bd77444b..76c9b3cbe 100644 --- a/compiler/lib/test/9-codegen/witness_forward.c +++ b/compiler/lib/test/9-codegen/witness_forward.c @@ -140,7 +140,7 @@ witness_forwardQ_PBD_Thing witness_forwardQ_PBD_ThingG_new() { } struct witness_forwardQ_PBD_ThingG_class witness_forwardQ_PBD_ThingG_methods; B_NoneType witness_forwardQ_PCD_ThingD___init__ (witness_forwardQ_PCD_Thing W_self) { - ((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43 = ((witness_forwardQ_PA)witness_forwardQ_PBD_ThingG_new()); + ((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34 = ((witness_forwardQ_PA)witness_forwardQ_PBD_ThingG_new()); ((B_NoneType (*) (witness_forwardQ_PC))witness_forwardQ_PCG_methods.__init__)(((witness_forwardQ_PC)W_self)); return B_None; } @@ -150,16 +150,16 @@ int64_t witness_forwardQ_PCD_ThingD_gamma (witness_forwardQ_PCD_Thing W_self, wi #line 33 "test/src/witness_forward.act" return N_2tmp; } -bool witness_forwardQ_PCD_ThingD_same (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p) { - bool N_3tmp = ((bool (*) ($WORD, witness_forwardQ_Thing, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43))->$class->same)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43, G_1p, G_2p); +int64_t witness_forwardQ_PCD_ThingD_total (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p) { + int64_t N_3tmp = ((int64_t (*) ($WORD, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->total)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p); return N_3tmp; } -int64_t witness_forwardQ_PCD_ThingD_total (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p) { - int64_t N_4tmp = ((int64_t (*) ($WORD, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43))->$class->total)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_43, G_1p); +bool witness_forwardQ_PCD_ThingD_same (witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p) { + bool N_4tmp = ((bool (*) ($WORD, witness_forwardQ_Thing, witness_forwardQ_Thing))((witness_forwardQ_PA)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34))->$class->same)(((witness_forwardQ_PCD_Thing)(W_self))->W_PA_34, G_1p, G_2p); return N_4tmp; } void witness_forwardQ_PCD_ThingD___serialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { - $step_serialize(self->W_PA_43, state); + $step_serialize(self->W_PA_34, state); } witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_forwardQ_PCD_Thing self, $Serial$state state) { $WORD $tmp; @@ -171,7 +171,7 @@ witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingD___deserialize__ (witness_ } self = $DNEW(witness_forwardQ_PCD_Thing, state); } - self->W_PA_43 = $step_deserialize(state); + self->W_PA_34 = $step_deserialize(state); return self; } witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new() { @@ -253,8 +253,8 @@ void witness_forwardQ___init__ () { witness_forwardQ_PCD_ThingG_methods.__repr__ = (B_str (*) (witness_forwardQ_PCD_Thing))B_valueG_methods.__repr__; witness_forwardQ_PCD_ThingG_methods.__init__ = (B_NoneType (*) (witness_forwardQ_PCD_Thing))witness_forwardQ_PCD_ThingD___init__; witness_forwardQ_PCD_ThingG_methods.gamma = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_gamma; - witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_same; witness_forwardQ_PCD_ThingG_methods.total = (int64_t (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_total; + witness_forwardQ_PCD_ThingG_methods.same = (bool (*) (witness_forwardQ_PCD_Thing, witness_forwardQ_Thing, witness_forwardQ_Thing))witness_forwardQ_PCD_ThingD_same; witness_forwardQ_PCD_ThingG_methods.__serialize__ = witness_forwardQ_PCD_ThingD___serialize__; witness_forwardQ_PCD_ThingG_methods.__deserialize__ = witness_forwardQ_PCD_ThingD___deserialize__; $register(&witness_forwardQ_PCD_ThingG_methods); diff --git a/compiler/lib/test/9-codegen/witness_forward.h b/compiler/lib/test/9-codegen/witness_forward.h index f95a04065..b44a95501 100644 --- a/compiler/lib/test/9-codegen/witness_forward.h +++ b/compiler/lib/test/9-codegen/witness_forward.h @@ -114,7 +114,7 @@ struct witness_forwardQ_PCD_ThingG_class { }; struct witness_forwardQ_PCD_Thing { struct witness_forwardQ_PCD_ThingG_class *$class; - witness_forwardQ_PA W_PA_43; + witness_forwardQ_PA W_PA_34; }; extern struct witness_forwardQ_PAG_class witness_forwardQ_PAG_methods; B_NoneType witness_forwardQ_PAD___init__(witness_forwardQ_PA W_self); @@ -137,6 +137,6 @@ extern struct witness_forwardQ_PCD_ThingG_class witness_forwardQ_PCD_ThingG_meth witness_forwardQ_PCD_Thing witness_forwardQ_PCD_ThingG_new(); B_NoneType witness_forwardQ_PCD_ThingD___init__(witness_forwardQ_PCD_Thing W_self); int64_t witness_forwardQ_PCD_ThingD_gamma(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing self); -bool witness_forwardQ_PCD_ThingD_same(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p); int64_t witness_forwardQ_PCD_ThingD_total(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p); +bool witness_forwardQ_PCD_ThingD_same(witness_forwardQ_PCD_Thing W_self, witness_forwardQ_Thing G_1p, witness_forwardQ_Thing G_2p); void witness_forwardQ___init__ (); \ No newline at end of file diff --git a/compiler/lib/test/9-codegen/witness_forward.input b/compiler/lib/test/9-codegen/witness_forward.input index ebfb0a4c8..caed9e7e9 100644 --- a/compiler/lib/test/9-codegen/witness_forward.input +++ b/compiler/lib/test/9-codegen/witness_forward.input @@ -39,18 +39,18 @@ class PBD_Thing (PB[Thing], PA[Thing], __builtin__.value): return (UNBOX __builtin__.int N_1tmp) class PCD_Thing (PC[Thing], PA[Thing], __builtin__.value): @property - W_PA_43 : PA[Thing] + W_PA_34 : PA[Thing] pure def __init__ (W_self : PCD_Thing) -> None: - W_self.W_PA_43 = PBD_Thing() + W_self.W_PA_34 = PBD_Thing() PC.__init__@[Thing](W_self) return None pure def gamma (W_self : PCD_Thing, self : Thing) -> UNBOXED __builtin__.int: N_2tmp: UNBOXED __builtin__.int = (UNBOX __builtin__.int 2) return (UNBOX __builtin__.int N_2tmp) - pure def same (W_self : PCD_Thing, G_1p : Thing, G_2p : Thing) -> UNBOXED __builtin__.bool: - N_3tmp: UNBOXED __builtin__.bool = W_self.W_PA_43.same(G_1p, G_2p) - return (UNBOX __builtin__.bool N_3tmp) pure def total (W_self : PCD_Thing, G_1p : Thing) -> UNBOXED __builtin__.int: - N_4tmp: UNBOXED __builtin__.int = W_self.W_PA_43.total(G_1p) - return (UNBOX __builtin__.int N_4tmp) + N_3tmp: UNBOXED __builtin__.int = W_self.W_PA_34.total(G_1p) + return (UNBOX __builtin__.int N_3tmp) + pure def same (W_self : PCD_Thing, G_1p : Thing, G_2p : Thing) -> UNBOXED __builtin__.bool: + N_4tmp: UNBOXED __builtin__.bool = W_self.W_PA_34.same(G_1p, G_2p) + return (UNBOX __builtin__.bool N_4tmp) # (recursive group) \ No newline at end of file