question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Type inference recursion error for self referencing types

See original GitHub issue

I don’t know if this should be labeled as a bug or missing feature, but is worth exposing it. Basicaly, here I am trying to self reference(forward declaration) a structref without using the undocumented DeferredType.

example code
import numba as nb
from numba import jit, njit, types, typed, float64, int64, typeof
from numba.experimental import structref
from numba.core.extending import overload_method
from numba.experimental.structref import new, register

@structref.register
class PolygonStructType(types.StructRef):

    def preprocess_fields(self, fields):
        self.name = f"numba.PolygonStructType#{id(self)}" # temp name to allow Optional instantiation, this workaround can be replaced by extending StructRef or making StructRef to assign itself a name before preprocess_fields is called
        fields = tuple([
            ('value', types.Optional(int64)),
            ('parent', types.Optional(self)),
            ])

        return fields

polygon_struct_type = PolygonStructType(fields=(('value', types.Any), ('parent', types.Any)))

class PolygonStruct(structref.StructRefProxy):
    def __new__(cls, value, parent):
        return structref.StructRefProxy.__new__(cls, value, parent)

    @property
    def value(self):
        return PolygonStruct_get_value(self)

    @property
    def parent(self):
        return PolygonStruct_get_parent(self)

@njit
def PolygonStruct_get_value(self):
    return self.value

@njit
def PolygonStruct_get_parent(self):
    return self.parent

structref.define_proxy(PolygonStruct, PolygonStructType, ["value", "parent"])

@overload_method(PolygonStructType, "flip")
def PolygonStruct_flip(self):
    def impl(self):
        if self.value is not None:
            self.value = -self.value
    return impl

@njit
def test():
    # p = PolygonStruct(0, None)
    p = new(polygon_struct_type)
    p2 = new(polygon_struct_type)
    p2.value = 1
    p.parent = p2
    p.value = 3
    
    p.flip()
    return p.value

y = test()
print(y)

IMO, the usage of recursive algorithms in general is not recommended for production code. And forward declaration for types is an essential feature in strong typed languages. There is no real workaround for it. I don’t understand why numba does not have it already…

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
rpopovicicommented, Aug 28, 2020

the error you are seing comes from traverse_types() in models.py. that’s where the inf recursion takes place

0reactions
sklamcommented, Sep 2, 2020

Regarding DeferredType, it was added for jitclass as part of an experiment just to prove that circular references work. However, it was not documented because there are still issues. The most important one is that Numba does not know how to collect circular references.

Anyway, I’m experimenting with a patch (https://github.com/numba/numba/pull/6209) so the following use of DeferredType will work:

@structref.register
class PolygonStructType(types.StructRef):
    def preprocess_fields(self, fields):
        # temp name to allow Optional instantiation, this workaround can be
        # replaced by extending StructRef or making StructRef to assign itself
        # a name before preprocess_fields is called
        self.name = f"numba.PolygonStructType#{id(self)}"

        # ======= new code =======
        # Create deferred type
        PolygonStructType_self = types.DeferredType()
        PolygonStructType_self.define(self)
        # ========= end =========
        fields = tuple(
            [
                ("value", types.Optional(int64)),
                ("parent", types.Optional(PolygonStructType_self)),
            ]
        )
        return fields
Read more comments on GitHub >

github_iconTop Results From Across the Web

Recursive type inference works for object literal but not for ...
TypeScript's inference involves a recursive depth-first search of a program's abstract syntax tree, as described in ...
Read more >
Type inference with polymorphic recursion
The Damas-Milner. Calculus is the typed. A-calculus underlying the type system for ML and several other strongly typed polymorphic functional languages.
Read more >
Zig Language Reference
This syntax tells the Zig compiler that the function will either return an error or a value. An error union type combines an...
Read more >
Weblogs Forum - The Importance of Recursive Types
The distinction is that the following (B) is legal with recursive types and can only be disallowed with a true self-referential type.
Read more >
prototype for eliminating recursive type parameters in ...
go/types: avoid infinite recursion in recursive instantiation. Type inference uses type parameter pointer identity to keep track of the
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found