Summary
An inherited method on a class defined inside a function is never dispatched: the class's superclass resolves, but a call of the inherited method on an instance of the nested class reaches no call-graph node for it, and a parameter of that method reads as absent.
Measured on master c94b03d0a and again with the module-order barrier from #944 applied, with the same result, so this is not the order defect.
The Fixture
One module, so no import and no module order is involved:
import tensorflow as tf
class Base:
def scale(self, x):
return x * 2.0
def scale_inside(x):
class Inner(Base):
pass
return Inner().scale(x)
b = scale_inside(tf.ones((4,)))
assert b.shape == (4,)
The harness pin on Base.scale (one tensor parameter, x of (4,) float32) fails with Function must exist in call graph: Base.scale has no node at all, so the call inside scale_inside never dispatched to it.
What Is Not The Cause
- The class hierarchy is right. In a three-module variant of the same shape (the base imported from another module, subclasses at top level, inside a function and inside a class), the function-nested class's superclass resolves to the base in both module orders once the order barrier is in place, so the entity walk does register a class defined inside a function and its base link is not the gap.
- Module order is not involved. The single-module fixture above fails identically with and without the barrier, and the top-level and class-nested siblings in the three-module variant dispatch their inherited method and deliver their parameter shapes,
(2, 3) and (5, 6, 7), while the function-nested sibling's (4,) is absent.
So the miss is in how the inherited member reaches the nested class's instances: the class-object emission for a class defined in a function body, or the read of the base's class object from that scope, does not carry the inherited method the way a top-level or class-nested definition does.
Expected
Base.scale has a node, and its x parameter reads (4,) float32.
Test
TestCrossModuleBaseOrder.testFunctionNestedSubclassSameModule carries the fixture as an expected failure with a TODO naming this issue, beside the three-module variant whose function-nested arm documents the same absence.
Summary
An inherited method on a class defined inside a function is never dispatched: the class's superclass resolves, but a call of the inherited method on an instance of the nested class reaches no call-graph node for it, and a parameter of that method reads as absent.
Measured on master
c94b03d0aand again with the module-order barrier from #944 applied, with the same result, so this is not the order defect.The Fixture
One module, so no import and no module order is involved:
The harness pin on
Base.scale(one tensor parameter,xof(4,) float32) fails withFunction must exist in call graph:Base.scalehas no node at all, so the call insidescale_insidenever dispatched to it.What Is Not The Cause
(2, 3)and(5, 6, 7), while the function-nested sibling's(4,)is absent.So the miss is in how the inherited member reaches the nested class's instances: the class-object emission for a class defined in a function body, or the read of the base's class object from that scope, does not carry the inherited method the way a top-level or class-nested definition does.
Expected
Base.scalehas a node, and itsxparameter reads(4,) float32.Test
TestCrossModuleBaseOrder.testFunctionNestedSubclassSameModulecarries the fixture as an expected failure with aTODOnaming this issue, beside the three-module variant whose function-nested arm documents the same absence.