Skip to content

names bound at some point in a class scope, but not at point of access, fall back to global scope #875

@carljm

Description

@carljm

Python's scoping semantics in class scopes are really quite odd. There's a subtle difference at runtime between these two examples:

x: str

def f(x: int):
    class C:
        reveal_type(x)  # revealed: int

def g(x: int):
    class C:
        reveal_type(x)  # revealed: str
        x = None

In both examples, x has no value assigned to it in the scope of class C at the point where it is referenced (in the reveal_type).

In the case of f where x is never bound in the scope of class C, at runtime a LOAD_DICT_OR_DEREF opcode is used, which will fall back to the x in the scope of f. But if x is bound at any point in the scope of C (as occurs in g), then a LOAD_NAME is used instead, which falls back only to the global scope, bypassing any enclosing function scopes.

Currently we don't model this difference correctly, so we wrongly infer int in both cases above. It should be a fairly simple change to TypeInferenceBuilder::infer_place_load to fix this (I think).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedContributions especially welcomeruntime semanticsAccurate modeling of how Python's semantics work at runtime

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions