Skip to content

Distinct function objects compare equal based on representation #346

@alexmojaki

Description

@alexmojaki

Summary

Monty collapses distinct function objects together based on their compiled function ID / representation.

That shows up in two nearby ways:

  • nested functions with no defaults compare identical and equal across separate calls
  • nested functions with defaults compare equal across separate calls

CPython treats all of these as distinct function objects.

Repro

import pydantic_monty


def run(code: str):
    return pydantic_monty.Monty(code).run()


assert run(
    """
def outer():
    def inner():
        return 1
    return inner

a = outer()
b = outer()
(a is b, a == b)
"""
) == (True, True)


assert run(
    """
def outer():
    def inner(y=1):
        return y
    return inner

a = outer()
b = outer()
(a is b, a == b)
"""
) == (False, True)

Actual behavior

Monty returns:

(True, True)
(False, True)

Expected behavior

CPython returns:

(False, False)
(False, False)

These are distinct runtime function objects, so they should not compare equal or identical just because they came from the same compiled function body.

Why this looks representation-driven

In the VM:

  • functions with no defaults are emitted as inline Value::DefFunction(func_id)
  • functions with defaults are emitted as heap HeapData::FunctionDefaults { func_id, defaults }

Relevant code:

  • crates/monty/src/bytecode/vm/mod.rs

Equality then appears to collapse objects by func_id:

  • Value::DefFunction(f1) == Value::DefFunction(f2) compares f1 == f2
  • HeapData::FunctionDefaults(a) == HeapData::FunctionDefaults(b) compares only func_id

Relevant code:

  • crates/monty/src/value.rs
  • crates/monty/src/heap_data.rs

This also affects hash/container behavior. For example, two distinct no-default nested function objects collapse to a single set element and work as the same dict key.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions