APrioriInvestments / typed_python

An llvm-based framework for generating and calling into high-performance native code from Python.
Apache License 2.0
198 stars 8 forks source link

Equality comparison on Held class members fail on first evaluation #404

Closed launeh closed 1 year ago

launeh commented 2 years ago

I have a Held class with a single member that defines __eq__ . For two instances, h1 and h1 with members of equal value h1 == h2 evaluates to False the first time then True on subsequent evaluations. This appears to happen for @Held classes where the expression used in a non-Entrypointed code block. i.e. the issue goes away if the @Held annotation is removed or if the enclosing function is Entrypointed

Reproduce with the following:


from typed_python.compiler.runtime import Entrypoint
from typed_python import Class, Final, Member, Held

@Held
class TS(Class, Final):
    ts = Member(float)

    @Entrypoint
    @staticmethod
    def make(ts: float):
        return TS(ts=ts)

    @Entrypoint
    def __eq__(self, other) -> bool:
        return self.ts == other.ts

def test_ts_eq():
    ts1 = TS.make(5)
    ts2 = TS.make(5)

    print("ts1 == ts2", ts1 == ts2)

@Entrypoint
def test_ts_eq_entrypointed():
    ts1 = TS.make(5)
    ts2 = TS.make(5)

    print("ts1 == ts2", ts1 == ts2)

print("\nExpect all to be true\n")

print("\nheld, not-entrypointed")
test_ts_eq()  # prints False, expected True
test_ts_eq()
test_ts_eq()

print("\nheld, entrypointed")
test_ts_eq_entrypointed()
test_ts_eq_entrypointed()
test_ts_eq_entrypointed()
guslonergan commented 2 years ago

If you insert a print statement before returning out of the __eq__ method, you'll see something interesting... Namely, if the expression ts1 == ts2 is evaluated inside another entrypointed function, it uses the __eq__ overload you defined (and evaluates to True as expected), but if it's evaluated in the interpreter it doesn't even defer to your overload. So it's presumably using some builtin notion of equality of held class instances, which is apparently nondeterministic.

So, there are two things that need fixing - (i) the nondeterminism of builtin notion of equality of held class instances. (I think it should be returning True if the instances have the same type and equal members, but the default semantics here are less important than the nondeterminism!) (ii) the failure of the interpreter to recognize that evaluating an expression like ts1 == ts2 involving held classes should check for magic method implementations.