Skip to content

fix(python): Resolve Composite/Any type unification and nested class field coercion (#882)#918

Draft
olivier-aws wants to merge 5 commits intomainfrom
fix/issue-882-composite-any-unification
Draft

fix(python): Resolve Composite/Any type unification and nested class field coercion (#882)#918
olivier-aws wants to merge 5 commits intomainfrom
fix/issue-882-composite-any-unification

Conversation

@olivier-aws
Copy link
Copy Markdown
Contributor

@olivier-aws olivier-aws commented Apr 14, 2026

Summary

Fixes two related root causes from #882 that prevented pending Python tests involving user-defined classes from progressing through the analysis pipeline.

Changes

1. Composite/Any/Box type unification

  • The Core type checker failed with "Impossible to unify Any with Composite" when user-defined class instances were used in contexts expecting Any (list insertion, dict storage, Any-typed variables).
  • 11 tests now progress past the type checker (previously: Internal error → now: Inconclusive with 0 failures).

2. Nested class field coercion

  • PythonToLaurel.lean had an explicit guard blocking coercion from user-defined class types to Any for nested object fields.
  • Removed the limitation so classes with fields typed as other user-defined classes can be translated.

Test status

  • 10 of 12 affected tests moved from "Internal error" to "Inconclusive" (0 failed, some unknown VCs remain)
  • 2 tests still have issues: test_object_in_list and test_recursive_data (field resolution after container retrieval — separate root cause)

Files changed

  • Strata/Languages/Python/PythonToLaurel.lean — main translation logic
  • Strata/Languages/Laurel/HeapParameterization.lean — heap parameter handling
  • Strata/Languages/Laurel/HeapParameterizationConstants.lean — constants
  • StrataTest/Languages/Python/ — new test files and expected outputs

Partially addresses #882

@olivier-aws olivier-aws requested a review from a team April 14, 2026 22:37
@olivier-aws olivier-aws marked this pull request as draft April 14, 2026 22:40
Fix two root causes of "Impossible to unify" type checking errors when
user-defined class instances are used in contexts expecting Any:

1. Rename heap Box datatype to $Box to avoid name collision with
   user-defined Python classes named "Box". Python identifiers cannot
   contain $ so this eliminates the collision. Affects
   HeapParameterizationConstants.lean and HeapParameterization.lean.

2. Add Composite→Any coercion (via Hole) in PythonToLaurel.lean for:
   - Comparison operands (is/is not None on Composite values)
   - List literal elements (Composite objects in [a, b])
   - Dict literal values (Composite objects in {"k": obj})
   - Field assignments on non-self objects (a.field = composite_val)
   - Variable assignments where target is Any-typed

Also fix with-statement variable handling: stop hoisting with-variables
(which incorrectly typed them as Any) and let the With case handle
declaration with the correct type.

Fixes 8 of 11 tests from GitHub issue #882. Remaining 3 have different
root causes (resolution failures, Composite __enter__ return type).

Core type checker fails to unify Composite with Any/Box
- Rename targetIsComposite to targetNeedsCoercion with inverted logic
  to clarify intent (Bug 1 / Recommendation 2)
- Add Composite→Any coercion for self.field = composite_val when no
  type annotation is present (Recommendation 5)
- Remove unused _ctx parameter from getWithItemsVars (Design Concern 2)
- Add TODO for replacing Hole-based coercion with from_ClassInstance
  pattern to preserve field values (Design Concern 1 / Recommendation 3)
- Clarify with-statement coercion comments: mkInstanceMethodCall already
  returns Any for new variables, so additional coercion is unnecessary
  (Bug 2 addressed with documentation)
- Fix stale comments: Box → $Box in HeapParameterization.lean
  (Stale Content 1, 2)
When a user-defined class has a field whose type is another user-defined
class (e.g., Outer.inner: Inner), the translation failed with "Coercion
from user-defined class to Any is not yet supported".

Root cause: four interrelated gaps in PythonToLaurel.lean:
1. wrapFieldInAny threw for UserDefined fields instead of passing through
2. self.field access threw for UserDefined fields
3. translateMethod typed all non-self params as Any, causing Composite/Any
   mismatch when __init__ assigns to Composite-typed fields
4. self.field = value coerced Composite RHS to Hole via coerceToAny

Fix: return UserDefined field expressions as-is (they are Composite heap
references used for further field access or coerced later); type composite
method parameters as UserDefined to match heap model expectations; skip
coercion for Composite-to-Composite field assignments.

Object-to-Any coercion not supported for nested class fields
…upported for nested class fields

- Fix non-self attribute assignment path to skip coercion for composite
  fields, mirroring the self path logic (Bug 1)
- Restore wrapFieldInAny for primitive self-field accesses so self.val
  gets wrapped in from_int/from_bool/etc., matching non-self path (Bug 2)
- Add TODO for annotated composite assignment using New instead of RHS
  when RHS is an existing variable (Design Concern 1)
- Add TODO for forward-reference class ordering issue in translateMethod
  (Design Concern 2)
- Update stale comment about RHS type in self.field assignment
- Fix misleading statement in memory doc about coerceToAny
- Add test for non-self receiver composite field reassignment
@olivier-aws olivier-aws force-pushed the fix/issue-882-composite-any-unification branch from c92aeac to e538f6f Compare April 14, 2026 22:43
@olivier-aws olivier-aws changed the title fix(python): Resolve Composite/Any type unification and related OOP issues (#882) fix(python): Resolve Composite/Any type unification and nested class field coercion (#882) Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants