Disambiguate unions independent of member class order (#230)#765
Open
vineethsaivs wants to merge 1 commit into
Open
Disambiguate unions independent of member class order (#230)#765vineethsaivs wants to merge 1 commit into
vineethsaivs wants to merge 1 commit into
Conversation
create_default_dis_func resolved unique discriminator fields in a single fixed-order pass: for each class it subtracted the fields of every not-yet-pinned class, so a class whose fields were all shared with another unpinned class got no unique field and was forced into the single fallback slot. A second such class then raised TypeError, even when a valid assignment existed. Because the order was fixed up front, whether it was found depended on the order the classes were passed in. Resolve unique fields iteratively to a fixpoint instead: on each pass, pin every class that has a non-default field unique among the classes not yet pinned; pinning a class can make a previously ambiguous one uniquely identifiable, so repeat until a pass pins nothing. The single class that still cannot be pinned becomes the fallback, and more than one is a genuine ambiguity (still a TypeError). Fixes python-attrs#230.
c2948bf to
b2d0716
Compare
Member
|
Interesting! LGTM. Will re-review in a couple of days and maybe merge. Thanks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #230.
Problem
create_default_dis_func(akacreate_uniq_field_dis_func) fails to disambiguate a valid union depending on the order its member classes are passed in.A valid assignment exists (
A->a,B->ab,C->bc), and 3 of the 6 orderings find it while the other 3 raise.Root cause
The "unique keys" branch resolves discriminator fields in a single fixed-order pass. For each class it subtracts the fields of every not-yet-pinned class and takes what's left as that class's unique fields. A class whose fields are all shared with another still-unpinned class therefore gets no unique field and is pushed into the single
fallbackslot; a second such class then raises. Since the processing order is fixed up front (sorted once by field count) and never re-selects which class to pin next, whether the valid assignment is found depends on the input order. In the example, ifCis processed whileBis still unpinned,C's only fieldbcis shared withB, soChas no unique field even though pinningBonabfirst would freebcforC.Fix
Resolve unique fields iteratively to a fixpoint. On each pass, pin every class that has a non-default field unique among the classes not yet pinned. Pinning a class removes its fields from consideration, which can make a previously ambiguous class uniquely identifiable, so passes repeat until one pins nothing. The single class that still cannot be pinned becomes the fallback; more than one remaining is a genuine ambiguity and still raises
TypeError. This is order-independent and contained to the one function; the discriminator-literal path and bothdis_funcvariants are unchanged.The generated
dis_funcstill checks keys in insertion (pin) order, and that stays correct: a class is pinned only on a field unique among the classes still unpinned on that pass, so its key cannot appear in the payload of any class pinned later or in the fallback, and an earlier-pinned class's key cannot appear in a later class's payload.Test
test_disambiguation_is_order_independentbuilds the union above and asserts that all 6 orderings both succeed and route each member's payload back to the correct class. It fails on the current code (3 of 6 orderings raise) and passes with this change. The existingtest_edge_errorscases (no fields, shared single field, unhelpful discriminator, and the fallback cases) still pass, as do the union suites.