Emitter seal reaches through nested coproducts (one member-type reader for the deserialize walk) - #12999
Merged
Conversation
…e deserialize walk type_expr_reaches_sealed_carrier read a looked-up coproduct's variants as fields; child_type_node resolves a variant to the coproduct itself, already in the seen-set, so a seal two coproducts deep was never reached and its containers kept a derived Deserialize (a second, unsealed mint). The top-level enum entry read variants correctly, so the two readings disagreed. member_type_nodes now defines a declaration's member types once (product: fields; coproduct: variant payload fields) and the walk and both entries use it. Controls in emitter_sole_constructor_seal_witness_test: a seal two coproducts deep drops Deserialize from both containers; the same shape over an Int keeps it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 2, 2026
The stage0 v1_compiler_emit_rust.rs mirror conflicted between this branch's pre-squash #12999 merge and main. Every src/v1/*.dag source here is identical to main's, so main's mirror is the regeneration of this tree; it is taken as such. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 2, 2026
…_field_selection_test (a superset of #12999's nested guard), VariantPattern arity per main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Defect.
v1.compiler.emit_rusttype_expr_reaches_sealed_carrierwalks a type looking for asole_constructorrecord, so that any container reaching one dropsDeserialize. A derivedDeserializewould be a second, unsealed way to build the record (seedag/extdeps/languages/rust/capabilities.dag, the sealed-construction derive contract).When the walk looked up a coproduct, it read the variants as if they were fields.
child_type_noderesolves a variant to the coproduct itself, which is already in the seen-set, so the walk stopped there. A seal one coproduct deep was caught by the top-level enum entry (enum_variant_payloads_forbid_deserialize). A seal two coproducts deep was not.Where it surfaced. #12942 (MQ-5) puts a sealed
BodyTermLoweredSealunderv2.std.nodeCoreEdgeLabel, three containers down fromEdgeLabel. The containers kept their derive, and the self-host emitted crate refused with E0277:Rc<CoreEdgeLabel>/Box<CoreEdgeLabel>does not implementDeserialize. Main has no nested seal today, so the defect is latent there.Fix. The two readings disagreed, so there is now one reader.
member_type_nodesdefines a declaration's member types: a product's fields, or a coproduct's variant payload fields.decl_member_type_nodesandmembers_forbid_deserializeroute the recursive walk,item_forbids_deserializeand the enum emit site through it.enum_variant_payloads_forbid_deserializeanddecl_children_forbid_deserializeare deleted.This is option A of the choice the operator made on 2026-10-02: keep the existing rule (a sealed record has no
Deserialize, transitively). The alternative, a hand-writtenDeserializethat always refuses, would have lowered every sealed type from a compile-time impossibility to a runtime refusal.Controls (
test.claim.emitter_sole_constructor_seal_witness_test):nested_coproduct_over_a_seal_drops_deserialize(red on the old emitter):OuterholdsInner, which holdsSealed. Neither enum may deriveDeserialize.nested_plain_coproduct_keeps_deserialize: the same shape over anIntkeeps the derive. This proves the negative pattern can match, so the red is not vacuous.Execution. The claims need a corpus-scale
claim_batch. BuildBuddy refuses it (HostBudgetUnreadable: the executor exposes no cgroup limit; seegunbc.host_budget_source), so CI is the executor.v1_compiler_emit_rust.rsturns it green.Downstream risk, stated: once #12942's seal exists,
EdgeLabelandNodeloseDeserializetransitively. If the emitted compiler decodes aNodeanywhere, #12942's build will name that site, and it is settled there (a decodedNodemust never carry a seal anyway).🤖 Generated with Claude Code