## ~jasper/type_stack_calc

fe1ae9a8389aa354af54d0778fa119d3e9037bea — Jasper den Ouden 9 months ago
```Complaining about type calculating of recursion not being correct, some changes there. Minor change in test, comments
```
```3 files changed, 30 insertions(+), 12 deletions(-)

M test/sets/intersection.py
M type_stack_calc/base/variant_able_function.py
M type_stack_calc/ib/tc_variant.py
```
`M test/sets/intersection.py => test/sets/intersection.py +3 -1`
```@@ 45,8 45,10 @@ check([r(0,10), s(0,1)], [r(3,3)],
('subtract',  [r(-3,7), s(0,1)]),
('gt',   [b(None)]))  # TODO need to move boolean into sets.

+print(si(r(0,1), s(0,1)))
+
import random  # Add constant, hammer more.
-for x in [0] + [random.randint(-20,20) for _i in range(10)]:
+for x in [0, 1] + [random.randint(-20,20) for _i in range(10)]:
check([r(0,10), s(0,1)], [r(x, x)],
('mult', [rm(0,x*10), s(0, abs(x))] if x!=0 else [r(0,0)]),

```
`M type_stack_calc/base/variant_able_function.py => type_stack_calc/base/variant_able_function.py +5 -2`
```@@ 23,10 23,13 @@ Provides just `.ensure_variant`"""
key = types_key(inp)
vdict = getattr(self, f"variants_{code_name}")
variant = vdict.get(key)
-        if variant is None:
+        # NOTE: function-recursion (loops) shouldn't happen it will just
+        #  produce the existing variant.
+        if variant is None:
variant = TCVariant(code_name, self, inp, depth)
vdict[key] = variant  # Remember it,
-            variant.variant_tp_calc(real)  # AFTER type calculate, it may refer back to the remembered.
+            variant.variant_tp_calc(real)
+            # AFTER type calculate, it may refer back to the remembered.

return variant

```
`M type_stack_calc/ib/tc_variant.py => type_stack_calc/ib/tc_variant.py +22 -9`
```@@ 53,6 53,8 @@ class TCVariant(Ided, BaseFunctionVariant):
f"Reached max depth of function calls at {depth}\n ({self})"
self.c_id = None  # C output ID.

+        self.unfinished = None  # Starts in invalid finishing state.
+
@property
def n(self): return len(self.parent.args)
@property

@@ 70,9 72,13 @@ class TCVariant(Ided, BaseFunctionVariant):
return getattr(self.parent, f"code_{self.code_name}")

# TODO in some cases no interest in tc_code, just the calculated type.
+
+# TODO make it actually correct.
+#  + If it received an `TpUnknown` or changes, it's unresolved-distance is 0
+#  + Otherwise it's the smallest distance of stuff it called, plus one. If none, the distance is infinite.
+#  + If the distance is greater than the deepest depth of call, then it is finished.
#
-# TODO it's wrong if more than one layer of recursion.
-#  lets say two: the first one will keep returning TpUnknown(), and the second will accept it.
+# So need to track depth of call and these distances. Note that if there is no recursion the bottom will get "infinite" as value, causing the next layer.. etc.
def variant_tp_calc(self, real):
"""Calculates a type-compiled variant. (does hard work)"""
self.notify_real(real)

@@ 93,7 99,6 @@ class TCVariant(Ided, BaseFunctionVariant):
parent = self.parent
max_try_cnt = getattr(parent, f"max_try_cnt_{self.code_name}")
while True:  # Calculate types it until the type stabilizes. # TODO will it?
-            prev_tp = tp
# Variables, same ach time, other functions is what changes.
vs = parent.scope.sub(True, zip(args, inp), depth=self.depth + 1)
vs.get_comp('~r').set(ReturnCatchType(self_id))

@@ 107,10 112,17 @@ class TCVariant(Ided, BaseFunctionVariant):
tc_code = tc_code + [c1 for c1 in extra if c1 is not None]
assert isinstance(vs['~r'].val, ReturnCatchType), vs['~r']
tp = vs['~r'].val.ret_stack()   # Fill return type.
-
-            self.ret_stack = tp   # Unchanged, result is good.
-            if try_cnt > 0 and types_eq(prev_tp, tp):
-                tc_code = [c1 for c1 in tc_code if c1 is not None]
+
+            # TODO now what if loop of unfinished objects..
+            tc_code = [c1 for c1 in tc_code if c1 is not None]
+            objections = self.unfinished is None or \
+                         any(getattr(c1, 'unfinished', None)
+                             for c1 in tc_code)
+
+            self.ret_stack = tp
+            # If no longer changing and can finish now, it's good.
+            if not objections and types_eq(self.unfinished, tp):
+                self.unfinished = False
self.tc_code = tc_code
for el in tc_code:  # Keep count what's actually used.
if fun := getattr(el, 'notify_real', None): fun()

@@ 122,12 134,13 @@ class TCVariant(Ided, BaseFunctionVariant):
# NOTE/TODO if produce C code(compiled result) here
#  won't need it in any case?
return
+            self.unfinished = tp
tc_code.clear()  # Reset code, or it will appear for each time.
try_cnt += 1
assert try_cnt < max_try_cnt, \
f"""Hit max attempt count at {try_cnt} types versus previous:
-{tp} vs {prev_tp}
-{types_key(prev_tp)} vs {types_key(tp)}"""
+{tp} vs {self.unfinished}
+{types_key(self.unfinished)} vs {types_key(tp)}"""

def notify_the_cc(self):  # TODO better name?
"""Locks all the `ComponentCollection`s made from here."""

```