diff --git a/dag/extdeps/languages/rust/emit.dag b/dag/extdeps/languages/rust/emit.dag index eb8d97a3668..906424fb1be 100644 --- a/dag/extdeps/languages/rust/emit.dag +++ b/dag/extdeps/languages/rust/emit.dag @@ -76,7 +76,8 @@ data rust_simple_method_specs: List = [ { method_name: "enumerate", template: "\{recv\}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()", wraps_result: true }, { method_name: "chars", template: "\{recv\}.chars().map(|c| c as i64).collect::>()", wraps_result: true }, { method_name: "skip", template: "\{recv\}.iter().cloned().skip(\{arg\} as usize).collect::>()", wraps_result: true }, - { method_name: "take", template: "\{recv\}.iter().cloned().take(\{arg\} as usize).collect::>()", wraps_result: true } + { method_name: "take", template: "\{recv\}.iter().cloned().take(\{arg\} as usize).collect::>()", wraps_result: true }, + { method_name: "is_empty", template: "\{recv\}.is_empty()", wraps_result: false } ] fn rust_method_templates() -> Map { @@ -255,7 +256,10 @@ data rt_function_registry: List = [ { name: "is_xid_start", bridge_name: "is_xid_start", passes_by_ref: false, wraps_result: false }, { name: "is_xid_continue", bridge_name: "is_xid_continue", passes_by_ref: false, wraps_result: false }, - { name: "is_emoji_ident", bridge_name: "is_emoji_ident", passes_by_ref: false, wraps_result: false } + { name: "is_emoji_ident", bridge_name: "is_emoji_ident", passes_by_ref: false, wraps_result: false }, + + { name: "symbol_lexeme", bridge_name: "symbol_lexeme", passes_by_ref: false, wraps_result: false }, + { name: "symbol_intern_lexeme", bridge_name: "symbol_intern_lexeme", passes_by_ref: false, wraps_result: false } ] fn rt_functions() -> Map { diff --git a/dag/std/primitive_identity.dag b/dag/std/primitive_identity.dag index aa7f80f2806..0430947387c 100644 --- a/dag/std/primitive_identity.dag +++ b/dag/std/primitive_identity.dag @@ -31,6 +31,7 @@ import std.primitive_projection { primitive_length, primitive_map_insert, primitive_map_get, primitive_empty_map, primitive_decl_facts, primitive_export_signature_facts, primitive_data_decl_type_facts, primitive_concept_decl_facts, primitive_concept_decl_facts_live, + primitive_symbol_lexeme, primitive_symbol_intern_lexeme, } data primitive_identity_d0_scope_note: String = "D0 is NOT terminal acceptance and does NOT close the subject universe. Landed and executing: join mechanism across five carriers, five refusal variants typed, three refusal detectors executing with witnesses (see primitive_d0_refusal_execution_note), derived census across all five surfaces including InterpreterDispatch from gunbc.v1_interpreter_primitive_surface. D1 (interpreter-primitive-roster consumption): measurement scaffold deleted; primitive_surface_census_derived reads the roster authority. Population-wide identity join closure remains the terminal acceptance bar." @@ -1010,6 +1011,8 @@ data primitive_projection_target_canonical_names: List = [ "data_decl_type_facts", "concept_decl_facts", "concept_decl_facts_live", + "symbol_lexeme", + "symbol_intern_lexeme", ] fn primitive_declared_definitions() -> List { @@ -1314,6 +1317,21 @@ data primitive_concept_decl_facts_traversal: PrimitiveTraversalOrderFact = Primi data primitive_concept_decl_facts_live_traversal: PrimitiveTraversalOrderFact = PrimitiveTraversalOrderFact { primitive: primitive_concept_decl_facts_live, order: SourcePreservingOrder } +// THE TWO SYMBOL BRIDGES TRAVERSE NOTHING, and OrderFreeResult is the honest arm rather than the +// convenient one. Both are scalar: symbol_lexeme maps one Symbol to its text and +// symbol_intern_lexeme is its inverse, so there is no collection to walk and the result is a +// function of the input alone -- it cannot depend on an order because no order is consulted. That +// is a different statement from HostUnspecifiedOrder, which would claim a real traversal whose +// order the host does not pin, and asserting it here would fabricate a leak the primitive cannot +// have. Adding these rows is an OBLIGATION of declaring the primitives, not bookkeeping beside it: +// v2.lens.determinism closes its denominator over primitive_declared_definitions, so a primitive +// with no traversal fact makes the closure false. That is how this was found -- nine reach_witness +// rows went red, including determinism_denominator_is_closed_on_declared_primitives, the row whose +// entire job is to notice exactly this. +data primitive_symbol_lexeme_traversal: PrimitiveTraversalOrderFact = PrimitiveTraversalOrderFact { primitive: primitive_symbol_lexeme, order: OrderFreeResult } + +data primitive_symbol_intern_lexeme_traversal: PrimitiveTraversalOrderFact = PrimitiveTraversalOrderFact { primitive: primitive_symbol_intern_lexeme, order: OrderFreeResult } + data primitive_projection_traversal_facts: List = [ primitive_length_traversal, primitive_map_insert_traversal, @@ -1324,6 +1342,8 @@ data primitive_projection_traversal_facts: List = [ primitive_data_decl_type_facts_traversal, primitive_concept_decl_facts_traversal, primitive_concept_decl_facts_live_traversal, + primitive_symbol_lexeme_traversal, + primitive_symbol_intern_lexeme_traversal, ] fn primitive_declared_traversal_facts() -> List { diff --git a/dag/std/primitive_projection.dag b/dag/std/primitive_projection.dag index a252f902ce8..47a2caa21b9 100644 --- a/dag/std/primitive_projection.dag +++ b/dag/std/primitive_projection.dag @@ -53,6 +53,14 @@ data primitive_export_signature_facts: PrimitiveIdentity = primitive_identity_sl data primitive_data_decl_type_facts: PrimitiveIdentity = primitive_identity_slug(name: "data_decl_type_facts") data primitive_concept_decl_facts: PrimitiveIdentity = primitive_identity_slug(name: "concept_decl_facts") data primitive_concept_decl_facts_live: PrimitiveIdentity = primitive_identity_slug(name: "concept_decl_facts_live") +data primitive_symbol_lexeme: PrimitiveIdentity = primitive_identity_slug(name: "symbol_lexeme") +data primitive_symbol_intern_lexeme: PrimitiveIdentity = primitive_identity_slug(name: "symbol_intern_lexeme") + +data symbol_lexeme_seam_disposition_note: String = "THE TWO SYMBOL BRIDGES WERE HOST SEAMS THAT NOTHING SAID WERE HOST SEAMS, and the cost of that silence is why they are rostered here rather than left alone. v2.std.compilers.lexing symbol_lexeme and symbol_intern_lexeme have self-call bodies -- the HostRealizedSeam shape exactly, correct by construction because reaching one recurses to the evaluation-budget refusal -- and the interpreter has carried real arms for both since they were written (gunbc.v1_interpreter_primitive_surface v4_bridge.symbol_lexeme, v4_bridge.symbol_intern_lexeme). But with no roster row the RESOLVER saw an ordinary declaration, so Rust emission emitted the declaration, and `pub fn symbol_lexeme(sym: String) -> String { symbol_lexeme(sym) }` COMPILES. The emitted v2 compiler closure therefore carried two functions that type-check, pass every gate we own, and diverge from the interpreter by not terminating -- the DESIGN section 5 fabricated-plausible-output failure in its quietest form, since unlike the sibling seams (decl_facts and friends, which at least refuse loudly as unresolved v1_rt symbols) nothing anywhere reports this one. + +WHAT MAKES THE RUST REALIZATION HONEST RATHER THAN A SHIM: extdeps.languages.rust.types already declares Symbol's target type as String, so on that target the interning table is the identity and both bridges ARE identity functions. That is a realization of the declared row, not a second opinion about it; a target where Symbol is NOT String would need a different realization and the registry is where it would say so. The interpreter's arms and the Rust registry rows now answer for the same two primitives, which is the agreement that was missing. + +THE RESIDUE IS NAMED, NOT CLOSED: a self-call body is a DECIDABLE structural marker of a host seam, so the compiler could refuse an unrealized one instead of emitting it. It does not yet, and until it does this roster is the only thing standing between a new v4_bridge declaration and another silently nonterminating emission. That check is this class's next-rung trigger." data primitive_projection_roster_note: String = "One row per declaration that is the modeled surface of a declared primitive. Every row was established by READING THE DECLARATION, not by matching its name: the HostRealizedSeam rows are declarations whose whole body is a self-call, and the ModeledProjection rows are declarations with a real body that the builtin arm intercepts ahead of. The single DivergentProjection row is v2.std.collection map_get, whose declared return type is Outcome> against the primitive's Optional; it is carried rather than omitted because omission and divergence are different answers -- omission means nobody has looked, divergence means someone looked and found two authorities. A consumer collecting call candidates must suppress the builtin co-candidate for the first two fidelities and must keep refusing on the third." @@ -68,6 +76,8 @@ fn primitive_projection_roster() -> List { primitive_projection_row(primitive: primitive_concept_decl_facts, module_path: "v2.std.concept_index", decl_name: "concept_decl_facts", fidelity: HostRealizedSeam), primitive_projection_row(primitive: primitive_concept_decl_facts_live, module_path: "v2.std.concept_index", decl_name: "concept_decl_facts_live", fidelity: HostRealizedSeam), primitive_projection_row(primitive: primitive_empty_map, module_path: "v2.std.collection", decl_name: "empty_map_primitive_delegate", fidelity: HostRealizedSeam), + primitive_projection_row(primitive: primitive_symbol_lexeme, module_path: "v2.std.compilers.lexing", decl_name: "symbol_lexeme", fidelity: HostRealizedSeam), + primitive_projection_row(primitive: primitive_symbol_intern_lexeme, module_path: "v2.std.compilers.lexing", decl_name: "symbol_intern_lexeme", fidelity: HostRealizedSeam), primitive_projection_row(primitive: primitive_length, module_path: "v2.std.algebra", decl_name: "length", fidelity: ModeledProjection), primitive_projection_row(primitive: primitive_map_insert, module_path: "v2.std.collection", decl_name: "map_insert", fidelity: ModeledProjection), primitive_projection_row(primitive: primitive_empty_map, module_path: "v2.std.collection", decl_name: "empty_map", fidelity: ModeledProjection), diff --git a/dag/test/claim/emitted_call_target_realization_witness_test.dag b/dag/test/claim/emitted_call_target_realization_witness_test.dag new file mode 100644 index 00000000000..b849d70e533 --- /dev/null +++ b/dag/test/claim/emitted_call_target_realization_witness_test.dag @@ -0,0 +1,396 @@ +module test.claim.emitted_call_target_realization_witness_test + +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +data emitted_call_target_realization_witness_note: String = "PERMANENT REGRESSION CONTROLS for the three emission defects that stood between the v2 compiler closure and a Rust crate rustc would accept (gunbc#9664 milestones 1 and 2). All three are one family -- emission ASSERTING a realization it had not established -- and each row here is the discriminating RED for one of them, paired with a boundary control that would go red if the repair had over-reached in the opposite direction.\n\nMEASURED POPULATIONS on the emitted closure at main 6447dd4, by `gunbc compile --entry src/v2/compiler/00_compile.dag --target rust` then `cargo check`: 260 coded errors, 254 of them E0425. Class C 102 (98 `v2` + 4 `std`), length 73, class B 65 (member 11, apply 11, is_empty 5, allocate_literal 4, step 3, init 3, and a tail of ones and twos).\n\nWHY THESE ARE NOT A SNAPSHOT. Each class's positive and negative assertion differ only in the fact the repair added, so no single edit satisfies both directions. The C rows would flip if decl_name reverted to the authored spelling; the length rows would flip if the registry membership gate were removed OR if the declaration fallback were extended to seams; the B rows would flip if runtime_bridge went back to a literal. The boundary controls (empty_map, a real bridge method) would flip if any of the three repairs had instead stopped emission reaching v1_rt at all.\n\nDISSOLVE-ON: never. Emission may change target SPELLING, but it may never again name a runtime symbol it has not established exists." + +// THE PROBE IMPORTS v2.std.collection AND THEN CALLS IT BY ITS QUALIFIED NAME, which looks +// redundant and is not. compile_dag_rust_emit_check compiles through the witness harness's +// restricted pool (2973 modules against the corpus's 4261), and a qualified spelling alone does not +// put the target module in it -- so without the import this row FAILED while the repair was green, +// which is how it was found. Measured both ways: the same probe emitted against the real corpus +// produces `crate::v2_std_collection::map_get(m.clone(), "key".to_string())` exactly as asserted. +// The import restores module presence; it does not answer the call, because decl_name is taken from +// the authored spelling at the CALL SITE regardless of what is imported -- which is why the +// negative assertion below still discriminates, and why falsifier 2 (restore the dotted spelling) +// must red this row. +// +// CLASS C, THE DISCRIMINATING RED. A qualified call carried the whole dotted path as its +// declaration name, so emission produced `crate::v2_std_collection::v2.std.collection.map_get(..)` +// -- the module prefix correct and the identifier a dotted path Rust reads as field access on an +// undefined value `v2`. map_get is the specimen because it is also DivergentProjection, so this +// row proves the qualified path reaches the DECLARATION and not the bridge. +test fn w_qualified_call_emits_only_the_declaration_last_segment() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.qualified_call_probe\nimport std.types { Int, String, Map }\nimport std.occurrence_identity { OccurrenceId }\nimport v2.std.collection { map_get }\nimport v2.std.diagnostic { Outcome }\nimport v2.std.optional { Optional }\nfn probe(m: Map) -> Outcome> { v2.std.collection.map_get(m: m, key: \"key\") }\n", + "src/test_claim_qualified_call_probe.rs", + ["crate::v2_std_collection::map_get("], + ["::v2.std.collection", "v2.std.collection.map_get("] + ) +} + +// LENGTH, THE DISCRIMINATING RED. v2.std.algebra length is a ModeledProjection of the `length` +// primitive and rt_function_registry has no `length` row, so emission took the identity arm of +// rust_runtime_bridge_name and wrote `v1_rt::length` -- a symbol the seed does not define -- at +// 73 sites. The declaration has a real fold body, so it is what a call must reach. +test fn w_unbridged_modeled_primitive_emits_its_declaration() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.length_projection_probe\nimport std.types { Int }\nimport std.occurrence_identity { OccurrenceId }\nimport v2.std.algebra { FreeMonoid, length }\nfn probe(xs: FreeMonoid) -> Int { length(xs: xs) }\n", + "src/test_claim_length_projection_probe.rs", + ["crate::v2_std_algebra::length("], + ["v1_rt::length("] + ) +} + +// THE BOUNDARY CONTROL FOR THE LENGTH REPAIR, AND THE ONE THAT PROVES THE GATE IS REGISTRY +// MEMBERSHIP RATHER THAN "ROSTER ROW MEANS DECLARATION". empty_map has a roster row AND a +// registry row, so it must still lower to the bridge. A repair that had simply routed every +// ModeledProjection to its declaration would go red here -- and would also emit +// v2.std.collection map_insert's `Map { lookup: fn .. }` body against an Rc carrier. +test fn w_bridged_modeled_primitive_still_lowers_to_the_bridge() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.bridged_empty_map_probe\nimport std.types { Int, String, Map }\nimport std.occurrence_identity { OccurrenceId }\nimport v2.std.collection { empty_map }\nfn probe() -> Map { empty_map() }\n", + "src/test_claim_bridged_empty_map_probe.rs", + ["v1_rt::rc_empty_map"], + ["crate::v2_std_collection::empty_map("] + ) +} + +// CLASS B, THE DISCRIMINATING RED. tier0 method resolution resolves an ordinary fn-typed RECORD +// FIELD through lookup_field_in_product, so a field call arrived as AlgebraMethodSemantics and the +// fallback at the end of that arm passed runtime_bridge: true unconditionally -- emitting +// `v1_rt::step`, and leaving emit_rust_generic_method_call's own callable-field arm, guarded on +// runtime_bridge == false, unreachable for the exact receiver it was written for. +test fn w_callable_record_field_lowers_as_a_field_not_a_bridge() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.callable_field_probe\nimport std.types { Int }\ntype Stepper { step: fn(Int) -> Int }\nfn probe(s: Stepper, n: Int) -> Int { s.step(n) }\n", + "src/test_claim_callable_field_probe.rs", + [".step)("], + ["v1_rt::step("] + ) +} + +// THE BOUNDARY CONTROL FOR THE B REPAIR. `trim` is in rt_function_registry and, unlike count / +// join / first and the rest of rust_simple_method_specs, has NO method template -- so it is one of +// the few names that actually REACHES the algebra fallback this repair changed. A repair that had +// stopped that fallback reaching v1_rt at all, rather than asking whether this particular name is +// realized, goes red here. +// +// count was the first choice for this row and is wrong, which is worth recording: it has a +// simple-method template, so it is answered before the fallback and would have gone red while +// testing nothing about the seam under repair -- a control passing through a codepath its +// subject never executes. +test fn w_registered_bridge_method_still_reaches_the_bridge() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.bridge_method_probe\nimport std.types { String }\nfn probe(s: String) -> String { s |> trim }\n", + "src/test_claim_bridge_method_probe.rs", + ["v1_rt::trim("], + ["compile_error"] + ) +} + +// THE SYMBOL BRIDGES, AND THIS ONE'S RED IS NOT AN ERROR COUNT. symbol_lexeme and +// symbol_intern_lexeme have self-call bodies -- host seams -- but carried no projection roster +// row, so the resolver saw ordinary declarations and emission emitted the declaration. The result +// COMPILES: `pub fn symbol_lexeme(sym: String) -> String { symbol_lexeme(sym) }` type-checks and +// never returns. That is why these two are absent from the E0425 census this file's other rows +// are measured against -- they were silent, not absent. A row asserting "no error" would have +// been green throughout the defect; this asserts the call reaches the realization. +test fn w_symbol_lexeme_reaches_its_host_realization_not_its_self_call() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.symbol_lexeme_probe\nimport std.types { String }\nimport v2.std.node { Symbol }\nimport v2.std.compilers.lexing { symbol_lexeme }\nfn probe(sym: Symbol) -> String { symbol_lexeme(sym: sym) }\n", + "src/test_claim_symbol_lexeme_probe.rs", + ["v1_rt::symbol_lexeme("], + ["crate::v2_std_compilers_lexing::symbol_lexeme("] + ) +} + +// THE SELF-CALL SEAM WALL, AND THIS IS THE ROW THAT MAKES THE CLASS STRUCTURAL RATHER THAN +// ROSTERED. Adding a self-calling declaration with no target realization used to emit it verbatim: +// `pub fn f(x) { f(x) }` compiles, and returns to no caller. Nothing reported it -- not the module +// index, not the compile-clean gate, not cargo check -- which is why the two symbol bridges were +// invisible until someone read the emitted bytes. The refusal now fires on the STRUCTURE, so a new +// seam cannot be added silently even if nobody remembers to roster it. +// +// The discriminating pair is the declaration itself: pre-wall the emitted module carries +// `pub fn declared_seam_without_realization`, post-wall it carries the refusal instead. +test fn w_unrealized_self_call_seam_refuses_instead_of_emitting_recursion() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.unrealized_seam_probe\nimport std.types { String }\nfn declared_seam_without_realization(x: String) -> String { declared_seam_without_realization(x: x) }\nfn probe(x: String) -> String { declared_seam_without_realization(x: x) }\n", + "src/test_claim_unrealized_seam_probe.rs", + ["unrealized host seam", "panic!"], + ["declared_seam_without_realization(x)"] + ) +} + +// THE REFUSAL KEEPS THE SIGNATURE AND THE CRATE KEEPS COMPILING. This is the row that separates the +// two refusal FORMS, and it is the whole point of the correction: compile_error! fails the entire +// crate, which is wrong for a seam nobody calls, because rustc's denominator is the whole emitted +// crate while the demand denominator is the entry-reachable closure. Five unreachable seams took +// the crate down under the old form. The declaration must therefore still be emitted, with its +// parameters and return type intact, so that dependent modules resolve and only an actual call +// fails. +test fn w_unrealized_seam_refusal_preserves_the_declaration_signature() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.unrealized_seam_shape_probe\nimport std.types { String }\nfn seam_with_no_realization(x: String) -> String { seam_with_no_realization(x: x) }\n", + "src/test_claim_unrealized_seam_shape_probe.rs", + ["pub fn seam_with_no_realization", "-> String", "panic!"], + ["compile_error!"] + ) +} + +// ORDINARY RECURSION IS NOT A SEAM, and this is the control that keeps the wall from refusing most +// of the compiler. The predicate is whole-body identity, not "contains a self-call": a match, an if +// or a let between the head and the recursive call means the body is not the call. +test fn w_ordinary_recursion_is_not_read_as_a_host_seam() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.ordinary_recursion_probe\nimport std.types { Int }\nfn countdown(n: Int) -> Int { if n <= 0 { 0 } else { countdown(n: n - 1) } }\n", + "src/test_claim_ordinary_recursion_probe.rs", + ["pub fn countdown"], + ["has no realization for this target"] + ) +} + +// A SEAM'S DECLARATION NAME IS NOT THE PRIMITIVE IT REALIZES, and this row is the one that caught +// it. v2.std.collection empty_map_primitive_delegate is a self-call body -- so the wall sees a seam +// -- and it IS realized, because its roster row names the `empty_map` primitive whose bridge is +// rc_empty_map. Keying the realization lookup on the DECLARATION name missed, because the registry +// holds `empty_map`, and the wall refused a seam that had a perfectly good realization. Measured, +// not predicted: it appeared as one of six refusals in the emitted closure. +// +// The discriminating direction is that this seam must NOT refuse while decl_facts, which genuinely +// has no realization, must. A wall that reverted to decl-name keying flips this row red and leaves +// the decl_facts row green, so the two cannot be satisfied by one edit. +test fn w_rostered_seam_resolves_realization_through_its_primitive_not_its_decl_name() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.delegate_seam_probe\nimport std.types { Int, String, Map }\nimport std.occurrence_identity { OccurrenceId }\nimport v2.std.collection { empty_map }\nfn probe() -> Map { empty_map() }\n", + "src/test_claim_delegate_seam_probe.rs", + ["v1_rt::rc_empty_map"], + ["has no realization for this target"] + ) +} + +// A REALIZED SEAM KEEPS ITS DECLARATION, and this row exists because removing it was a defect I +// shipped. The wall first suppressed a realized seam's item, reasoning that every call to a +// realized primitive routes to the bridge so nothing calls the declaration. Other modules still +// IMPORT it, so 10 unresolved imports (E0432) appeared in the emitted closure -- created by the +// suppression, not found by it. The same reasoning that made suppression look safe is what makes +// it unnecessary: the seam's body is a self-call, resolution routes it through the roster to the +// bridge, and ordinary emission writes the delegation by itself. +test fn w_realized_seam_keeps_its_declaration_and_delegates_to_the_bridge() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.realized_seam_probe\nimport std.types { String }\nimport v2.std.node { Symbol }\nimport v2.std.compilers.lexing { symbol_lexeme }\nfn probe(sym: Symbol) -> String { symbol_lexeme(sym: sym) }\n", + "src/test_claim_realized_seam_probe.rs", + ["v1_rt::symbol_lexeme("], + ["has no realization for this target"] + ) +} + +// is_empty HAD NO RUST REALIZATION AT ALL, and the class-B repair is what made that visible rather +// than what caused it. Before it, `xs |> is_empty` emitted `v1_rt::is_empty(..)` -- a symbol the +// seed does not define -- and appeared in the census as 5 x E0425. After it, the same 5 sites +// became typed refusals: correct in KIND, and still 5 errors. A conversion is not a repair, so this +// row exists to pin the actual realization. +// +// THE ROW IS A TARGET FACT, NOT EMITTER CODE. is_empty is an algebra template over FreeMonoid whose +// Rust realization is Vec::is_empty, exactly as count's is Vec::len -- so it belongs in +// rust_simple_method_specs beside count, and nothing in 05_emit_rust learns a new name. The +// negative assertion is the refusal message, because that is precisely the state this row leaves. +test fn w_is_empty_lowers_to_its_rust_realization_not_a_refusal() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.is_empty_probe\nimport std.types { Int, Bool }\nimport std.occurrence_identity { OccurrenceId }\nimport v2.std.algebra { FreeMonoid, is_empty }\nfn probe(xs: FreeMonoid) -> Bool { xs |> is_empty }\n", + "src/test_claim_is_empty_probe.rs", + [".is_empty()"], + ["v1_rt::is_empty(", "neither a resolved callable receiver field"] + ) +} + +// THE NEGATIVE IDENTITY CONTROL FOR THE is_empty ROW, and it caught a live hole rather than +// confirming a safe one. Adding is_empty to rust_simple_method_specs put a new spelling in front of +// the two rust_method_templates lookups -- and those lookups are keyed on the bare name with no +// receiver check, so a fn-typed record field named is_empty was captured by the target template and +// emitted as `recv.is_empty()` instead of `(recv.is_empty)(..)`. That is class B surviving one +// layer up: the class-B repair fixed the algebra FALLBACK and left the tables in front of it. +// +// The same hole existed for count, first, join, split, take, skip, last, chars and enumerate before +// is_empty was ever added; this row is the one that made it executable. +test fn w_callable_field_named_is_empty_is_not_captured_by_the_target_template() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.is_empty_field_probe\nimport std.types { Bool, Int }\ntype Probe { is_empty: fn(Int) -> Bool }\nfn probe(p: Probe, n: Int) -> Bool { p.is_empty(n) }\n", + "src/test_claim_is_empty_field_probe.rs", + [".is_empty)("], + ["p.is_empty()"] + ) +} + +// THE SAME CONTROL FOR A SPELLING THAT WAS ALREADY IN THE TABLE. count has had a template since +// before this change, so if the hole were specific to is_empty this row would be green on main and +// green here. It is not: it is the pre-existing instance of the class, and it is enrolled so the +// repair is measured against the whole table rather than the row that exposed it. +test fn w_callable_field_named_count_is_not_captured_by_the_target_template() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.count_field_probe\nimport std.types { Int }\ntype Counter { count: fn(Int) -> Int }\nfn probe(c: Counter, n: Int) -> Int { c.count(n) }\n", + "src/test_claim_count_field_probe.rs", + [".count)("], + ["c.count()"] + ) +} + +// THE ALGEBRA-PROFILE CONTROL FOR THE CALLABLE-FIELD TIER, and the direct regression control for +// the 202-refusal class it caused. The tier asked rust_record_field_needs_fn_rc, whose candidate +// list widens a receiver's name to its CONTAINER TEMPLATE ALGEBRA -- and an algebra declares its +// operations as arrow-typed members, so a Map receiver read as having a callable field named +// map_keys, and the tier emitted `(m.map_keys)(..)` for a call rustc then refused with E0609 no +// field `map_keys` on Rc>. The receiver here is a genuine Map, so the tier must +// decline it and the ordinary bridge lowering must survive. This is the discriminating red for +// narrowing the predicate to the receiver's OWN declared record: restore the candidate sweep and +// the must_not_contain clause fires. +test fn w_map_receiver_operation_is_not_read_as_a_callable_field() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.map_receiver_probe\nimport std.types { Int, String }\nfn probe(m: Map) -> List { map_keys(m) }\n", + "src/test_claim_map_receiver_probe.rs", + ["map_keys"], + [".map_keys)("] + ) +} + +// --------------------------------------------------------------------------------------------- +// THE TAIL CLASSES: the fifteen rustc refusals that remained on the 00_compile closure once the +// three families above were closed, one discriminating row per emitter defect. Each row's +// must_not_contain is the exact malformed spelling the emitter produced, so a revert of that one +// repair reddens exactly that row. +// --------------------------------------------------------------------------------------------- + +// WITNESS CARRIER, THE DISCRIMINATING RED. A Holds value bound through a NESTED record pattern can +// reach emission with its produced identity erased; the turbofish then rendered the erasure itself: +// `Witness::::Holds`. The enclosing fn's declared return +// is the carrier authority and must be what the arm falls back to. +test fn w_witness_carrier_falls_back_to_the_fn_return_when_the_value_identity_is_erased() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.witness_carrier_probe\nimport std.types { Int }\nimport std.algebra { Cons, Empty }\nimport v2.std.algebra { fold_list, list_snoc_item }\nimport v2.std.witness { Holds, Violates, Witness }\nimport v2.std.diagnostic { Diagnostic }\ntype Progress { edges: List, index: Int }\nfn step(p: Witness, x: Int) -> Witness { match p { Holds { value: Progress { edges: es, index: i } } => Holds { value: Progress { edges: list_snoc_item(xs: es, item: x), index: i + 1 } } Violates { diagnostic: d } => Violates { diagnostic: d } } }\nfn probe(xs: List) -> Witness> { match fold_list(xs: xs, empty: Holds { value: Progress { edges: Empty, index: 0 } }, cons: fn(p, x) { step(p: p, x: x) }) { Holds { value: Progress { edges: result, index: _ } } => Holds { value: result } Violates { diagnostic: d } => Violates { diagnostic: d } } }\n", + "src/test_claim_witness_carrier_probe.rs", + ["Witness::<"], + ["UNRESOLVED_CompilerError"] + ) +} + +// FOLD ACCUMULATOR, THE DISCRIMINATING RED. A fold whose init is empty_map() took the Map +// declaration's own formals as its accumulator and wrote them into the turbofish: +// `v1_rt::rc_empty_map::()`. THE PROBE IMPORTS ONLY `Symbol` (the harness pool is the probe's +// import closure, so an unimported `Symbol` refuses there while the whole corpus binds it): it mirrors +// v2.lens.unit_modeling, which resolves Map, Symbol, empty_map and map_insert by namespace alone -- +// under an explicit `import std.types { Map }` the accumulator resolves to the kernel container and +// the defect never fires (measured: that probe emitted `rc_empty_map::` on the +// defective compiler). The init must decline the formals exactly as the lambda beside it does. +test fn w_fold_empty_map_init_renders_the_resolved_accumulator_not_the_declaration_formals() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.fold_accumulator_probe\nimport v2.std.node { Symbol }\n\ntype Row {\n field: Symbol\n carrier: Symbol\n}\n\ndata rows: List = [\n Row { field: ^a, carrier: ^b }\n]\n\nfn probe() -> Map {\n fold(rows, init: empty_map(), f: fn(acc, row) {\n map_insert(m: acc, key: row.field, value: row.carrier)\n })\n}\n", + "src/test_claim_fold_accumulator_probe.rs", + ["rc_empty_map::<"], + ["rc_empty_map::"] + ) +} + +// LAMBDA PARAMETER ANNOTATION, THE DISCRIMINATING RED. A closure parameter whose inferred type +// carries a formal no fn generic binds (`FreeMonoid`) was annotated with it verbatim, a type the +// closure cannot name. The annotation must decline exactly as the fold element annotation does. +test fn w_lambda_param_annotation_declines_a_spurious_generic() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.lambda_annotation_probe\nimport std.types { Bool }\nimport std.algebra { Cons, Empty }\nimport v2.std.node { Symbol }\nfn has(xs: List, s: Symbol) -> Bool { match xs { Empty => false Cons { head: h, tail: t } => if h == s { true } else { has(xs: t, s: s) } } }\nfn probe(hits: List, accepted: List) -> Bool { match hits { Empty => false Cons { head: first_hit, tail: rest_hits } => fold(Cons { head: first_hit, tail: rest_hits }, init: true, f: fn(acc, a) { acc && has(xs: accepted, s: a) }) } }\n", + "src/test_claim_lambda_annotation_probe.rs", + ["|acc: bool"], + ["FreeMonoid>|"] + ) +} + +// ZERO-PARAMETER ALIAS, THE DISCRIMINATING RED. An alias over a parameterized type, referenced as +// the return of an arrow type, rendered with the arguments of what it abbreviates: +// `Gate = Rc Outc>>>` (E0107, with the E0391 variance cycle rustc +// reports beside it). The alias is zero-parameter and must render bare. +test fn w_zero_param_alias_in_an_arrow_return_renders_without_its_rhs_arguments() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.alias_arrow_probe\nimport std.types { Int }\nimport v2.std.witness { Witness }\nimport v2.std.diagnostic { Outcome }\nimport v2.std.node { Node }\ntype Outc = Outcome>\ntype Gate = fn(Int) -> Outc\ntype Lens { gate: Gate }\n", + "src/test_claim_alias_arrow_probe.rs", + ["-> Outc>"], + ["Outc<"] + ) +} + +// ARROW RETURN BINDING, THE DISCRIMINATING RED. An arrow type's return rendered through the +// target-generic renderer, which never reads an applied-type binding, so `fn(Int) -> Outcome>` +// dropped the inner binding: `Rc>>` (E0107 missing generics). The Rust renderer +// honours the binding and must be what an arrow's positions render through. +test fn w_arrow_return_type_keeps_its_applied_binding() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.arrow_binding_probe\nimport std.types { Int, String }\nimport v2.std.diagnostic { Outcome, Accepted }\ntype Box2 { v: T }\nfn probe() -> fn(Int) -> Outcome> { fn(n) { Accepted { value: Box2 { v: \"x\" }, diagnostics: None } } }\n", + "src/test_claim_arrow_binding_probe.rs", + ["Box2"], + ["Rc>"] + ) +} + +// QUALIFIED TYPE REFERENCE, POSITIVE CONTROL ONLY. A field typed by a dotted, unimported provider +// name renders as its bare leaf and must earn the leaf's use-line from the provider the author +// named. The defect that opened this class (v2.std.determinism, E0425 x2 on the whole closure) +// fires only when the pool also carries a coproduct with a VARIANT spelled `Determinism` +// (v2.lens.registry LensIdV0): the bare-name disposition classified the type-position leaf as that +// variant. This harness cannot carry that specimen at all -- its pool is the probe's DECLARED +// import closure, so a provider referenced only by a dotted name is absent (hard diagnostic), and +// std.determinism itself references std.perturbation the same way -- so this row uses std.decl_ref +// with a sibling import to bring the provider into the pool, and is satisfied by the unfixed +// emitter too. The class's discriminating red is +// a_known_variant_spelling_in_a_type_position_takes_the_registry_arm in +// src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag (the disposition at +// function grain, red by construction on the variant arm) together with the closure count of the +// use-line in v2_std_determinism.rs. The expected line is spelled as two substrings because a +// `{Name}` inside a .dag string literal is read as an interpolation of that name. +test fn w_qualified_type_reference_earns_the_leaf_use_line() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.qualified_type_ref_probe\nimport std.types { String }\nimport std.decl_ref { DeclField }\ntype Fact { signature: String, r: std.decl_ref.DeclarationRef }\nfn probe(f: Fact) -> std.decl_ref.DeclarationRef { f.r }\n", + "src/test_claim_qualified_type_ref_probe.rs", + ["use crate::std_decl_ref::{", "DeclarationRef};"], + ["std.decl_ref.DeclarationRef"] + ) +} + +// QUALIFIED VALUE HEAD, THE OTHER DIRECTION. `v2.std.nat.Succ { prev: .. }` is a variant record +// literal; the value route imports it through its parent enum, and this route must synthesize +// nothing for it (E0603 `variant import is private`, sixteen sites on the first regeneration). +test fn w_qualified_variant_head_earns_no_type_use_line() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.qualified_variant_head_probe\nimport v2.std.nat { Nat, Zero }\nfn probe() -> Nat { v2.std.nat.Succ { prev: Zero } }\n", + "src/test_claim_qualified_variant_head_probe.rs", + ["Succ"], + ["use crate::v2_std_nat::Succ;"] + ) +} + +// GENERIC ARROW RETURN. A fn-scope generic inside an arrow return position rendered by its erased +// inference -- `Rc>` (v2.std.staging +// then_outcome, three sites) -- while the same shape in a parameter position rendered `C`. Both +// positions now ask the fn-signature renderer. +test fn w_generic_arrow_return_renders_the_fn_scope_generic() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.generic_arrow_probe\nimport v2.std.diagnostic { Outcome, Accepted, Rejected }\nfn then2(f: fn(A) -> Outcome, g: fn(B) -> Outcome) -> fn(A) -> Outcome {\n fn(a) { match f(a) { Accepted { value: b, diagnostics: _ } => g(b) Rejected { diagnostics: d } => Rejected { diagnostics: d } } }\n}\n", + "src/test_claim_generic_arrow_probe.rs", + ["-> Rc Rc>>"], + ["UNRESOLVED_"] + ) +} + +// SERVICE OP WITHOUT A REALIZATION, THE DISCRIMINATING RED. `Filesystem.Read(path:)` inside a fn +// the signature authority judged service-free emitted `filesystem.read(path).await?` -- an unbound +// name in a non-async fn, the only `.await?` in the whole closure. It is a host seam with no +// realization in this target and must emit the generated refusal body naming the seam. The realized +// arm has no positive control here because no source in the corpus spells a receiver the signature +// authority accepts; that absence is recorded in rust_unrealized_service_op_refusal's note. +test fn w_service_op_in_a_service_free_fn_refuses_instead_of_awaiting_an_unbound_binding() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.service_op_probe\nimport std.types { String }\nimport extdeps.filesystem.filesystem_io { Filesystem }\nfn probe(path: String) -> String { let read = Filesystem.Read(path: path)\n read.content }\n", + "src/test_claim_service_op_probe.rs", + ["unrealized host seam: service op Filesystem.Read"], + [".await?"] + ) +} diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 2c3ba31126e..9aaa6dfe0d7 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -107,8 +107,46 @@ type VarBindingKind // where the declaration and the primitive genuinely disagree and the declaration is what was // called. CallableTargetUndetermined is a refusal-bearing residue -- it is not permission for // emission to fall back to a name lookup, and emission refuses on it. +// THE DECLARED HALF OF AN IDENTITY, HOISTED SO THE RESOLVED ARM CAN CARRY IT WITHOUT +// CARRYING THE BUILTIN ARM. +// +// `decide_callable_candidates` proves a builtin never answers -- it can make a population +// ambiguous and on its own hands the call back to the known-builtin bridge -- so the owner a +// resolution binds is ALWAYS a declared one. Spelling FuncSigResolved's owner as +// CallableIdentity would therefore admit a state the decision already ruled out, and spelling +// it as a loose owner/decl String pair beside DeclaredCallable would be a second representation +// of the pair v1.compiler.sigs already owns (DESIGN section 3). One record, two consumers. +// +// It is declared HERE rather than in v1.compiler.sigs, its former home, because +// CallTargetIdentity below carries one and this module sits under that one. Re-spelling the pair +// in either direction to avoid the import would fork it. +type DeclaredCallableIdentity { + owner_module_path: String + decl_name: String +} + +// A PRIMITIVE'S IDENTITY AND ITS PER-TARGET REALIZATION ARE TWO FACTS, AND `projected_from` +// IS WHAT KEEPS THEM APART. +// +// The resolver knows both halves at once: that this call resolved to declaration D, and that +// std.primitive_projection says D projects primitive P. It used to record only P, and every +// emitter then had to assume P has a bridge in ITS runtime. That assumption is false and was +// fabricating: v2.std.algebra length is a ModeledProjection of the `length` primitive, the Rust +// registry has no `length` row (its list realization is `count`, its string realization is +// `string_length`), and 05_emit_rust rendered `v1_rt::length` -- a symbol that does not exist in +// the seed -- at 73 sites. That is DESIGN section 5 fabricated-plausible-output: the emitter +// produced a well-formed call to nothing and only rustc refused, downstream of every gate we own. +// +// Which realization a target has is a REALIZATION fact and belongs peripheral to the target +// (DESIGN section 3: the dispatch that selects a realization is itself realization), so the +// resolver states only what it knows and carries the declaration alongside. An emitter routes to +// the bridge when its own registry has the primitive, falls back to the declaration when one was +// projected, and REFUSES otherwise -- it never invents a runtime symbol from a name. +// +// `projected_from` is Absent exactly when the target came from builtin resolution, where no +// declaration answered and there is nothing to fall back to. type CallTargetIdentity - = RuntimePrimitiveCall { primitive_name: String } + = RuntimePrimitiveCall { primitive_name: String, projected_from: DeclaredCallableIdentity? } | SourceDeclarationCall { owner_module_path: String, decl_name: String } | CallableTargetUndetermined diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index c7cde55b906..a7da42b7336 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -5127,7 +5127,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu if node_is_keyed_collection(n: exp, source_indices: scope.type_env.source_indices) { InferResult { typed: make_named_expr_node(occurrence_identity: texpr.occurrence_identity, - name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map" } } }, descent_evidence: none }, + name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map", projected_from: none } } }, descent_evidence: none }, children: typed_arg_nodes, inferred: Present { value: Resolved { node: exp } }, span: span, @@ -5141,7 +5141,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu Present { value: map_t } => InferResult { typed: make_named_expr_node(occurrence_identity: texpr.occurrence_identity, - name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map" } } }, descent_evidence: none }, + name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map", projected_from: none } } }, descent_evidence: none }, children: typed_arg_nodes, inferred: Present { value: Resolved { node: map_t } }, span: span, @@ -5152,7 +5152,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu Absent => InferResult { typed: make_named_expr_node(occurrence_identity: texpr.occurrence_identity, - name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map" } } }, descent_evidence: none }, + name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map", projected_from: none } } }, descent_evidence: none }, children: typed_arg_nodes, inferred: Present { value: CompilerError { message: "empty_map(): Map kernel container profile missing (compiler misconfigured)", span: span } }, span: span, @@ -5167,7 +5167,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu Present { value: map_t } => InferResult { typed: make_named_expr_node(occurrence_identity: texpr.occurrence_identity, - name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map" } } }, descent_evidence: none }, + name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map", projected_from: none } } }, descent_evidence: none }, children: typed_arg_nodes, inferred: Present { value: Resolved { node: map_t } }, span: span, @@ -5178,7 +5178,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu Absent => InferResult { typed: make_named_expr_node(occurrence_identity: texpr.occurrence_identity, - name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map" } } }, descent_evidence: none }, + name: func_name, expr_data: ExprCall { call_semantics: Present { value: PlainCallSemantics { target: RuntimePrimitiveCall { primitive_name: "empty_map", projected_from: none } } }, descent_evidence: none }, children: typed_arg_nodes, inferred: Present { value: CompilerError { message: "empty_map(): Map kernel container profile missing (compiler misconfigured)", span: span } }, span: span, @@ -5260,7 +5260,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu let tier2b = infer_tier2b_builtin_with_kernel_diags(func_name: func_name, typed_args: typed_args, scope: scope, span: span) let bt = tier2b.bt let call_semantics = if func_name == "lookup" { - Present { value: LookupCallSemantics { target: RuntimePrimitiveCall { primitive_name: func_name } } } + Present { value: LookupCallSemantics { target: RuntimePrimitiveCall { primitive_name: func_name, projected_from: none } } } } else { Present { value: PlainCallSemantics { target: resolved_plain_call_target(func_name: func_name, sig_lookup: call_sig_lookup) } } } diff --git a/src/v1/04_lookup.dag b/src/v1/04_lookup.dag index 84f8406d71a..33ec77e2563 100644 --- a/src/v1/04_lookup.dag +++ b/src/v1/04_lookup.dag @@ -9,6 +9,7 @@ import std.primitive_projection { } import v1.std.core { + DeclaredCallableIdentity, Node, param_node_type_expr, authored_name_at, NewlineIndex, find_child_named, has_child_named, qualified_last_segment, @@ -58,7 +59,7 @@ import v1.compiler.infer_emit_info { import v1.compiler.infer_sigs { ResolvedFuncSig, ResolvedFuncEnv, lookup_resolved_sig, FuncSigLookup, FuncSigResolved, FuncSigUnresolved, FuncSigAmbiguous, - CallableCandidate, CallableIdentity, DeclaredCallableIdentity, DeclaredCallable, BuiltinCallable, + CallableCandidate, CallableIdentity, DeclaredCallable, BuiltinCallable, parent_closure_callable_candidates, decide_callable_candidates } @@ -231,6 +232,19 @@ fn builtin_is_a_rival_authority(declared: List) -> Bool { declared |> any(c => declared_candidate_rivals_the_builtin(candidate: c)) } +// A DECLARATION'S IDENTITY IS ITS LAST SEGMENT, NEVER THE SPELLING THE CALLER USED. +// +// The four DeclaredCallableIdentity constructions in this module took `decl_name` from the +// authored name. For a qualified call that name is the whole dotted path, so `v2.std.grammar.f(..)` +// resolved with owner `v2.std.grammar` AND decl_name `v2.std.grammar.f`, and 05_emit_rust rendered +// `crate::v2_std_grammar::v2.std.grammar.f(..)` -- 102 E0425s in the v2 compiler closure. The same +// pair also keys `lookup_item` for default-argument filling and `decl_ref` for the primitive +// projection roster, so both silently missed on every qualified call. +// +// `qualified_last_segment` is the idiom `constructor_declaration_for_admission` already uses for +// exactly this, and it is the identity on a bare name -- so it is applied at all four sites rather +// than at the one that was measured failing, which removes the class instead of an instance. +// // TARGET SELECTION, DECIDED HERE AND CONSUMED VERBATIM BY EMISSION. // // A call's target is chosen once, from the declaration resolution actually bound, and 05_emit @@ -243,14 +257,23 @@ fn builtin_is_a_rival_authority(declared: List) -> Bool { // The runtime NAME comes from the roster row's primitive identity, never from the call's leaf // spelling. That is the whole repair: a bare `contains` resolving to a v2 declaration no longer // reaches the bridge merely because `contains` is also a v1_rt name. +// +// ONLY ModeledProjection CARRIES `projected_from`, AND THE FIDELITY DISTINCTION IS WHY. +// `projected_from` is what an emitter falls back to when its own runtime has no realization for +// the primitive, so it is only sound where the declaration's body is real code -- which is +// precisely what ModeledProjection means. A HostRealizedSeam body is a SELF-CALL by construction +// (decl_facts is `decl_facts(pool_roots: pool_roots)`), so emitting it would produce a +// well-formed, compiling, NONTERMINATING function: silent wrongness, strictly worse than the +// unresolved-symbol refusal it would replace. A seam with no realization in the target has no +// honest lowering at all, so it carries nothing and reaches emission's refusal arm. fn resolved_declaration_call_target(declared: DeclaredCallableIdentity) -> CallTargetIdentity { match primitive_projection_row_for_declaration( declaration: decl_ref(module_path: declared.owner_module_path, decl_name: declared.decl_name) ) { Present { value: row } => match row.fidelity { - HostRealizedSeam => RuntimePrimitiveCall { primitive_name: primitive_identity_runtime_name(identity: row.primitive) } - ModeledProjection => RuntimePrimitiveCall { primitive_name: primitive_identity_runtime_name(identity: row.primitive) } + HostRealizedSeam => RuntimePrimitiveCall { primitive_name: primitive_identity_runtime_name(identity: row.primitive), projected_from: none } + ModeledProjection => RuntimePrimitiveCall { primitive_name: primitive_identity_runtime_name(identity: row.primitive), projected_from: Present { value: declared } } DivergentProjection { divergence: _ } => SourceDeclarationCall { owner_module_path: declared.owner_module_path, decl_name: declared.decl_name } } @@ -274,7 +297,7 @@ fn resolved_plain_call_target(func_name: String, sig_lookup: FuncSigLookup) -> C fn builtin_call_target_or_undetermined(func_name: String) -> CallTargetIdentity { match infer_builtin_call_type(name: func_name) { - Present { value: _ } => RuntimePrimitiveCall { primitive_name: func_name } + Present { value: _ } => RuntimePrimitiveCall { primitive_name: func_name, projected_from: none } Absent => CallableTargetUndetermined } } @@ -347,7 +370,7 @@ fn callable_lookup_over_candidates( ) -> FuncSigLookup { match map_get(func_env.local, name) { Present { value: sig } => - FuncSigResolved { sig: sig, declared: DeclaredCallableIdentity { owner_module_path: func_env.name, decl_name: name } } + FuncSigResolved { sig: sig, declared: DeclaredCallableIdentity { owner_module_path: func_env.name, decl_name: qualified_last_segment(name: name) } } Absent => let declared = parent_closure_callable_candidates(env: func_env, name: name) let builtin_admissible = match author_named_visibility(type_env: type_env, name: name) { @@ -561,7 +584,7 @@ fn borrowed_census_callable_candidate(type_env: TypeEnv, name: String, candidate excluded: excluded ) CallableCandidate { - identity: DeclaredCallable { identity: DeclaredCallableIdentity { owner_module_path: candidate.module_path, decl_name: name } }, + identity: DeclaredCallable { identity: DeclaredCallableIdentity { owner_module_path: candidate.module_path, decl_name: qualified_last_segment(name: name) } }, sig: ResolvedFuncSig { name: name, params: node.params, @@ -606,7 +629,7 @@ fn func_sig_from_global_bare(type_env: TypeEnv, name: String) -> FuncSigLookup { is_async: false, output_provenance: [], variant_provenance: empty_map() - }, declared: DeclaredCallableIdentity { owner_module_path: bd.owner_module_path, decl_name: name } } + }, declared: DeclaredCallableIdentity { owner_module_path: bd.owner_module_path, decl_name: qualified_last_segment(name: name) } } } else { let qualified_return = qualify_borrowed_type_names(n: raw_return, owner_module_path: bd.owner_module_path, env: type_env, excluded: excluded) FuncSigResolved { sig: ResolvedFuncSig { @@ -616,7 +639,7 @@ fn func_sig_from_global_bare(type_env: TypeEnv, name: String) -> FuncSigLookup { is_async: false, output_provenance: [], variant_provenance: empty_map() - }, declared: DeclaredCallableIdentity { owner_module_path: bd.owner_module_path, decl_name: name } } + }, declared: DeclaredCallableIdentity { owner_module_path: bd.owner_module_path, decl_name: qualified_last_segment(name: name) } } } } BorrowedCensusDeclAmbiguous { candidates: cands } => diff --git a/src/v1/04_sigs.dag b/src/v1/04_sigs.dag index 3199c92bc3e..cfb236e596a 100644 --- a/src/v1/04_sigs.dag +++ b/src/v1/04_sigs.dag @@ -6,6 +6,7 @@ import v1.std.core { ErrorNode, make_error_node, MissingAnnotation, no_span, DeclaredFuncSig, + DeclaredCallableIdentity, NewlineIndex, authored_name_at } @@ -110,19 +111,10 @@ type ParentSigScan { // the collision this vocabulary exists to describe (gap-analysis row 30 -- `map_get` is both // `v2.std.collection.map_get` returning Outcome> and a builtin returning // Optional). So the candidate is the identity, and the label is a projection of it. -// THE DECLARED HALF OF AN IDENTITY, HOISTED SO THE RESOLVED ARM CAN CARRY IT WITHOUT -// CARRYING THE BUILTIN ARM. -// -// `decide_callable_candidates` proves a builtin never answers -- it can make a population -// ambiguous and on its own hands the call back to the known-builtin bridge -- so the owner a -// resolution binds is ALWAYS a declared one. Spelling FuncSigResolved's owner as -// CallableIdentity would therefore admit a state the decision already ruled out, and spelling -// it as a loose owner/decl String pair beside DeclaredCallable would be a second representation -// of the pair this file already owns (DESIGN section 3). One record, two consumers. -type DeclaredCallableIdentity { - owner_module_path: String - decl_name: String -} +// DeclaredCallableIdentity IS NOT DECLARED HERE ANY MORE. It moved to v1.std.core because +// CallTargetIdentity -- which lives there, below this module -- now carries one, and a second +// spelling of the pair in this file to break the import direction would be the nicknaming defect +// DESIGN section 3 names. The reasoning for the record itself travels with the declaration. type CallableIdentity = DeclaredCallable { identity: DeclaredCallableIdentity } diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 2065be7f5f3..6705b8f3d8f 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -1,6 +1,10 @@ module v1.compiler.emit_rust import std.occurrence_identity { OccurrenceSynthetic } +import std.decl_ref { decl_ref } +import std.primitive_projection { + primitive_identity_runtime_name, primitive_projection_row_for_declaration +} import extdeps.languages.rust.capabilities { phantom_opaque_carrier_derive_traits } @@ -18,7 +22,7 @@ import v1.std.core { ExprCast, ExprForEach, ExprIndex, ExprSlice, ExprReturn, make_expr_node, FieldSummary, StoredField, EnumAccessor, OptionalUnwrap, TupleFirst, TupleSecond, OptionalValue, VarBindingKind, LocalValueBinding, FunctionValueBinding, VariantValueBinding, - MatchBoundBinding, CallTargetIdentity, RuntimePrimitiveCall, SourceDeclarationCall, CallableTargetUndetermined, + MatchBoundBinding, CallTargetIdentity, DeclaredCallableIdentity, RuntimePrimitiveCall, SourceDeclarationCall, CallableTargetUndetermined, CallSemantics, PlainCallSemantics, LookupCallSemantics, FunctionValueCallSemantics, MethodSemantics, PlainMethodSemantics, AlgebraMethodSemantics, ServiceMethodSemantics, MatchPattern, field_binding_pattern, TextFile, ErrorNode, make_error_node, InternalError, @@ -37,7 +41,7 @@ import v1.std.core { match_scrutinee, match_arm_nodes, if_condition, if_then_branch, if_else_branch, index_base, index_expr, slice_base, slice_start, slice_end, cast_target, cast_expr, return_value, binop_left, binop_right, no_span, with_required_cardinality, Connective, Conj, Disj, - NoConnective, Cardinality, Required, CardOptional, is_rest_transport, + NoConnective, Arrow, Cardinality, Required, CardOptional, is_rest_transport, transport_base_path, empty_intern_table } @@ -84,7 +88,7 @@ import v1.compiler.closure_stub_v2_std_integer_rust { closure_stub_v2_std_intege import v1.compiler.closure_stub_v2_std_text_rust { closure_stub_v2_std_text_source } import v1.compiler.coercion { coerce_primitive_type, is_copy, lookup_checkpoint, type_reference_decl_file, - rust_seed_host_numeric_alias + rust_seed_host_numeric_alias, target_callable } import v1.compiler.trait_derive_emit { v1_emit_enum_derives, @@ -222,6 +226,57 @@ fn render_rust_type(n: Node, shared_types: Set, source_indices: Map Outcome>` came out as +// `Rc Rc>>>` (E0107, v2.compiler.compile stage_translate). +// Two earlier cuts each introduced a SECOND per-position renderer beside the parameter side's: +// routing every position through render_rust_type rendered a fn-scope generic by its erased +// inference (`Outcome`, v2.std.staging then_outcome), +// and a gate that admitted only applied-binding subtrees kept that defect for exactly those +// subtrees while re-opening `Rc` for the rest. The parameter side of the same arrow -- +// emit_rust_param_type's `impl Fn(A) -> Rc>` -- had rendered both shapes correctly all +// along, through render_rust_fn_sig_type in the fn's generic scope. An arrow's return is not a +// different kind of type from an arrow's parameter, so both positions ask that renderer, and the +// defect set is whatever that renderer gets wrong, which the parameter side already measures. +// The callable template and the void arm are the same rows render_node_type consumes. +fn render_rust_arrow_position(n: Node, shared_types: Set, source_indices: Map, emit_info: EmitGraphInfo) -> String { + render_rust_fn_sig_type(n: n, generic_param_names: emit_info.fn_generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: emit_info.variant_to_enum, env: emit_info.fn_type_env) +} + +// Named rather than inlined because `if x == Variant {` parses as a record literal in .dag. +fn rust_type_node_is_arrow(n: Node) -> Bool { + n.connective == Arrow +} + +fn render_rust_arrow_type(n: Node, shared_types: Set, source_indices: Map, emit_info: EmitGraphInfo) -> String { + let repr = target_callable(target: Rust) + let param_strs = n.params |> map(p => render_rust_arrow_position(n: param_node_type_expr(n: p), shared_types: shared_types, source_indices: source_indices, emit_info: emit_info)) + let param_str = param_strs |> join(separator: repr.param_separator) + let spec = language_spec(target: Rust) + let ret_str = match n.inferred { + Present { value: Resolved { node: rt } } => render_rust_arrow_position(n: rt, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) + _ => spec.void_type + } + match spec.callable_type_template { + Present { value: t } => replace(replace(t, "\{params\}", param_str), "\{return\}", ret_str) + Absent => + if ret_str == "" { + replace(replace(repr.template, concat(repr.return_separator, "\{return\}"), ""), "\{params\}", param_str) + } else { + replace(replace(repr.template, "\{params\}", param_str), "\{return\}", ret_str) + } + } +} + +// ZERO-PARAMETER ALIAS AT THE EXPRESSION-TYPE POSITION (the closed_alias_peels_zero_param arm below): +// A ZERO-PARAMETER ALIAS NEVER TAKES THE ARGUMENTS OF WHAT IT ABBREVIATES. The resolver hands an +// alias reference back carrying its right-hand side's children, and rendering name-plus-children +// wrote `CompileLensOutcome>>>` for `type CompileLensOutcome = +// Outcome>` (E0107, and the E0391 variance cycle rustc reports beside it). The +// fn-signature renderer already answers this position bare via closed_alias_peels_zero_param; +// this is the same verdict asked at the expression-type position. fn render_rust_type_without_applied_binding(n: Node, shared_types: Set, source_indices: Map, emit_info: EmitGraphInfo) -> String { if is_host_text_carrier_type(n: n, source_indices: source_indices) { return rust_carrier_optional_wrap(n: n, rendered: "String") } if is_host_optional_carrier_type(n: n, source_indices: source_indices) { @@ -230,7 +285,12 @@ fn render_rust_type_without_applied_binding(n: Node, shared_types: Set, if is_host_diagnostics_carrier_type(n: n, source_indices: source_indices) { return render_rust_diagnostics_carrier_applied(shared_types: shared_types) } - if n.connective == NoConnective && n.children |> count > 0 && !is_container_type(name: authored_name_at(source_indices: source_indices, node: n)) { + if rust_type_node_is_arrow(n: n) { + render_rust_arrow_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) + } else if n.connective == NoConnective && n.children |> count > 0 && !is_container_type(name: authored_name_at(source_indices: source_indices, node: n)) && closed_alias_peels_zero_param(env: emit_info.fn_type_env, n: n) { + let leaf = qualified_last_segment(name: authored_name_at(source_indices: source_indices, node: n)) + render_rust_shared_type_with_optional(n: n, type_name: leaf, rendered: leaf, shared_types: shared_types) + } else if n.connective == NoConnective && n.children |> count > 0 && !is_container_type(name: authored_name_at(source_indices: source_indices, node: n)) { render_rust_applied_via_emit_info(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) } else if node_is_set_collection(n: n, source_indices: source_indices) { match n.children |> first { @@ -678,7 +738,18 @@ fn rust_witness_applied_type_arg_node(type_node: Node, source_indices: Map::Holds { .. }`, one site on the +// 00_compile closure (v2.std.grammar derive_grammar_relation_token_edges_recursive). The fn's return +// type `Witness>` was present the whole time and is the authority for the arm's carrier; +// an erased first source must DECLINE so the second is consulted, never render its own refusal. fn rust_witness_carrier_from_type_node(type_node: Node, shared_types: Set, emit_info: EmitGraphInfo, source_indices: Map) -> String? { + if type_node.inferred != none && is_compiler_error(inferred: type_node.inferred.value) { return none } if type_node_has_unbound_type_variable(n: type_node, generic_param_names: emit_info.fn_generic_param_names, source_indices: source_indices) { return none } let peeled = rust_peel_all_rc_type_node(type_node: type_node, source_indices: source_indices) if qualified_last_segment(name: peeled.name) == "Witness" { @@ -1197,7 +1268,7 @@ fn render_rust_decl_type(n: Node, generic_param_names: List, shared_type } else if type_node_has_unbound_type_variable(n: n, generic_param_names: generic_param_names, source_indices: source_indices) { "_" } else { - render_rust_type_with_applied_binding(n: n, shared_types: shared_types, source_indices: source_indices, env: env) + render_rust_type_with_applied_binding_in_scope(n: n, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, env: env) } } } @@ -1330,6 +1401,10 @@ Fix: route on the TERMINAL segment via qualified_last_segment (v1.std.core, the The v2 corpus authors qualified names in type position only rarely today, which is why one line carried this much weight; the point of fixing the renderer rather than editing that one declaration is that the next qualified name must not reintroduce it (DESIGN: do not cement the seed's accidents)." +// THE FINAL else ARM CARRIES THE MODULE ENV: +// The env travels: an alias right-hand side that is an Arrow renders its return through +// render_rust_type, and the zero-param-alias verdict there is asked of emit_info.fn_type_env. An +// empty env answered ClosedAliasBindingAbsent for every alias and the arm could never fire. fn render_rust_alias_rhs_type(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, scope: InferScope, imports: List, registry: Map, module_name: String, export_sets: Map>, typed_modules: List, module_index: ModuleIndex, variant_to_enum: Map) -> String { if is_where_refinement_type(ty: n) { match n.children |> first { @@ -1421,7 +1496,7 @@ fn render_rust_alias_rhs_type(n: Node, generic_param_names: List, shared } } } else { - render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: empty_emit_graph_info()) + render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info_with_fn_type_context(emit_info: empty_emit_graph_info(), generic_param_names: generic_param_names, env: scope.type_env)) } } @@ -3767,6 +3842,12 @@ fn local_coproduct_variant_names(items: List, source_indices: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex, type_summaries: Map, variant_to_enum: Map) -> ReferenceDerivedCandidateDisposition { - if is_known_variant(type_summaries: type_summaries, name: name) { +fn reference_derived_candidate_disposition(name: String, this_module_name: String, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex, type_summaries: Map, variant_to_enum: Map, in_type_position: Bool) -> ReferenceDerivedCandidateDisposition { + if is_known_variant(type_summaries: type_summaries, name: name) && !in_type_position { match map_get(variant_to_enum, name) { Present { value: parent } => if parent == "" { CandidateVariantParentUnresolved } @@ -4091,6 +4172,7 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< let value_names = unique_strings(items: items |> flat_map(item => collect_value_ref_names(n: item, source_indices: source_indices, type_summaries: emit_info.type_summaries, variant_to_enum: emit_info.variant_to_enum))) let item_emit_surface_names = items |> flat_map(item => collect_item_emit_surface_names(item: item, source_indices: source_indices, emit_info: emit_info)) let type_surface_names = unique_strings(items: item_emit_surface_names) + let type_position_set = type_surface_names |> fold(init: empty_map(), f: (acc, nm) => map_insert(acc, nm, true)) let field_surface_names = collect_items_field_import_surface_names(surface_names: item_emit_surface_names, emit_info: emit_info) let variant_payload_structs = expand_variant_payload_struct_imports(names: type_surface_names, type_summaries: emit_info.type_summaries, variant_to_enum: emit_info.variant_to_enum) let emitter_attested_anon_heads = unique_strings(items: items |> flat_map(item => collect_anonymous_record_lit_heads(n: item, source_indices: source_indices, type_summaries: emit_info.type_summaries))) @@ -4119,7 +4201,7 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, - disposition: reference_derived_candidate_disposition(name: name, this_module_name: this_module_name, registry: registry, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index, type_summaries: emit_info.type_summaries, variant_to_enum: emit_info.variant_to_enum) + disposition: reference_derived_candidate_disposition(name: name, this_module_name: this_module_name, registry: registry, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index, type_summaries: emit_info.type_summaries, variant_to_enum: emit_info.variant_to_enum, in_type_position: emit_map_has(m: type_position_set, key: name)) }] } ) @@ -4143,7 +4225,7 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< let block_lines = if block == "" { [] } else { block |> split(delimiter: "\n") } let emitted_here = block_lines |> flat_map(l => imported_names_in_use_line(line: l)) let fallback = names |> flat_map(nm => - if (emitted_here |> any(e => e == nm)) || is_known_variant(type_summaries: emit_info.type_summaries, name: nm) { + if (emitted_here |> any(e => e == nm)) || (is_known_variant(type_summaries: emit_info.type_summaries, name: nm) && !emit_map_has(m: type_position_set, key: nm)) { [] } else if provider_proven_exports_symbol(name: nm, provider_module: provider, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { [concat(rust_visibility_prefix(), concat("use crate::", concat(module_to_filename(name: provider), concat("::", concat(emit_import_name(n: nm, registry: registry), ";")))))] @@ -4153,8 +4235,74 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< ) concat(block_lines, fallback) ) - ReferenceDerivedUseLinePlan { lines: lines, rows: rows } - } + let names_already_in_lines = lines |> flat_map(l => imported_names_in_use_line(line: l)) + let qualified_rows = qualified_type_reference_rows(names: type_surface_names |> filter(nm => emit_map_has(m: emitted_source_token_set, key: qualified_last_segment(name: nm))), this_module_name: this_module_name, already: already, names_already_in_lines: names_already_in_lines, registry: registry, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) + let qualified_lines = qualified_type_reference_use_lines(rows: qualified_rows, registry: registry) + ReferenceDerivedUseLinePlan { lines: concat(lines, qualified_lines), rows: concat(rows, qualified_rows) } + } +} + +// A QUALIFIED TYPE REFERENCE REACHES EMISSION AS ITS LEAF AND NEEDS THE LEAF'S USE-LINE. The type +// renderers spell `std.determinism.Determinism` as bare `Determinism` (rust_fn_sig_leaf_name). A +// reference the resolver binds reaches the surface walk as that leaf and is decided by the +// bare-name route above; a reference the resolver leaves authored reaches it in its DOTTED spelling +// -- never an identifier token in the emitted source, so the candidate filter dropped it before any +// disposition was assigned. This route decides the dotted ones. (The v2.std.determinism E0425 that +// motivated it was the OTHER case, measured by tracing this route's inputs: the leaf arrived bare +// and the bare-name route classified it as the v2.lens.registry VARIANT of the same spelling; that +// is the type-position exemption in reference_derived_candidate_disposition, not this route.) The qualifier IS the provider the author named, so the line is synthesized from it +// directly -- under the same construction wall every other synthesized use-line passes: +// provider_proven_exports_symbol must hold for the leaf on that module, or nothing is emitted and +// the name stays a loud unresolved symbol. A first cut keyed this on the qualified item-registry +// overlay instead; that overlay carries no row for the reference and the cut was a measured +// NON-EVENT (falsifier m12 reddened nothing; the two sites stayed), so it is replaced, not kept. +// THE SURFACE WALK ALSO COLLECTS QUALIFIED VALUE HEADS, AND THOSE ARE NOT THIS ROUTE'S. The first +// regeneration of this function synthesized `pub use crate::v2_std_nat::Succ;` for every +// `v2.std.nat.Succ { prev: .. }` record literal (E0603, sixteen sites): a variant reaches the surface +// walk in the same dotted spelling as a type, and Rust admits a variant only through its parent +// enum, which the value route already imports. So the leaf must be a TYPE the registry declares in +// the named qualifier before any line is synthesized; every other qualified name is the value +// route's, and this route says nothing about it. Each decision this route does take is a census row +// beside the bare-name rows -- the qualified spelling is the row's name, so the two never collide. +fn qualified_type_reference_rows(names: List, this_module_name: String, already: Map, names_already_in_lines: List, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { + let leaves_done = names_already_in_lines |> fold(init: empty_map(), f: (acc, nm) => map_insert(acc, nm, true)) + names |> flat_map(name => + if !string_contains(s: name, pattern: ".") { [] } else { + let leaf = qualified_last_segment(name: name) + let segments = name |> split(delimiter: ".") + let qualifier = segments |> take(n: (segments |> count) - 1) |> join(separator: ".") + if qualifier == this_module_name || emit_map_has(m: already, key: leaf) || emit_map_has(m: leaves_done, key: leaf) || is_kernel_type(name: leaf) { + [] + } else { + match map_get(registry, leaf) { + Absent => [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateRegistryAbsent }] + Present { value: info } => + if !(registry_row_is_type_declared_in(info: info, module_name: qualifier)) { + [] + } else if provider_proven_exports_symbol(name: leaf, provider_module: qualifier, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { + [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateSurvived { provider_module: qualifier } }] + } else { + [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateExportProofFailed { provider_module: qualifier } }] + } + } + } + } + ) +} + +// Named because `info.kind == TypeItem` is a comparison the .dag surface reads as a record literal. +fn registry_row_is_type_declared_in(info: ItemInfo, module_name: String) -> Bool { + if info.module_name != module_name { false } else { match info.kind { TypeItem => true _ => false } } +} + +fn qualified_type_reference_use_lines(rows: List, registry: Map) -> List { + unique_strings(items: rows |> flat_map(r => + match r.disposition { + CandidateSurvived { provider_module: provider } => + [concat(rust_visibility_prefix(), "use crate::", module_to_filename(name: provider), "::", emit_import_name(n: qualified_last_segment(name: r.name), registry: registry), ";")] + _ => [] + } + )) } fn reference_derived_use_lines(items: List, unlisted_type_names: List, this_module_name: String, registry: Map, emit_info: EmitGraphInfo, local_type_names: List, already_imported_names: List, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex, shared_types: Set, emitted_source: String) -> List { @@ -6452,7 +6600,16 @@ fn emit_struct_from_children(name: String, type_params: String, generic_param_na data render_rust_type_applied_binding_env_note: String = "Construction wall for the zero-parameter-alias applied-argument class (E0107 'type alias takes 0 generic arguments but N were supplied'). A reference to a closed alias arrives at the emitter carrying the RESOLVED DEFINITION's type arguments as its children while its authored spelling is still the alias name, so a renderer that prints name-plus-children emits ClosedCarrierAlias for a Rust alias declared with no parameters. Four renderers already refuse that shape by asking the single peel authority closed_alias_peel_verdict: render_rust_applied_type, render_rust_decl_type, render_rust_fn_sig_type and render_rust_alias_rhs_type. This function is the fifth path, and it did NOT lack the guard -- it reached render_rust_applied_type, which asked, and was answered with an EMPTY TypeEnv this function constructed inline plus an empty_emit_graph_info() it passed to render_rust_type. An empty env resolves no binding, and the peel predicate renders 'I could not look this up' identically to 'this declaration has parameters' -- the state-space conflation DESIGN section 5 names, failing open into the widened applied form rather than refusing. Every one of this function's callers already holds the TypeEnv it was discarding, so the fix is to stop discarding it: the peel authority stays the single authority and is simply answered truthfully. Measured on the src/v2/compiler/03_ingest.dag closure. The next rung is upstream and not taken here: an alias reference that preserved its AUTHORED arity would make the bad shape unwritable rather than merely refused, at which point no renderer would need to ask at all." +// THE FN'S GENERIC SCOPE MUST SURVIVE THIS HOP. render_rust_decl_type reaches here from a fn +// signature carrying generic_param_names, and the EmitGraphInfo rebuilt below used to carry an +// empty scope, so a fn-scope generic inside an arrow return (`fn(A) -> Outcome`) rendered as +// `_` (E0121) or by its erased inference; the parameter side of the same arrow rendered `C`. +// The scope-free spelling stays for the callers that have no fn scope (fields, data, wire rows). fn render_rust_type_with_applied_binding(n: Node, shared_types: Set, source_indices: Map, env: TypeEnv) -> String { + render_rust_type_with_applied_binding_in_scope(n: n, generic_param_names: [], shared_types: shared_types, source_indices: source_indices, env: env) +} + +fn render_rust_type_with_applied_binding_in_scope(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, env: TypeEnv) -> String { if is_host_text_carrier_type(n: n, source_indices: source_indices) { return rust_carrier_optional_wrap(n: n, rendered: "String") } match find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) { Present { value: applied } => @@ -6463,15 +6620,15 @@ fn render_rust_type_with_applied_binding(n: Node, shared_types: Set, sou if outer_name != applied_name { render_rust_shared_type_with_optional(n: n, type_name: outer_name, rendered: outer_name, shared_types: shared_types) } else { - render_rust_applied_type_shared(n: applied, generic_param_names: [], shared_types: shared_types, source_indices: source_indices, variant_to_enum: empty_map(), env: env) + render_rust_applied_type_shared(n: applied, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: empty_map(), env: env) } } else { - render_rust_applied_type_shared(n: applied, generic_param_names: [], shared_types: shared_types, source_indices: source_indices, variant_to_enum: empty_map(), env: env) + render_rust_applied_type_shared(n: applied, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: empty_map(), env: env) } } else { - render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info_with_fn_type_context(emit_info: empty_emit_graph_info(), generic_param_names: [], env: env)) + render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info_with_fn_type_context(emit_info: empty_emit_graph_info(), generic_param_names: generic_param_names, env: env)) } - Absent => render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info_with_fn_type_context(emit_info: empty_emit_graph_info(), generic_param_names: [], env: env)) + Absent => render_rust_type(n: n, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info_with_fn_type_context(emit_info: empty_emit_graph_info(), generic_param_names: generic_param_names, env: env)) } } @@ -7220,6 +7377,73 @@ fn v1_call_site_equality_forwarded_param_names( } } +// A WHOLE-BODY SELF-CALL IS THE STRUCTURAL MARKER OF A HOST SEAM, AND IT IS DECIDABLE. +// +// `fn decl_facts(pool_roots) { decl_facts(pool_roots: pool_roots) }` is not recursion that happens +// to be unbounded -- it is a declaration saying "my body lives in the host", and reaching it in the +// interpreter recurses to the evaluation-budget refusal, which is why the shape is safe there. In +// EMITTED RUST it is not safe: the same shape compiles, and the resulting function returns to no +// caller. That is DESIGN section 5 silent wrongness, and it is strictly worse than the unresolved +// symbol a missing bridge produces, because nothing anywhere reports it. The two symbol bridges sat +// in exactly this state and were absent from every error census precisely because they were silent. +// +// THE TEST IS THE WHOLE BODY, NOT A CONTAINED CALL. Ordinary recursion has a match, an if, or a let +// between the head and the self-call, so it never matches; expr_has_self_call, which walks children, +// is the wrong predicate here and would refuse most of the compiler. This asks only whether the body +// IS the self-call. +fn rust_body_is_bare_self_call(name: String, body: Node, source_indices: Map) -> Bool { + match body.expr_data { + ExprCall { call_semantics: _, descent_evidence: _ } => + qualified_last_segment(name: expr_call_func_at(texpr: body, source_indices: source_indices)) == name + _ => false + } +} + +// THE SEAM'S DISPOSITION AT THIS TARGET, ASKED OF THE REALIZATION REGISTRY RATHER THAN A NAME +// LITERAL. rust_host_string_seam_fn_emit above answers for two seams by spelling their Rust bodies +// out in the emitter, which is both hand-written target code in a compiler stage and a bare-name +// key. This asks the same registry the CALL SITES ask, so a seam's declaration is suppressed here +// exactly when a call to it lowers to a bridge, and the two answers cannot drift. +// +// THE REFUSAL IS A BODY, NOT A compile_error!, AND THE DENOMINATOR IS WHY. This wall first emitted +// compile_error! for every unrealized seam, which fails the WHOLE crate. That form assumes every +// seam it refuses is one somebody calls -- and it is not: rustc type-checks the entire emitted +// crate, including declarations imported but never invoked, so the refusal denominator is strictly +// larger than the entry-reachable execution closure. Five unreachable seams took the crate down. +// A panic body preserves the signature, compiles, and still fails loudly if anything actually calls +// it, which moves the refusal from the crate to the one declaration that earned it. Reachability is +// then a separate question answered by a separate instrument, and an entry-rooted pruner can +// replace the body later without revisiting this decision. +// +// THE REALIZED ARM EMITS NOTHING SPECIAL, AND THAT IS THE POINT. It first SUPPRESSED the +// declaration, on the reasoning that a realized primitive's calls all route to the bridge so the +// declaration has no honest caller. Measured, that was wrong twice over: other modules still +// `use crate::v2_std_compilers_lexing::symbol_lexeme`, so removing the item left 10 dangling +// imports (E0432) that the suppression itself created; and the reasoning that made suppression +// look safe is exactly the reasoning that makes it unnecessary. A realized seam's body IS a call +// to itself, and resolution already routes that call through the roster to the bridge -- so +// ordinary emission writes `v1_rt::symbol_lexeme(sym)` as the body with no help from here. +// Suppression was solving a problem resolution had already solved, and paying for it with the +// import surface. The wall's whole job is the UNREALIZED arm. +// THE PRIMITIVE A DECLARATION PROJECTS, WHICH IS NOT ITS OWN NAME. A seam's declaration name and +// the primitive it realizes are different facts, and keying realization on the declaration name +// was a defect measured rather than reasoned: v2.std.collection empty_map_primitive_delegate is +// rostered to the `empty_map` primitive, whose bridge is rc_empty_map, so it IS realized -- but the +// registry holds `empty_map`, not `empty_map_primitive_delegate`, so a decl-name lookup missed and +// the wall refused a seam that had a perfectly good realization. The roster is the authority that +// joins the two; the declaration name is only the fallback for a seam nobody has rostered. +fn rust_seam_primitive_name(module_path: String, name: String) -> String { + match primitive_projection_row_for_declaration(declaration: decl_ref(module_path: module_path, decl_name: name)) { + Present { value: row } => primitive_identity_runtime_name(identity: row.primitive) + Absent => name + } +} + +fn rust_host_seam_is_unrealized(module_path: String, name: String, body: Node, source_indices: Map) -> Bool { + rust_body_is_bare_self_call(name: name, body: body, source_indices: source_indices) + && rust_runtime_primitive_has_bridge(primitive_name: rust_seam_primitive_name(module_path: module_path, name: name)) == false +} + fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, registry: Map, scope: InferScope, shared_types: Set, emit_info: EmitGraphInfo) -> String { let si = scope.type_env.source_indices let host_text_op = rust_host_string_op_fn_emit(name: name) @@ -7307,6 +7531,15 @@ fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, reg let depth = 0 let use_tco = is_tco_eligible(name: name, body: body, registry: registry, source_indices: si) let needs_stacker = is_self_recursive(name: name, body: body, registry: registry, source_indices: si) && use_tco == false + if rust_host_seam_is_unrealized(module_path: scope.module_name, name: name, body: body, source_indices: si) { + let kw = rust_items().func_keyword + return concat(rust_visibility_prefix(), kw, " ", emit_ident(name: name, target: Rust), type_params_str, "(", params_str, ")", ret_str, " {\n", + make_indent(level: 1), + "panic!(\"unrealized host seam '", name, "' in ", scope.module_name, + ": this declaration's whole body is a self-call, so it has no behaviour on this target. ", + "It compiles as a refusal rather than as recursion. Give it a row in ", + "extdeps.languages.rust.emit rt_function_registry and a body in v1.runtime_rust, or delete its consumers.\")\n}") + } if use_tco { let tco_params_str = emit_tco_params(params: value_params, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: si, variant_to_enum: emit_info.variant_to_enum, env: scope.type_env) let body_str = emit_typed_tco_body(texpr: body, fn_name: name, params: value_params, registry: registry, scope: body_scope, depth: depth + 1, shared_types: shared_types, emit_info: body_emit_info) @@ -8788,8 +9021,16 @@ fn rust_runtime_bridge_passes_receiver_by_ref(function_name: String) -> Bool { // rt_bridge_function_names ever becomes hand-authored rather than derived from // rt_function_registry, a miss stops meaning "spelled the same" and this arm // becomes a fabrication, so that change invalidates this argument. -// An unknown name cannot reach either use of the result, which a reader of this -// function alone cannot see. THE MECHANISM CHANGED WITH CallTargetIdentity and +// AN UNKNOWN NAME COULD REACH THIS, AND DID -- the claim that stood here was false and the +// receipt is worth keeping. `length` is a primitive with a projection row and NO row in +// rt_function_registry, so it arrived as a runtime primitive, missed rt_bridge_function_names, +// took the identity arm, and emitted `v1_rt::length` at 73 sites in the v2 compiler closure -- +// a symbol the seed does not define. The Absent arm was never the defect: the derivation +// argument below is sound for every name the registry HOLDS, and the hole was that nothing +// checked membership first. `rust_runtime_primitive_has_bridge` is now that check, asked before +// this function is reached, so the Absent arm's premise -- this primitive is in the registry and +// merely spelled the same -- is established rather than assumed. +// THE MECHANISM CHANGED WITH CallTargetIdentity and // the argument is restated rather than kept, because the old wording named the // two name-matching guards that no longer exist: emit_typed_call consumed // runtime_name only inside `if is_rt`, and is_rt was @@ -8900,8 +9141,11 @@ fn emit_rust_empty_set_expr(set_type: Node, shared_types: Set, source_in // The method roster had no template on that path -- nothing was lowered by it -- so its wrap was // unpaired by construction. The repair is to pair each wrap with the roster that produced the text // beside it, and at the plain-call position to derive the gate from the SAME predicate that chose -// the emission form (rust_plain_call_emits_as_runtime_bridge, read by emit_typed_call for is_rt and -// by emit_typed_call_expr for the wrap), so no second opinion about the form exists to drift. +// the emission form. That fusion is now stated on the PRIMITIVE's identity rather than the leaf +// spelling: emit_typed_call's is_rt and emit_typed_call_expr's wrap both key on the target's +// runtime_primitive_name, and both ask the same registry, so no second opinion about the form +// exists to drift. rust_plain_call_emits_as_runtime_bridge below is the older leaf-spelling +// statement of that predicate and has no caller. // RUNG (DESIGN 4b): this moves the plain-call half of the class from silent wrongness -- emitted, // compiled by us, refused only by rustc downstream -- to STRUCTURALLY IMPOSSIBLE for the // cross-roster direction specifically: after this change no method-roster row can reach a plain @@ -8977,7 +9221,7 @@ fn call_semantics_target(cs: CallSemantics?) -> CallTargetIdentity { fn call_target_runtime_primitive_name(target: CallTargetIdentity) -> String? { match target { - RuntimePrimitiveCall { primitive_name: primitive_name } => Present { value: primitive_name } + RuntimePrimitiveCall { primitive_name: primitive_name, projected_from: _ } => Present { value: primitive_name } SourceDeclarationCall { owner_module_path: _, decl_name: _ } => none CallableTargetUndetermined => none } @@ -8987,6 +9231,26 @@ fn call_target_is_runtime_primitive(target: CallTargetIdentity) -> Bool { call_target_runtime_primitive_name(target: target) != none } +// THE DECLARATION A RUNTIME TARGET WAS PROJECTED FROM, WHICH IS THE ONLY HONEST ROUTE WHEN THIS +// TARGET HAS NO ROW IN THIS TARGET'S RUNTIME REGISTRY. Absent means builtin resolution produced +// the target and no declaration ever answered, so there is nothing to fall back to and the one +// remaining arm is a refusal. +fn call_target_projected_declaration(target: CallTargetIdentity) -> DeclaredCallableIdentity? { + match target { + RuntimePrimitiveCall { primitive_name: _, projected_from: projected_from } => projected_from + SourceDeclarationCall { owner_module_path: _, decl_name: _ } => none + CallableTargetUndetermined => none + } +} + +// THE REALIZATION HALF OF THE DECISION, ASKED OF THE PRIMITIVE'S OWN IDENTITY RATHER THAN THE +// AUTHORED LEAF SPELLING. `rt_functions()` is derived from rt_function_registry, so membership +// here is exactly `this primitive has a v1_rt realization`. A target that is a runtime primitive +// but has no row is NOT a bridge call, and treating it as one is what emitted `v1_rt::length`. +fn rust_runtime_primitive_has_bridge(primitive_name: String) -> Bool { + map_contains_key(rt_functions(), primitive_name) +} + fn emit_rust_expr_call(expr: Node, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { match expr.expr_data { ExprCall { call_semantics: cs, descent_evidence: _ } => @@ -9554,6 +9818,7 @@ fn emit_typed_call(func: String, args: List, callee_is_function_value: Boo Absent => "" } let is_rt = callee_is_function_value == false && target_is_runtime + && rust_runtime_primitive_has_bridge(primitive_name: runtime_primitive_name) let is_rt_ref_map = is_rt && map_contains_key(rt_ref_map_functions(), runtime_primitive_name) let arg_strs = filled_args |> enumerate |> map(pair => let idx = pair.first @@ -9593,8 +9858,14 @@ fn emit_typed_call(func: String, args: List, callee_is_function_value: Boo else { concat("crate::", module_to_filename(name: owner), "::", emit_ident(name: decl, target: Rust)) } CallableTargetUndetermined => emit_error_expr(message: "call target identity was not established before Rust emission", target: Rust) - RuntimePrimitiveCall { primitive_name: _ } => - emit_error_expr(message: "runtime call target lost its bridge identity", target: Rust) + RuntimePrimitiveCall { primitive_name: unbridged_primitive, projected_from: projected_from } => + match projected_from { + Present { value: declared } => + if declared.owner_module_path == scope.module_name { emit_ident(name: declared.decl_name, target: Rust) } + else { concat("crate::", module_to_filename(name: declared.owner_module_path), "::", emit_ident(name: declared.decl_name, target: Rust)) } + Absent => + emit_error_expr(message: concat("primitive ", unbridged_primitive, " has no v1_rt realization and no declaration to emit"), target: Rust) + } } } let func_name = if callee_self_capture { concat(func_ident, ".clone()") } else { func_ident } let call_str = if is_rt && func == "concat" && all_args |> count > 2 { @@ -9776,6 +10047,14 @@ fn lambda_scope_from_children(scope: InferScope, params: List, param_nod ) } +// THE RENDERED-STRING ADMISSION ON AN INFERRED PARAMETER TYPE: +// The same admission the fold arm applies to its element annotation +// (fold_elem_type_str): a rendered `FreeMonoid` whose T is no fn generic names a +// type this closure cannot spell, and an annotation that names it is refused by rustc +// (E0425 cannot find type `T`, v2.lens.mandatory_tag on the 00_compile closure). The +// node-grain check above missed it because the formal's spelling resolves to SOME +// binding in the whole-pool env; the rendered-string check is the one authority both +// arms consult, so the two cannot disagree. fn lambda_param_type_strs(params: List, param_nodes: List, fallback_types: List, shared_types: Set, source_indices: Map, emit_info: EmitGraphInfo, fold_acc_uses_fallback: Bool, type_env: TypeEnv) -> List { params |> enumerate |> map(pair => let idx = pair.first @@ -9794,7 +10073,9 @@ fn lambda_param_type_strs(params: List, param_nodes: List, fallbac } else if type_node_has_closure_unbound_generic_atom(n: param_type, generic_param_names: emit_info.fn_generic_param_names, env: type_env, source_indices: source_indices) { none } else { - Present { value: render_rust_type(n: param_type, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) } + let rendered = render_rust_type(n: param_type, shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) + if rust_fold_rendered_type_has_any_spurious_generic(type_str: rendered, generic_param_names: emit_info.fn_generic_param_names) { none } + else { Present { value: rendered } } } _ => none } @@ -9880,6 +10161,19 @@ fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str // instead would restate it, and would then have to be kept in step with any future change to the strip // -- reintroducing the shape of this defect while fixing this instance of it. `_` is also the device // already used for elem_render_has_spurious_generic a few bindings earlier. +// THE INIT TURBOFISH DECLINES A DECLARATION'S OWN FORMALS, BY THE SAME ADMISSION THE LAMBDA USES. +// In a module that resolves Map, Symbol, empty_map and map_insert by namespace alone +// (v2.lens.unit_modeling imports none of them), the fold's accumulator arrives as the v2 Map +// declaration's parametric shape -- Map with K and V the declaration's formals -- and so +// does the call's contextual result type, so no choice between the two helps. The lambda beside +// the init already renders such an accumulator as `_` (rust_fold_rendered_type_has_any_spurious_generic +// over the rendered string), while the init arm wrote the formals into a turbofish: +// `v1_rt::rc_empty_map::()`, four sites on the 00_compile closure. The init now asks the same +// rendered-string admission and, where the formals are unbound in this fn's generic scope, defers +// the key and value types to the target's own inference exactly as the lambda does -- rustc binds +// them from the fold body or refuses loudly. An earlier cut of this repair instead widened the +// "under-resolved accumulator" test to prefer the contextual type; that was a measured NON-EVENT +// (falsifier m09: removing it reddened nothing, and the four sites stayed) and is deleted. fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Node?, result_type: InferredNode?, receiver: Node, args: List, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { let recv_str = emit_typed_iter_owned_receiver(receiver: receiver, downstream: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) let elem_type_str = collection_element_type(receiver_type: receiver.inferred, shared_types: shared_types, source_indices: scope.type_env.source_indices, emit_info: emit_info, type_env: scope.type_env) @@ -10010,7 +10304,9 @@ fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Nod let init_func = expr_call_func_at(texpr: arg_value(n: init_arg), source_indices: scope.type_env.source_indices) if init_func == "empty_map" && acc_type_str != "_" && acc_type_str != "" && !acc_has_unit_child { let kv_type_str = rust_empty_map_kv_type_str(map_type: acc_type_node, shared_types: shared_types, source_indices: scope.type_env.source_indices) - if kv_type_str != "" { + if kv_type_str != "" && rust_fold_rendered_type_has_any_spurious_generic(type_str: concat("<", kv_type_str, ">"), generic_param_names: emit_info.fn_generic_param_names) { + "v1_rt::rc_empty_map::<_, _>()" + } else if kv_type_str != "" { concat("v1_rt::rc_empty_map::<", kv_type_str, ">()") } else { rust_shared_wrap_ctor(inner_expr: "HashMap::new() /* BRIDGE: fold empty_map value type unresolved */") @@ -10227,6 +10523,19 @@ fn emit_rust_first_method_call(receiver: Node, registry: Map, // method's NAME appears in `rt_functions()`, which is the same leaf-spelling re-lookup the call // path above removed: a record field holding a callable named `contains` lost to the bridge // roster purely on spelling. +// +// ONE CALLER DID NOT KNOW, AND SAID `true` ANYWAY. The algebra fallback in emit_typed_method_call +// hardcoded runtime_bridge: true, and tier0 method resolution resolves an ordinary fn-typed RECORD +// FIELD through lookup_field_in_product -- so `algebra.step(..)` arrived as AlgebraMethodSemantics +// with the field node as its method_def, took that fallback, and emitted `v1_rt::step`. The +// callable-field arm directly below exists for exactly that receiver and was unreachable, because +// it is guarded on runtime_bridge == false. 65 E0425s in the v2 compiler closure -- `member`, +// `apply`, `is_empty`, `step`, `init`, `allocate_literal` and friends -- were that one `true`. +// +// It now passes the realization question, keyed on the same registry as the plain-call seam +// (rust_runtime_primitive_has_bridge), so a method that IS a bridge still lowers to one, a +// callable field lowers as a field, and a name that is neither reaches the loud refusal below +// instead of a fabricated v1_rt symbol. fn emit_rust_generic_method_call(method_name: String, receiver: Node, args: List, result_type: InferredNode?, runtime_bridge: Bool, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { let function_name = method_name if runtime_bridge == false && rust_receiver_has_callable_method_field(receiver: receiver, method_name: function_name, scope: scope) { @@ -10252,9 +10561,28 @@ fn emit_rust_generic_method_call(method_name: String, receiver: Node, args: List } } +// TWO AUTHORITIES DECIDED ONE SERVICE CALL, AND THE EMISSION FOLLOWED THE ONE THAT DOES NOT REALIZE. +// The Rust realization of a service op is the SIGNATURE fact: ItemInfo.service_names, computed by +// collect_typed_service_calls over is_typed_service_call_receiver, which makes the fn async, binds +// the service as a parameter, and puts `.await?` on every caller. The method-call arm keyed on +// ServiceMethodSemantics alone and wrote `filesystem.read(path).await?` into a fn the signature +// authority had judged service-free -- an unbound name inside a non-async fn (E0425 + E0728, two +// sites in v2.compiler.source_authority, and the ONLY `.await?` in the whole emitted closure). That +// is a fabricated realization. The arm now asks the same predicate the signature asks, so the two +// agree by construction; where it declines, the op is a host seam with no realization in this +// target and emits the generated refusal body the seam ruling prescribes -- a panic naming the seam, +// never a call into a binding that does not exist. The realized arm is currently UNINHABITED by +// the corpus (no `.dag` source in the seed or the closure spells a receiver the predicate accepts), +// so its positive control is not authorable today; that is recorded, not papered over. +fn rust_unrealized_service_op_refusal(service_name: String, method: String, module_name: String) -> String { + concat("panic!(\"unrealized host seam: service op ", service_name, ".", method, " in ", module_name, ": the Rust realization binds a service only through an async fn parameter, and this function declares none\")") +} + fn emit_typed_method_call(method_call_node: Node, receiver: Node, method: String, args: List, result_type: InferredNode?, method_semantics: MethodSemantics?, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { if method_semantics != none { match method_semantics.value { - ServiceMethodSemantics { service_name: svc_name, op_params: op_params } => { + ServiceMethodSemantics { service_name: svc_name, op_params: op_params } => if !is_typed_service_call_receiver(receiver: receiver, source_indices: scope.type_env.source_indices) { + rust_unrealized_service_op_refusal(service_name: svc_name, method: method, module_name: scope.module_name) + } else { let var_name = service_var_name(service_name: svc_name) let si = scope.type_env.source_indices let filled_args = fill_op_default_args(ordered: args, op_params: op_params, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) @@ -10290,7 +10618,10 @@ fn emit_typed_method_call(method_call_node: Node, receiver: Node, method: String let method_name = if method_name_raw == "length" { if is_string_typed_expr(e: receiver, source_indices: scope.type_env.source_indices) { "string_length" } else { "count" } } else { method_name_raw } - if method_name == "fold" { + let algebra_field_call = rust_callable_field_method_call(method_name: method_name, receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) + if algebra_field_call != none { + match algebra_field_call { Present { value: field_call } => field_call Absent => "" } + } else if method_name == "fold" { emit_rust_fold_method_call(method_call_node: method_call_node, fold_accumulator_type: fold_accumulator_type, result_type: result_type, receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) } else if method_name == "sort_by" { emit_rust_sort_by_method_call(receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) @@ -10329,14 +10660,19 @@ fn emit_typed_method_call(method_call_node: Node, receiver: Node, method: String rust_shared_wrap_ctor(inner_expr: raw) } else { raw } Absent => - emit_rust_generic_method_call(method_name: method_name, receiver: receiver, args: args, result_type: result_type, runtime_bridge: true, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) + emit_rust_generic_method_call(method_name: method_name, receiver: receiver, args: args, result_type: result_type, runtime_bridge: rust_runtime_primitive_has_bridge(primitive_name: method_name), registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) } } } } } PlainMethodSemantics => - if method == "get" && is_collection_typed_expr(texpr: receiver, source_indices: scope.type_env.source_indices) { + if rust_callable_field_method_call(method_name: method, receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) != none { + match rust_callable_field_method_call(method_name: method, receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) { + Present { value: field_call } => field_call + Absent => "" + } + } else if method == "get" && is_collection_typed_expr(texpr: receiver, source_indices: scope.type_env.source_indices) { emit_rust_get_method_call(receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) } else { let recv_str = emit_typed_expr(texpr: receiver, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) @@ -10875,10 +11211,46 @@ fn rust_record_field_needs_fn_rc(scope: InferScope, struct_name: String, field_n // The rt_functions() name guard that stood here is gone: the ONE caller now passes // `runtime_bridge` from the resolution that decided it, so this answers only the question it is // named for -- does the receiver have a callable field by this name. +// THE RECEIVER'S OWN CALLABLE FIELD OUTRANKS EVERY NAME-KEYED TABLE, and this is the same class as +// class B one layer up. That repair stopped the algebra FALLBACK from asserting a v1_rt bridge for +// a fn-typed record field, but left the two rust_method_templates lookups in front of it keyed on +// the bare spelling -- so a field named count, first, join, split, take, skip, last, chars, +// enumerate or is_empty was still captured by a target template and emitted as `recv.count()` +// rather than `(recv.count)(..)`. Adding the is_empty row to rust_simple_method_specs is what made +// this urgent: it put a brand-new spelling in front of that table, and the spelling most likely to +// name a predicate field. +// +// A receiver that genuinely has a fn-typed field of this name is answering with its OWN structure, +// which no global roster can outrank -- so the check goes before the tables rather than after them, +// and one helper serves both arms so the two cannot drift. +fn rust_callable_field_method_call(method_name: String, receiver: Node, args: List, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String? { + if rust_receiver_has_callable_method_field(receiver: receiver, method_name: method_name, scope: scope) { + let recv_str = emit_typed_expr_base(texpr: receiver, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) + let arg_strs = args |> map(a => emit_cloned_arg(texpr: arg_value(n: a), registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info)) + Present { value: concat("(", recv_str, ".", emit_ident(name: method_name, target: Rust), ")(", arg_strs |> join(separator: ", "), ")") } + } else { + none + } +} + +// THE RECEIVER'S OWN DECLARED RECORD, NEVER ITS ALGEBRA PROFILE. This asked +// rust_record_field_needs_fn_rc, which sweeps rust_struct_field_lookup_candidates -- and that +// candidate list deliberately widens a receiver's name to its container template algebra so +// FIELD-TYPE questions can be answered through the profile. An algebra declares its OPERATIONS as +// arrow-typed members, so under that widening every Map receiver "has a callable field" named +// map_keys, map_values, lookup or get, and the callable-field tier captured the very bridge calls +// it sits in front of: 202 rustc refusals on the regenerated seed (E0609 no field `map_keys` on +// Rc>, E0615 on `get`), one class, measured at the first compile of the converged +// mirror. That is the SAME defect this tier was built to close, one level up -- a name-keyed +// lookup consuming an identity domain that is not its own -- so the fix is to consume only the +// domain this question is about: a field declared on the receiver's own record. fn rust_receiver_has_callable_method_field(receiver: Node, method_name: String, scope: InferScope) -> Bool { let receiver_type = resolved_type(n: receiver) let receiver_type_name = authored_name_at(source_indices: scope.type_env.source_indices, node: receiver_type) - rust_record_field_needs_fn_rc(scope: scope, struct_name: receiver_type_name, field_name: method_name) + match rust_struct_field_type_node(scope: scope, struct_name: receiver_type_name, field_name: method_name) { + Present { value: field_type } => field_type.connective == Arrow + Absent => false + } } fn rust_struct_field_type_node_variant_aware(scope: InferScope, emit_info: EmitGraphInfo, struct_name: String, field_name: String) -> Node? { diff --git a/src/v1/compile.dag b/src/v1/compile.dag index 44be7cb8620..f1b21bf910a 100644 --- a/src/v1/compile.dag +++ b/src/v1/compile.dag @@ -385,7 +385,7 @@ fn var_binding_kind_name(value: VarBindingKind) -> String { fn serialize_call_target_identity(value: CallTargetIdentity) -> String { match value { - RuntimePrimitiveCall { primitive_name: primitive_name } => + RuntimePrimitiveCall { primitive_name: primitive_name, projected_from: _ } => concat("{\"kind\": \"RuntimePrimitiveCall\", \"primitive_name\": ", json_quote(s: primitive_name), "}") SourceDeclarationCall { owner_module_path: owner, decl_name: decl } => concat("{\"kind\": \"SourceDeclarationCall\", \"owner_module_path\": ", json_quote(s: owner), ", \"decl_name\": ", json_quote(s: decl), "}") diff --git a/src/v1/runtime_rust.dag b/src/v1/runtime_rust.dag index db4279d885b..ce5622ce922 100644 --- a/src/v1/runtime_rust.dag +++ b/src/v1/runtime_rust.dag @@ -117,6 +117,18 @@ fn rt_string_ops() -> String { "pub fn string_length(s: &str) -> i64 {\n", " if s.is_ascii() { s.len() as i64 } else { s.chars().count() as i64 }\n", "}\n\n", + "/// The two symbol bridges. `Symbol` is declared to realize as `String` on this target\n", + "/// (extdeps.languages.rust.types), so interning and its inverse are both the identity\n", + "/// HERE and nowhere else: a target whose Symbol is a table index needs a real table, and\n", + "/// its registry rows would say so. These exist because the .dag declarations are\n", + "/// self-calls -- host seams -- and emitting a self-call produces a function that compiles\n", + "/// and never returns (std.primitive_projection symbol_lexeme_seam_disposition_note).\n", + "pub fn symbol_lexeme(sym: String) -> String {\n", + " sym\n", + "}\n\n", + "pub fn symbol_intern_lexeme(lexeme: String) -> String {\n", + " lexeme\n", + "}\n\n", "/// See `char_at`: the ASCII fast path is bounded by `end`, not by the whole string.\n", "pub fn substring(s: &str, start: i64, end: i64) -> String {\n", " let start = start.max(0) as usize;\n", diff --git a/src/v1/stage0/src/extdeps_languages_rust_emit.rs b/src/v1/stage0/src/extdeps_languages_rust_emit.rs index 455431df768..fd50682ac44 100644 --- a/src/v1/stage0/src/extdeps_languages_rust_emit.rs +++ b/src/v1/stage0/src/extdeps_languages_rust_emit.rs @@ -89,7 +89,7 @@ pub fn rust_container_templates() -> Rc> { pub fn rust_simple_method_specs() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_value(serde_json::json!([{"method_name": "count", "template": "({recv}.len() as i64)", "wraps_result": false}, {"method_name": "join", "template": "{recv}.join(&{arg})", "wraps_result": false}, {"method_name": "split", "template": "{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()", "wraps_result": true}, {"method_name": "last", "template": "{recv}.last().cloned()", "wraps_result": false}, {"method_name": "first", "template": "{recv}.first().cloned()", "wraps_result": false}, {"method_name": "enumerate", "template": "{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()", "wraps_result": true}, {"method_name": "chars", "template": "{recv}.chars().map(|c| c as i64).collect::>()", "wraps_result": true}, {"method_name": "skip", "template": "{recv}.iter().cloned().skip({arg} as usize).collect::>()", "wraps_result": true}, {"method_name": "take", "template": "{recv}.iter().cloned().take({arg} as usize).collect::>()", "wraps_result": true}])) + serde_json::from_value(serde_json::json!([{"method_name": "count", "template": "({recv}.len() as i64)", "wraps_result": false}, {"method_name": "join", "template": "{recv}.join(&{arg})", "wraps_result": false}, {"method_name": "split", "template": "{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()", "wraps_result": true}, {"method_name": "last", "template": "{recv}.last().cloned()", "wraps_result": false}, {"method_name": "first", "template": "{recv}.first().cloned()", "wraps_result": false}, {"method_name": "enumerate", "template": "{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()", "wraps_result": true}, {"method_name": "chars", "template": "{recv}.chars().map(|c| c as i64).collect::>()", "wraps_result": true}, {"method_name": "skip", "template": "{recv}.iter().cloned().skip({arg} as usize).collect::>()", "wraps_result": true}, {"method_name": "take", "template": "{recv}.iter().cloned().take({arg} as usize).collect::>()", "wraps_result": true}, {"method_name": "is_empty", "template": "{recv}.is_empty()", "wraps_result": false}])) .expect("valid data definition") }; } @@ -525,7 +525,7 @@ pub struct RuntimeFunction { pub fn rt_function_registry() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_value(serde_json::json!([{"name": "concat", "bridge_name": "concat", "passes_by_ref": false, "wraps_result": false}, {"name": "char_at", "bridge_name": "char_at", "passes_by_ref": true, "wraps_result": false}, {"name": "string_length", "bridge_name": "string_length", "passes_by_ref": true, "wraps_result": false}, {"name": "substring", "bridge_name": "substring", "passes_by_ref": true, "wraps_result": false}, {"name": "string_contains", "bridge_name": "string_contains", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_while", "bridge_name": "scan_while", "passes_by_ref": true, "wraps_result": false}, {"name": "skip_horizontal_ws", "bridge_name": "skip_horizontal_ws", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_to_eol", "bridge_name": "scan_to_eol", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_string_end", "bridge_name": "scan_string_end", "passes_by_ref": true, "wraps_result": false}, {"name": "code_point", "bridge_name": "code_point", "passes_by_ref": false, "wraps_result": false}, {"name": "from_code_point", "bridge_name": "from_code_point", "passes_by_ref": false, "wraps_result": false}, {"name": "lookup", "bridge_name": "lookup", "passes_by_ref": true, "wraps_result": false}, {"name": "index_by", "bridge_name": "rc_index_by", "passes_by_ref": false, "wraps_result": false}, {"name": "empty_map", "bridge_name": "rc_empty_map", "passes_by_ref": false, "wraps_result": false}, {"name": "empty_set", "bridge_name": "rc_empty_set", "passes_by_ref": false, "wraps_result": false}, {"name": "set_insert", "bridge_name": "rc_set_insert", "passes_by_ref": false, "wraps_result": false}, {"name": "set_union", "bridge_name": "rc_set_union", "passes_by_ref": false, "wraps_result": false}, {"name": "set_contains", "bridge_name": "set_contains", "passes_by_ref": true, "wraps_result": false}, {"name": "map_insert", "bridge_name": "rc_map_insert", "passes_by_ref": false, "wraps_result": false}, {"name": "map_merge", "bridge_name": "rc_map_merge", "passes_by_ref": false, "wraps_result": false}, {"name": "list_concat", "bridge_name": "rc_list_concat", "passes_by_ref": false, "wraps_result": false}, {"name": "str_eq", "bridge_name": "str_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "filesystem_read", "bridge_name": "filesystem_read", "passes_by_ref": false, "wraps_result": false}, {"name": "list_push", "bridge_name": "rc_list_push", "passes_by_ref": false, "wraps_result": false}, {"name": "map_get", "bridge_name": "map_get", "passes_by_ref": true, "wraps_result": false}, {"name": "map_keys", "bridge_name": "map_keys", "passes_by_ref": true, "wraps_result": true}, {"name": "sorted_map_keys", "bridge_name": "sorted_map_keys", "passes_by_ref": true, "wraps_result": true}, {"name": "map_values", "bridge_name": "map_values", "passes_by_ref": true, "wraps_result": true}, {"name": "parse_int", "bridge_name": "parse_int", "passes_by_ref": false, "wraps_result": false}, {"name": "map_contains_key", "bridge_name": "map_contains_key", "passes_by_ref": true, "wraps_result": false}, {"name": "map_has", "bridge_name": "map_has", "passes_by_ref": true, "wraps_result": false}, {"name": "map_is_empty", "bridge_name": "map_is_empty", "passes_by_ref": true, "wraps_result": false}, {"name": "reverse", "bridge_name": "reverse", "passes_by_ref": false, "wraps_result": false}, {"name": "replace", "bridge_name": "replace", "passes_by_ref": false, "wraps_result": false}, {"name": "chars_to_string", "bridge_name": "chars_to_string", "passes_by_ref": true, "wraps_result": false}, {"name": "record_source_chars_index_lookup", "bridge_name": "record_source_chars_index_lookup", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_is_enabled", "bridge_name": "resolution_silent_pick_is_enabled", "passes_by_ref": false, "wraps_result": false}, {"name": "name_resolution_policy_is_namespace_only", "bridge_name": "name_resolution_policy_is_namespace_only", "passes_by_ref": false, "wraps_result": false}, {"name": "type_ref_hit_ne_bind_measure_active", "bridge_name": "type_ref_hit_ne_bind_measure_active", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_global_bare_lcp_pick", "bridge_name": "resolution_silent_pick_record_global_bare_lcp_pick", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_global_bare_lcp_tie", "bridge_name": "resolution_silent_pick_record_global_bare_lcp_tie", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_fn_parent_first_hit", "bridge_name": "resolution_silent_pick_record_fn_parent_first_hit", "passes_by_ref": false, "wraps_result": false}, {"name": "append", "bridge_name": "append", "passes_by_ref": false, "wraps_result": true}, {"name": "contains", "bridge_name": "contains", "passes_by_ref": false, "wraps_result": false}, {"name": "starts_with", "bridge_name": "starts_with", "passes_by_ref": false, "wraps_result": false}, {"name": "ends_with", "bridge_name": "ends_with", "passes_by_ref": false, "wraps_result": false}, {"name": "trim", "bridge_name": "trim", "passes_by_ref": false, "wraps_result": false}, {"name": "count", "bridge_name": "count", "passes_by_ref": false, "wraps_result": false}, {"name": "clamp", "bridge_name": "clamp", "passes_by_ref": false, "wraps_result": false}, {"name": "atom_identity_hash", "bridge_name": "atom_identity_hash", "passes_by_ref": false, "wraps_result": false}, {"name": "hash_combine", "bridge_name": "hash_combine", "passes_by_ref": false, "wraps_result": false}, {"name": "trace_mark", "bridge_name": "trace_mark", "passes_by_ref": false, "wraps_result": false}, {"name": "rc_ptr_eq", "bridge_name": "rc_ptr_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "rc_vec_ptr_eq", "bridge_name": "rc_vec_ptr_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "is_xid_start", "bridge_name": "is_xid_start", "passes_by_ref": false, "wraps_result": false}, {"name": "is_xid_continue", "bridge_name": "is_xid_continue", "passes_by_ref": false, "wraps_result": false}, {"name": "is_emoji_ident", "bridge_name": "is_emoji_ident", "passes_by_ref": false, "wraps_result": false}])) + serde_json::from_value(serde_json::json!([{"name": "concat", "bridge_name": "concat", "passes_by_ref": false, "wraps_result": false}, {"name": "char_at", "bridge_name": "char_at", "passes_by_ref": true, "wraps_result": false}, {"name": "string_length", "bridge_name": "string_length", "passes_by_ref": true, "wraps_result": false}, {"name": "substring", "bridge_name": "substring", "passes_by_ref": true, "wraps_result": false}, {"name": "string_contains", "bridge_name": "string_contains", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_while", "bridge_name": "scan_while", "passes_by_ref": true, "wraps_result": false}, {"name": "skip_horizontal_ws", "bridge_name": "skip_horizontal_ws", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_to_eol", "bridge_name": "scan_to_eol", "passes_by_ref": true, "wraps_result": false}, {"name": "scan_string_end", "bridge_name": "scan_string_end", "passes_by_ref": true, "wraps_result": false}, {"name": "code_point", "bridge_name": "code_point", "passes_by_ref": false, "wraps_result": false}, {"name": "from_code_point", "bridge_name": "from_code_point", "passes_by_ref": false, "wraps_result": false}, {"name": "lookup", "bridge_name": "lookup", "passes_by_ref": true, "wraps_result": false}, {"name": "index_by", "bridge_name": "rc_index_by", "passes_by_ref": false, "wraps_result": false}, {"name": "empty_map", "bridge_name": "rc_empty_map", "passes_by_ref": false, "wraps_result": false}, {"name": "empty_set", "bridge_name": "rc_empty_set", "passes_by_ref": false, "wraps_result": false}, {"name": "set_insert", "bridge_name": "rc_set_insert", "passes_by_ref": false, "wraps_result": false}, {"name": "set_union", "bridge_name": "rc_set_union", "passes_by_ref": false, "wraps_result": false}, {"name": "set_contains", "bridge_name": "set_contains", "passes_by_ref": true, "wraps_result": false}, {"name": "map_insert", "bridge_name": "rc_map_insert", "passes_by_ref": false, "wraps_result": false}, {"name": "map_merge", "bridge_name": "rc_map_merge", "passes_by_ref": false, "wraps_result": false}, {"name": "list_concat", "bridge_name": "rc_list_concat", "passes_by_ref": false, "wraps_result": false}, {"name": "str_eq", "bridge_name": "str_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "filesystem_read", "bridge_name": "filesystem_read", "passes_by_ref": false, "wraps_result": false}, {"name": "list_push", "bridge_name": "rc_list_push", "passes_by_ref": false, "wraps_result": false}, {"name": "map_get", "bridge_name": "map_get", "passes_by_ref": true, "wraps_result": false}, {"name": "map_keys", "bridge_name": "map_keys", "passes_by_ref": true, "wraps_result": true}, {"name": "sorted_map_keys", "bridge_name": "sorted_map_keys", "passes_by_ref": true, "wraps_result": true}, {"name": "map_values", "bridge_name": "map_values", "passes_by_ref": true, "wraps_result": true}, {"name": "parse_int", "bridge_name": "parse_int", "passes_by_ref": false, "wraps_result": false}, {"name": "map_contains_key", "bridge_name": "map_contains_key", "passes_by_ref": true, "wraps_result": false}, {"name": "map_has", "bridge_name": "map_has", "passes_by_ref": true, "wraps_result": false}, {"name": "map_is_empty", "bridge_name": "map_is_empty", "passes_by_ref": true, "wraps_result": false}, {"name": "reverse", "bridge_name": "reverse", "passes_by_ref": false, "wraps_result": false}, {"name": "replace", "bridge_name": "replace", "passes_by_ref": false, "wraps_result": false}, {"name": "chars_to_string", "bridge_name": "chars_to_string", "passes_by_ref": true, "wraps_result": false}, {"name": "record_source_chars_index_lookup", "bridge_name": "record_source_chars_index_lookup", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_is_enabled", "bridge_name": "resolution_silent_pick_is_enabled", "passes_by_ref": false, "wraps_result": false}, {"name": "name_resolution_policy_is_namespace_only", "bridge_name": "name_resolution_policy_is_namespace_only", "passes_by_ref": false, "wraps_result": false}, {"name": "type_ref_hit_ne_bind_measure_active", "bridge_name": "type_ref_hit_ne_bind_measure_active", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_global_bare_lcp_pick", "bridge_name": "resolution_silent_pick_record_global_bare_lcp_pick", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_global_bare_lcp_tie", "bridge_name": "resolution_silent_pick_record_global_bare_lcp_tie", "passes_by_ref": false, "wraps_result": false}, {"name": "resolution_silent_pick_record_fn_parent_first_hit", "bridge_name": "resolution_silent_pick_record_fn_parent_first_hit", "passes_by_ref": false, "wraps_result": false}, {"name": "append", "bridge_name": "append", "passes_by_ref": false, "wraps_result": true}, {"name": "contains", "bridge_name": "contains", "passes_by_ref": false, "wraps_result": false}, {"name": "starts_with", "bridge_name": "starts_with", "passes_by_ref": false, "wraps_result": false}, {"name": "ends_with", "bridge_name": "ends_with", "passes_by_ref": false, "wraps_result": false}, {"name": "trim", "bridge_name": "trim", "passes_by_ref": false, "wraps_result": false}, {"name": "count", "bridge_name": "count", "passes_by_ref": false, "wraps_result": false}, {"name": "clamp", "bridge_name": "clamp", "passes_by_ref": false, "wraps_result": false}, {"name": "atom_identity_hash", "bridge_name": "atom_identity_hash", "passes_by_ref": false, "wraps_result": false}, {"name": "hash_combine", "bridge_name": "hash_combine", "passes_by_ref": false, "wraps_result": false}, {"name": "trace_mark", "bridge_name": "trace_mark", "passes_by_ref": false, "wraps_result": false}, {"name": "rc_ptr_eq", "bridge_name": "rc_ptr_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "rc_vec_ptr_eq", "bridge_name": "rc_vec_ptr_eq", "passes_by_ref": false, "wraps_result": false}, {"name": "is_xid_start", "bridge_name": "is_xid_start", "passes_by_ref": false, "wraps_result": false}, {"name": "is_xid_continue", "bridge_name": "is_xid_continue", "passes_by_ref": false, "wraps_result": false}, {"name": "is_emoji_ident", "bridge_name": "is_emoji_ident", "passes_by_ref": false, "wraps_result": false}, {"name": "symbol_lexeme", "bridge_name": "symbol_lexeme", "passes_by_ref": false, "wraps_result": false}, {"name": "symbol_intern_lexeme", "bridge_name": "symbol_intern_lexeme", "passes_by_ref": false, "wraps_result": false}])) .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/namespace_wave_admission.rs b/src/v1/stage0/src/namespace_wave_admission.rs index 4fa2876b39a..cd17b796a93 100644 --- a/src/v1/stage0/src/namespace_wave_admission.rs +++ b/src/v1/stage0/src/namespace_wave_admission.rs @@ -367,7 +367,90 @@ pub struct TransitionAdmission { /// the shape the paragraph above records for the first 53. Removed by the trigger they were /// authored with. The roster is EMPTY and empty is not permissive: a run carrying a real /// namespace delta still refuses it as UNADJUDICATED until its author adds a row here. -pub const NAMESPACE_TRANSITION_ADMISSIONS: &[TransitionAdmission] = &[]; +/// FOURTH TRANSITION (2026-08-29, gunbc#9665 / issue #9664). `DeclaredCallableIdentity` moved +/// from `v1.compiler.infer_sigs` to `v1.std.core`, so every binding of that spelling inside +/// `v1.compiler.infer_lookup` reports `TargetChanged`. The move is not cosmetic and not +/// avoidable by re-spelling: `v1.std.core`'s own `CallTargetIdentity` now CARRIES a +/// `DeclaredCallableIdentity` on its `RuntimePrimitiveCall` arm -- the declaration a runtime +/// target was projected from, which is what lets Rust emission fall back to the declaration +/// when its registry has no bridge for the primitive instead of inventing `v1_rt::length`. +/// `v1.compiler.infer_sigs` imports `v1.std.core`, so the type could not stay where it was +/// without a cycle, and re-declaring the pair in `v1.std.core` beside the original is the +/// second-representation defect DESIGN §3 forbids -- the type's own note already says so, and +/// that note travelled with the declaration. +/// +/// FOUR ROWS, ONE PER BINDING SITE, and they are enumerated rather than matched by a module +/// pattern because the roster's own rule is that its population is an enumeration and never a +/// predicate. The two `membership` deltas this change also produces +/// (`v1.compiler.emit_rust -> std.decl_ref` and `-> std.primitive_projection`) are +/// `ExplicitlyEvaluatedZeroDelta` and auto-admit, so they are deliberately absent here. +/// +/// DISSOLVE-ON: this PR merging. Once `DeclaredCallableIdentity` is declared in `v1.std.core` +/// on main, the merge base and head both carry it, no run can produce these deltas, and all +/// four report stale -- which refuses every unrelated PR in the repository, exactly the shape +/// the three shrinks above record. Remove them by that trigger, not by reinterpreting it. +pub const NAMESPACE_TRANSITION_ADMISSIONS: &[TransitionAdmission] = &[ + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_lookup", + in_declaration: "borrowed_census_callable_candidate", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_lookup", + in_declaration: "callable_lookup_over_candidates", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_lookup", + in_declaration: "func_sig_from_global_bare", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_lookup", + in_declaration: "resolved_declaration_call_target", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + // THE DECLARING MODULE REBINDS TOO, which the first four rows missed. `v1.compiler.infer_sigs` + // used to DECLARE `DeclaredCallableIdentity`, so its own two construction sites resolved + // locally and produced no delta; now that the declaration lives in `v1.std.core` and this + // module imports it, those sites rebind exactly like the consumers in `infer_lookup`. Missing + // them was an enumeration error on my part, not a second transition: same subject, same + // trigger, same dissolve. + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_sigs", + in_declaration: "lookup_resolved_sig", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: "DeclaredCallableIdentity hoist to v1.std.core 2026-08-29", + subject: AdmissionSubject::Binding { + module: "v1.compiler.infer_sigs", + in_declaration: "parent_closure_callable_candidates", + spelling: "DeclaredCallableIdentity", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, +]; /// The denominators a green must name (DESIGN §5). A run that cannot say what it covered is /// an instrument failure wearing coverage's clothes. diff --git a/src/v1/stage0/src/std_nat.rs b/src/v1/stage0/src/std_nat.rs index 19188c278f1..796b083f66c 100644 --- a/src/v1/stage0/src/std_nat.rs +++ b/src/v1/stage0/src/std_nat.rs @@ -42,11 +42,11 @@ pub fn nat_min(a: Nat, b: Nat) -> Nat { } } -pub fn nat_range_inclusive(lo: Nat, hi: Nat) -> Rc> { +pub fn nat_range_inclusive(lo: Nat, hi: Nat) -> Rc> { nat_range_accumulate(lo.clone(), hi.clone(), Rc::new(vec![])) } -pub fn nat_range_accumulate(mut lo: Nat, mut hi: Nat, mut acc: Rc>) -> Rc> { +pub fn nat_range_accumulate(mut lo: Nat, mut hi: Nat, mut acc: Rc>) -> Rc> { loop { if (lo.clone() > hi.clone()) { break acc; diff --git a/src/v1/stage0/src/std_primitive_projection.rs b/src/v1/stage0/src/std_primitive_projection.rs index dbc97abfbea..ecf7cc5961e 100644 --- a/src/v1/stage0/src/std_primitive_projection.rs +++ b/src/v1/stage0/src/std_primitive_projection.rs @@ -191,6 +191,33 @@ pub fn primitive_concept_decl_facts_live() -> Rc { CACHED.with(|c: &Rc| c.clone()) } +pub fn primitive_symbol_lexeme() -> Rc { + thread_local! { + static CACHED: Rc = { + primitive_identity_slug("symbol_lexeme".to_string()) + }; + } + CACHED.with(|c: &Rc| c.clone()) +} + +pub fn primitive_symbol_intern_lexeme() -> Rc { + thread_local! { + static CACHED: Rc = { + primitive_identity_slug("symbol_intern_lexeme".to_string()) + }; + } + CACHED.with(|c: &Rc| c.clone()) +} + +pub fn symbol_lexeme_seam_disposition_note() -> String { + thread_local! { + static CACHED: String = { + "THE TWO SYMBOL BRIDGES WERE HOST SEAMS THAT NOTHING SAID WERE HOST SEAMS, and the cost of that silence is why they are rostered here rather than left alone. v2.std.compilers.lexing symbol_lexeme and symbol_intern_lexeme have self-call bodies -- the HostRealizedSeam shape exactly, correct by construction because reaching one recurses to the evaluation-budget refusal -- and the interpreter has carried real arms for both since they were written (gunbc.v1_interpreter_primitive_surface v4_bridge.symbol_lexeme, v4_bridge.symbol_intern_lexeme). But with no roster row the RESOLVER saw an ordinary declaration, so Rust emission emitted the declaration, and `pub fn symbol_lexeme(sym: String) -> String { symbol_lexeme(sym) }` COMPILES. The emitted v2 compiler closure therefore carried two functions that type-check, pass every gate we own, and diverge from the interpreter by not terminating -- the DESIGN section 5 fabricated-plausible-output failure in its quietest form, since unlike the sibling seams (decl_facts and friends, which at least refuse loudly as unresolved v1_rt symbols) nothing anywhere reports this one.\n\nWHAT MAKES THE RUST REALIZATION HONEST RATHER THAN A SHIM: extdeps.languages.rust.types already declares Symbol's target type as String, so on that target the interning table is the identity and both bridges ARE identity functions. That is a realization of the declared row, not a second opinion about it; a target where Symbol is NOT String would need a different realization and the registry is where it would say so. The interpreter's arms and the Rust registry rows now answer for the same two primitives, which is the agreement that was missing.\n\nTHE RESIDUE IS NAMED, NOT CLOSED: a self-call body is a DECIDABLE structural marker of a host seam, so the compiler could refuse an unrealized one instead of emitting it. It does not yet, and until it does this roster is the only thing standing between a new v4_bridge declaration and another silently nonterminating emission. That check is this class's next-rung trigger.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + pub fn primitive_projection_roster_note() -> String { thread_local! { static CACHED: String = { @@ -251,6 +278,18 @@ pub fn primitive_projection_roster() -> Rc>> { "empty_map_primitive_delegate".to_string(), Rc::new(ProjectionFidelity::HostRealizedSeam), ), + primitive_projection_row( + primitive_symbol_lexeme(), + "v2.std.compilers.lexing".to_string(), + "symbol_lexeme".to_string(), + Rc::new(ProjectionFidelity::HostRealizedSeam), + ), + primitive_projection_row( + primitive_symbol_intern_lexeme(), + "v2.std.compilers.lexing".to_string(), + "symbol_intern_lexeme".to_string(), + Rc::new(ProjectionFidelity::HostRealizedSeam), + ), primitive_projection_row( primitive_length(), "v2.std.algebra".to_string(), diff --git a/src/v1/stage0/src/v1_compiler_compile.rs b/src/v1/stage0/src/v1_compiler_compile.rs index 14f64102b59..ed809719284 100644 --- a/src/v1/stage0/src/v1_compiler_compile.rs +++ b/src/v1/stage0/src/v1_compiler_compile.rs @@ -752,10 +752,7 @@ pub fn var_binding_kind_name(value: Rc) -> String { pub fn serialize_call_target_identity(value: Rc) -> String { match (*value.clone()).clone() { - CallTargetIdentity::RuntimePrimitiveCall { - primitive_name: primitive_name, - .. - } => v1_rt::concat( + CallTargetIdentity::RuntimePrimitiveCall { primitive_name, .. } => v1_rt::concat( v1_rt::concat( "{\"kind\": \"RuntimePrimitiveCall\", \"primitive_name\": ".to_string(), crate::v1_compiler_dag_collect_support::json_quote(primitive_name.clone()), diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index e2eb1fd25a5..d94b5921a40 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -37,12 +37,17 @@ pub use crate::gunbc_stage0_emitted_population_manifest::{ pub use crate::std_algebra::trim; pub use crate::std_coercion::TypeCheckpoint; pub use crate::std_content_hash::Fnv1a64Structural; +pub use crate::std_decl_ref::decl_ref; +pub use crate::std_decl_ref::DeclarationRef; use crate::std_induction::SubValueRelation::SubValueUnknown; pub use crate::std_induction::{InductiveField, SubValueRelation}; pub use crate::std_measure::millisecond_count; pub use crate::std_nat::Nat; pub use crate::std_occurrence_identity::NodeOccurrenceIdentity; use crate::std_occurrence_identity::NodeOccurrenceIdentity::OccurrenceSynthetic; +pub use crate::std_primitive_projection::{ + primitive_identity_runtime_name, primitive_projection_row_for_declaration, +}; use crate::std_serialization::VariantEncoding::*; use crate::std_serialization::VariantNaming::*; pub use crate::std_serialization::{CoproductWireContract, VariantEncoding, VariantNaming}; @@ -59,7 +64,7 @@ pub use crate::v1_compiler_closure_stub_v2_std_text_rust::closure_stub_v2_std_te pub use crate::v1_compiler_coercion::decl_identity_file; pub use crate::v1_compiler_coercion::{ coerce_primitive_type, is_copy, lookup_checkpoint, rust_seed_host_numeric_alias, - type_reference_decl_file, + target_callable, type_reference_decl_file, }; pub use crate::v1_compiler_compiler_tests_rust::compiler_tests_source; use crate::v1_compiler_emit::BoundOperation::{ @@ -179,7 +184,7 @@ use crate::v1_std_core::CompilerDiagnostic::{ InternalError, ParameterDefaultFormNotAdmitted, ReferenceDerivedImportExportUnproven, ReferenceDerivedImportProviderUnknown, UnlistedImportUse, }; -use crate::v1_std_core::Connective::{Conj, Disj, NoConnective}; +use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::ExprData::{ ExprBinOp, ExprBlock, ExprCall, ExprCast, ExprError, ExprFieldAccess, ExprForEach, ExprIf, ExprIndex, ExprLambda, ExprLet, ExprListLit, ExprLiteral, ExprMatch, ExprMethodCall, @@ -221,9 +226,10 @@ pub use crate::v1_std_core::{ transport_stdin, transport_tls_posture, tuple_type_name, with_required_cardinality, }; pub use crate::v1_std_core::{ - CallSemantics, CallTargetIdentity, Cardinality, CompilerDiagnostic, Connective, ErrorNode, - ExprData, FieldAccessStyle, FieldSummary, FieldValueShape, InferredNode, MatchPattern, - MethodSemantics, NewlineIndex, Node, StringPart, TextFile, UnaryOpKind, VarBindingKind, + CallSemantics, CallTargetIdentity, Cardinality, CompilerDiagnostic, Connective, + DeclaredCallableIdentity, ErrorNode, ExprData, FieldAccessStyle, FieldSummary, FieldValueShape, + InferredNode, MatchPattern, MethodSemantics, NewlineIndex, Node, StringPart, TextFile, + UnaryOpKind, VarBindingKind, }; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; @@ -313,6 +319,90 @@ pub fn render_rust_type( } } +pub fn render_rust_arrow_position( + n: Rc, + shared_types: Rc>, + source_indices: Rc>>, + emit_info: Rc, +) -> String { + render_rust_fn_sig_type( + n.clone(), + emit_info.fn_generic_param_names.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.variant_to_enum.clone(), + emit_info.fn_type_env.clone(), + ) +} + +pub fn rust_type_node_is_arrow(n: Rc) -> bool { + (n.connective.clone() == Connective::Arrow) +} + +pub fn render_rust_arrow_type( + n: Rc, + shared_types: Rc>, + source_indices: Rc>>, + emit_info: Rc, +) -> String { + { + let repr = crate::v1_compiler_coercion::target_callable(RenderTarget::Rust); + let param_strs = Rc::new({ + let mut __result = Vec::new(); + for p in n.params.clone().iter().cloned() { + __result.push(render_rust_arrow_position( + crate::v1_std_core::param_node_type_expr(p.clone()), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + )); + } + __result + }); + let param_str = param_strs.clone().join(&repr.param_separator.clone()); + let spec = crate::v1_compiler_emit_core_support::language_spec(RenderTarget::Rust); + let ret_str = match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: rt, .. }) => render_rust_arrow_position( + rt.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ), + _ => spec.void_type.clone(), + }; + match spec.callable_type_template.clone() { + Some(t) => v1_rt::replace( + v1_rt::replace(t.clone(), "{params}".to_string(), param_str.clone()), + "{return}".to_string(), + ret_str.clone(), + ), + None => { + if (ret_str.clone() == "".to_string()) { + v1_rt::replace( + v1_rt::replace( + repr.template.clone(), + v1_rt::concat(repr.return_separator.clone(), "{return}".to_string()), + "".to_string(), + ), + "{params}".to_string(), + param_str.clone(), + ) + } else { + v1_rt::replace( + v1_rt::replace( + repr.template.clone(), + "{params}".to_string(), + param_str.clone(), + ), + "{return}".to_string(), + ret_str.clone(), + ) + } + } + } + } +} + pub fn render_rust_type_without_applied_binding( n: Rc, shared_types: Rc>, @@ -336,121 +426,49 @@ pub fn render_rust_type_without_applied_binding( if is_host_diagnostics_carrier_type(n.clone(), source_indices.clone()) { return render_rust_diagnostics_carrier_applied(shared_types.clone()); } - if (((n.connective.clone() == Connective::NoConnective) - && ((n.children.clone().len() as i64) > 0)) - && !crate::std_types::is_container_type(crate::v1_std_core::authored_name_at( - source_indices.clone(), - n.clone(), - ))) - { - render_rust_applied_via_emit_info( + if rust_type_node_is_arrow(n.clone()) { + render_rust_arrow_type( n.clone(), shared_types.clone(), source_indices.clone(), emit_info.clone(), ) } else { - if crate::v1_compiler_infer_types::node_is_set_collection( - n.clone(), - source_indices.clone(), - ) { - match n.children.clone().first().cloned() { - Some(elem_child) => { - let elem_node = - crate::v1_compiler_infer_types::child_type_node(elem_child.clone()); - let elem_is_type_var = if (elem_node.inferred.clone() != None) { - is_type_variable(elem_node.inferred.clone().clone().unwrap()) - } else { - false - }; - if elem_is_type_var.clone() { - emit_rust_compile_error_expr("Set element type unresolved".to_string()) - } else { - if !rust_btree_set_element_ord_eligible( - elem_node.clone(), - source_indices.clone(), - emit_info.clone(), - ) { - { - let elem_name = crate::v1_std_core::authored_name_at( - source_indices.clone(), - elem_node.clone(), - ); - emit_rust_compile_error_expr(v1_rt::concat( - v1_rt::concat( - "Set element type ".to_string(), - elem_name.clone(), - ), - " is not Ord-eligible for BTreeSet".to_string(), - )) - } - } else { - crate::v1_compiler_emit::render_node_type( - n.clone(), - RenderTarget::Rust, - shared_types.clone(), - source_indices.clone(), - ) - } - } - } - None => emit_rust_compile_error_expr("Set missing element type".to_string()), + if ((((n.connective.clone() == Connective::NoConnective) + && ((n.children.clone().len() as i64) > 0)) + && !crate::std_types::is_container_type(crate::v1_std_core::authored_name_at( + source_indices.clone(), + n.clone(), + ))) + && closed_alias_peels_zero_param(emit_info.fn_type_env.clone(), n.clone())) + { + { + let leaf = crate::v1_std_core::qualified_last_segment( + crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()), + ); + render_rust_shared_type_with_optional( + n.clone(), + leaf.clone(), + leaf.clone(), + shared_types.clone(), + ) } } else { - if crate::v1_compiler_infer_types::node_is_keyed_collection( - n.clone(), - source_indices.clone(), - ) { - match n.children.clone().first().cloned() { - Some(key_child) => { - let key_node = - crate::v1_compiler_infer_types::child_type_node(key_child.clone()); - let val_node = - match n.children.clone().iter().cloned().skip(1 as usize).next() { - Some(val_child) => { - crate::v1_compiler_infer_types::child_type_node( - val_child.clone(), - ) - } - None => type_variable_node("map_value".to_string()), - }; - let key_str = render_rust_type( - key_node.clone(), - shared_types.clone(), - source_indices.clone(), - emit_info.clone(), - ); - let val_str = render_rust_type( - val_node.clone(), - shared_types.clone(), - source_indices.clone(), - emit_info.clone(), - ); - let tn = crate::v1_std_core::authored_name_at( - source_indices.clone(), - n.clone(), - ); - let base = crate::v1_compiler_emit::emit_map_type( - key_str.clone(), - val_str.clone(), - RenderTarget::Rust, - ); - render_rust_shared_type_with_optional( - n.clone(), - tn.clone(), - base.clone(), - shared_types.clone(), - ) - } - None => crate::v1_compiler_emit::render_node_type( - n.clone(), - RenderTarget::Rust, - shared_types.clone(), - source_indices.clone(), - ), - } + if (((n.connective.clone() == Connective::NoConnective) + && ((n.children.clone().len() as i64) > 0)) + && !crate::std_types::is_container_type(crate::v1_std_core::authored_name_at( + source_indices.clone(), + n.clone(), + ))) + { + render_rust_applied_via_emit_info( + n.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ) } else { - if crate::v1_compiler_infer_types::node_is_element_collection( + if crate::v1_compiler_infer_types::node_is_set_collection( n.clone(), source_indices.clone(), ) { @@ -459,60 +477,178 @@ pub fn render_rust_type_without_applied_binding( let elem_node = crate::v1_compiler_infer_types::child_type_node( elem_child.clone(), ); - let inner_str = render_rust_type( - elem_node.clone(), - shared_types.clone(), - source_indices.clone(), - emit_info.clone(), - ); - let tn = crate::v1_std_core::authored_name_at( - source_indices.clone(), + let elem_is_type_var = if (elem_node.inferred.clone() != None) { + is_type_variable(elem_node.inferred.clone().clone().unwrap()) + } else { + false + }; + if elem_is_type_var.clone() { + emit_rust_compile_error_expr( + "Set element type unresolved".to_string(), + ) + } else { + if !rust_btree_set_element_ord_eligible( + elem_node.clone(), + source_indices.clone(), + emit_info.clone(), + ) { + { + let elem_name = crate::v1_std_core::authored_name_at( + source_indices.clone(), + elem_node.clone(), + ); + emit_rust_compile_error_expr(v1_rt::concat( + v1_rt::concat( + "Set element type ".to_string(), + elem_name.clone(), + ), + " is not Ord-eligible for BTreeSet".to_string(), + )) + } + } else { + crate::v1_compiler_emit::render_node_type( + n.clone(), + RenderTarget::Rust, + shared_types.clone(), + source_indices.clone(), + ) + } + } + } + None => { + emit_rust_compile_error_expr("Set missing element type".to_string()) + } + } + } else { + if crate::v1_compiler_infer_types::node_is_keyed_collection( + n.clone(), + source_indices.clone(), + ) { + match n.children.clone().first().cloned() { + Some(key_child) => { + let key_node = crate::v1_compiler_infer_types::child_type_node( + key_child.clone(), + ); + let val_node = match n + .children + .clone() + .iter() + .cloned() + .skip(1 as usize) + .next() + { + Some(val_child) => { + crate::v1_compiler_infer_types::child_type_node( + val_child.clone(), + ) + } + None => type_variable_node("map_value".to_string()), + }; + let key_str = render_rust_type( + key_node.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ); + let val_str = render_rust_type( + val_node.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ); + let tn = crate::v1_std_core::authored_name_at( + source_indices.clone(), + n.clone(), + ); + let base = crate::v1_compiler_emit::emit_map_type( + key_str.clone(), + val_str.clone(), + RenderTarget::Rust, + ); + render_rust_shared_type_with_optional( + n.clone(), + tn.clone(), + base.clone(), + shared_types.clone(), + ) + } + None => crate::v1_compiler_emit::render_node_type( n.clone(), - ); - let base = crate::v1_compiler_emit::emit_container( - tn.clone(), - inner_str.clone(), RenderTarget::Rust, - ); - render_rust_shared_type_with_optional( - n.clone(), - tn.clone(), - base.clone(), shared_types.clone(), - ) + source_indices.clone(), + ), } - None => crate::v1_compiler_emit::render_node_type( - n.clone(), - RenderTarget::Rust, - shared_types.clone(), - source_indices.clone(), - ), - } - } else { - { - let tn = crate::v1_std_core::authored_name_at( - source_indices.clone(), - n.clone(), - ); - if type_node_has_unbound_type_variable( + } else { + if crate::v1_compiler_infer_types::node_is_element_collection( n.clone(), - emit_info.fn_generic_param_names.clone(), source_indices.clone(), ) { - "_".to_string() - } else { - if is_host_text_carrier_type(n.clone(), source_indices.clone()) { - rust_carrier_optional_wrap( - n.clone(), - render_rust_text_carrier(shared_types.clone()), - ) - } else { - crate::v1_compiler_emit::render_node_type( + match n.children.clone().first().cloned() { + Some(elem_child) => { + let elem_node = + crate::v1_compiler_infer_types::child_type_node( + elem_child.clone(), + ); + let inner_str = render_rust_type( + elem_node.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ); + let tn = crate::v1_std_core::authored_name_at( + source_indices.clone(), + n.clone(), + ); + let base = crate::v1_compiler_emit::emit_container( + tn.clone(), + inner_str.clone(), + RenderTarget::Rust, + ); + render_rust_shared_type_with_optional( + n.clone(), + tn.clone(), + base.clone(), + shared_types.clone(), + ) + } + None => crate::v1_compiler_emit::render_node_type( n.clone(), RenderTarget::Rust, shared_types.clone(), source_indices.clone(), - ) + ), + } + } else { + { + let tn = crate::v1_std_core::authored_name_at( + source_indices.clone(), + n.clone(), + ); + if type_node_has_unbound_type_variable( + n.clone(), + emit_info.fn_generic_param_names.clone(), + source_indices.clone(), + ) { + "_".to_string() + } else { + if is_host_text_carrier_type( + n.clone(), + source_indices.clone(), + ) { + rust_carrier_optional_wrap( + n.clone(), + render_rust_text_carrier(shared_types.clone()), + ) + } else { + crate::v1_compiler_emit::render_node_type( + n.clone(), + RenderTarget::Rust, + shared_types.clone(), + source_indices.clone(), + ) + } + } } } } @@ -1114,6 +1250,11 @@ pub fn rust_witness_carrier_from_type_node( source_indices: Rc>>, ) -> Option { { + if ((type_node.inferred.clone() != None) + && crate::v1_std_core::is_compiler_error(type_node.inferred.clone().clone().unwrap())) + { + return None; + } if type_node_has_unbound_type_variable( type_node.clone(), emit_info.fn_generic_param_names.clone(), @@ -2198,8 +2339,9 @@ if peel.clone() { ) { "_".to_string() } else { - render_rust_type_with_applied_binding( + render_rust_type_with_applied_binding_in_scope( n.clone(), + generic_param_names.clone(), shared_types.clone(), source_indices.clone(), env.clone(), @@ -2851,7 +2993,11 @@ pub fn render_rust_alias_rhs_type( n.clone(), shared_types.clone(), source_indices.clone(), - crate::v1_compiler_infer_emit_info::empty_emit_graph_info(), + crate::v1_compiler_infer_emit_info::emit_info_with_fn_type_context( + crate::v1_compiler_infer_emit_info::empty_emit_graph_info(), + generic_param_names.clone(), + scope.type_env.clone(), + ), ) } } @@ -7893,8 +8039,11 @@ pub fn reference_derived_candidate_disposition( module_index: Rc, type_summaries: Rc>>, variant_to_enum: Rc>, + in_type_position: bool, ) -> Rc { - if crate::v1_compiler_infer_emit_info::is_known_variant(type_summaries.clone(), name.clone()) { + if (crate::v1_compiler_infer_emit_info::is_known_variant(type_summaries.clone(), name.clone()) + && !in_type_position.clone()) + { match v1_rt::map_get(&variant_to_enum, name.clone()) { Some(parent) => { if (parent.clone() == "".to_string()) { @@ -8192,6 +8341,12 @@ pub fn reference_derived_use_line_plan( }); let type_surface_names = crate::v1_compiler_emit_core_support::unique_strings(item_emit_surface_names.clone()); + let type_position_set = type_surface_names.iter().cloned().fold( + v1_rt::rc_empty_map::(), + |acc: Rc>, nm: String| { + v1_rt::rc_map_insert(acc, nm.clone(), true) + }, + ); let field_surface_names = collect_items_field_import_surface_names( item_emit_surface_names.clone(), emit_info.clone(), @@ -8367,6 +8522,10 @@ pub fn reference_derived_use_line_plan( module_index.clone(), emit_info.type_summaries.clone(), emit_info.variant_to_enum.clone(), + crate::v1_compiler_infer_types::emit_map_has( + type_position_set.clone(), + name.clone(), + ), ), })]) }) @@ -8423,7 +8582,7 @@ let block_lines = if (block.clone() == "".to_string()) { Rc::new(block.clone().split(&"\n".to_string()).map(|s| s.to_string()).collect::>()) }; let emitted_here = Rc::new({ let mut __result = Vec::new(); for l in block_lines.iter().cloned() { __result.extend((*imported_names_in_use_line(l.clone())).iter().cloned()); } __result }); -let fallback = Rc::new({ let mut __result = Vec::new(); for nm in names.iter().cloned() { __result.extend((*if ({ let mut __found = false; for e in emitted_here.iter().cloned() { if (e.clone() == nm.clone()) { __found = true; break; } } __found } || crate::v1_compiler_infer_emit_info::is_known_variant(emit_info.type_summaries.clone(), nm.clone())) { +let fallback = Rc::new({ let mut __result = Vec::new(); for nm in names.iter().cloned() { __result.extend((*if ({ let mut __found = false; for e in emitted_here.iter().cloned() { if (e.clone() == nm.clone()) { __found = true; break; } } __found } || (crate::v1_compiler_infer_emit_info::is_known_variant(emit_info.type_summaries.clone(), nm.clone()) && !crate::v1_compiler_infer_types::emit_map_has(type_position_set.clone(), nm.clone()))) { Rc::new(vec![]) } else { if provider_proven_exports_symbol(nm.clone(), provider.clone(), export_sets.clone(), typed_modules.clone(), source_indices.clone(), module_index.clone()) { @@ -8437,13 +8596,205 @@ v1_rt::concat(block_lines.clone(), fallback.clone()) } __result }); + let names_already_in_lines = Rc::new({ + let mut __result = Vec::new(); + for l in lines.iter().cloned() { + __result.extend((*imported_names_in_use_line(l.clone())).iter().cloned()); + } + __result + }); + let qualified_rows = qualified_type_reference_rows( + Rc::new({ + let mut __result = Vec::new(); + for nm in type_surface_names.iter().cloned() { + if crate::v1_compiler_infer_types::emit_map_has( + emitted_source_token_set.clone(), + crate::v1_std_core::qualified_last_segment(nm.clone()), + ) { + __result.push(nm); + } + } + __result + }), + this_module_name.clone(), + already.clone(), + names_already_in_lines.clone(), + registry.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ); + let qualified_lines = + qualified_type_reference_use_lines(qualified_rows.clone(), registry.clone()); Rc::new(ReferenceDerivedUseLinePlan { - lines: lines.clone(), - rows: rows.clone(), + lines: v1_rt::concat(lines.clone(), qualified_lines.clone()), + rows: v1_rt::concat(rows.clone(), qualified_rows.clone()), + }) + } +} + +pub fn qualified_type_reference_rows( + names: Rc>, + this_module_name: String, + already: Rc>, + names_already_in_lines: Rc>, + registry: Rc>>, + export_sets: Rc>>>, + typed_modules: Rc>>, + source_indices: Rc>>, + module_index: Rc, +) -> Rc>> { + { + let leaves_done = names_already_in_lines.iter().cloned().fold( + v1_rt::rc_empty_map::(), + |acc: Rc>, nm: String| { + v1_rt::rc_map_insert(acc, nm.clone(), true) + }, + ); + Rc::new({ + let mut __result = Vec::new(); + for name in names.iter().cloned() { + __result.extend( + (*if !v1_rt::string_contains(&name, ".".to_string()) { + Rc::new(vec![]) + } else { + { + let leaf = crate::v1_std_core::qualified_last_segment(name.clone()); + let segments = Rc::new( + name.clone() + .split(&".".to_string()) + .map(|s| s.to_string()) + .collect::>(), + ); + let qualifier = Rc::new( + segments + .clone() + .iter() + .cloned() + .take(((segments.clone().len() as i64) - 1) as usize) + .collect::>(), + ) + .join(&".".to_string()); + if ((((qualifier.clone() == this_module_name.clone()) + || crate::v1_compiler_infer_types::emit_map_has( + already.clone(), + leaf.clone(), + )) + || crate::v1_compiler_infer_types::emit_map_has( + leaves_done.clone(), + leaf.clone(), + )) + || crate::std_types::is_kernel_type(leaf.clone())) + { + Rc::new(vec![]) + } else { + match v1_rt::map_get(®istry, leaf.clone()) { + None => Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: this_module_name.clone(), + name: name.clone(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), +})]), + Some(info) => { + if !registry_row_is_type_declared_in( + info.clone(), + qualifier.clone(), + ) { + Rc::new(vec![]) + } else { + if provider_proven_exports_symbol( + leaf.clone(), + qualifier.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) { + Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: this_module_name.clone(), + name: name.clone(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: qualifier.clone(), +}), +})]) + } else { + Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: this_module_name.clone(), + name: name.clone(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: qualifier.clone(), +}), +})]) + } + } + } + } + } + } + }) + .iter() + .cloned(), + ); + } + __result }) } } +pub fn registry_row_is_type_declared_in(info: Rc, module_name: String) -> bool { + if (info.module_name.clone() != module_name.clone()) { + false + } else { + match info.kind.clone() { + ItemKind::TypeItem => true, + _ => false, + } + } +} + +pub fn qualified_type_reference_use_lines( + rows: Rc>>, + registry: Rc>>, +) -> Rc> { + crate::v1_compiler_emit_core_support::unique_strings(Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + __result.extend( + (*match (*r.disposition.clone()).clone() { + ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: provider, + .. + } => Rc::new(vec![v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + rust_visibility_prefix(), + "use crate::".to_string(), + ), + crate::v1_compiler_emit_core_support::module_to_filename( + provider.clone(), + ), + ), + "::".to_string(), + ), + emit_import_name( + crate::v1_std_core::qualified_last_segment(r.name.clone()), + registry.clone(), + ), + ), + ";".to_string(), + )]), + _ => Rc::new(vec![]), + }) + .iter() + .cloned(), + ); + } + __result + })) +} + pub fn reference_derived_use_lines( items: Rc>>, unlisted_type_names: Rc>, @@ -14353,6 +14704,22 @@ pub fn render_rust_type_with_applied_binding( shared_types: Rc>, source_indices: Rc>>, env: Rc, +) -> String { + render_rust_type_with_applied_binding_in_scope( + n.clone(), + Rc::new(vec![]), + shared_types.clone(), + source_indices.clone(), + env.clone(), + ) +} + +pub fn render_rust_type_with_applied_binding_in_scope( + n: Rc, + generic_param_names: Rc>, + shared_types: Rc>, + source_indices: Rc>>, + env: Rc, ) -> String { { if is_host_text_carrier_type(n.clone(), source_indices.clone()) { @@ -14386,7 +14753,7 @@ pub fn render_rust_type_with_applied_binding( } else { render_rust_applied_type_shared( applied.clone(), - Rc::new(vec![]), + generic_param_names.clone(), shared_types.clone(), source_indices.clone(), v1_rt::rc_empty_map::(), @@ -14396,7 +14763,7 @@ pub fn render_rust_type_with_applied_binding( } else { render_rust_applied_type_shared( applied.clone(), - Rc::new(vec![]), + generic_param_names.clone(), shared_types.clone(), source_indices.clone(), v1_rt::rc_empty_map::(), @@ -14411,7 +14778,7 @@ pub fn render_rust_type_with_applied_binding( source_indices.clone(), crate::v1_compiler_infer_emit_info::emit_info_with_fn_type_context( crate::v1_compiler_infer_emit_info::empty_emit_graph_info(), - Rc::new(vec![]), + generic_param_names.clone(), env.clone(), ), ) @@ -14423,7 +14790,7 @@ pub fn render_rust_type_with_applied_binding( source_indices.clone(), crate::v1_compiler_infer_emit_info::emit_info_with_fn_type_context( crate::v1_compiler_infer_emit_info::empty_emit_graph_info(), - Rc::new(vec![]), + generic_param_names.clone(), env.clone(), ), ), @@ -16109,6 +16476,46 @@ pub fn v1_call_site_equality_forwarded_param_names( } } +pub fn rust_body_is_bare_self_call( + name: String, + body: Rc, + source_indices: Rc>>, +) -> bool { + match (*body.expr_data.clone()).clone() { + ExprData::ExprCall { .. } => { + (crate::v1_std_core::qualified_last_segment(crate::v1_std_core::expr_call_func_at( + body.clone(), + source_indices.clone(), + )) == name.clone()) + } + _ => false, + } +} + +pub fn rust_seam_primitive_name(module_path: String, name: String) -> String { + match crate::std_primitive_projection::primitive_projection_row_for_declaration( + crate::std_decl_ref::decl_ref(module_path.clone(), name.clone()), + ) { + Some(row) => { + crate::std_primitive_projection::primitive_identity_runtime_name(row.primitive.clone()) + } + None => name.clone(), + } +} + +pub fn rust_host_seam_is_unrealized( + module_path: String, + name: String, + body: Rc, + source_indices: Rc>>, +) -> bool { + (rust_body_is_bare_self_call(name.clone(), body.clone(), source_indices.clone()) + && (rust_runtime_primitive_has_bridge(rust_seam_primitive_name( + module_path.clone(), + name.clone(), + )) == false)) +} + pub fn emit_fn_def( name: String, params: Rc>>, @@ -16278,6 +16685,17 @@ pub fn emit_fn_def( registry.clone(), si.clone(), ) && (use_tco.clone() == false)); + if rust_host_seam_is_unrealized( + scope.module_name.clone(), + name.clone(), + body.clone(), + si.clone(), + ) { + { + let kw = rust_items().func_keyword.clone(); + return v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(rust_visibility_prefix(), kw.clone()), " ".to_string()), crate::v1_compiler_emit::emit_ident(name.clone(), RenderTarget::Rust)), type_params_str.clone()), "(".to_string()), params_str.clone()), ")".to_string()), ret_str.clone()), " {\n".to_string()), crate::v1_compiler_emit_core_support::make_indent(1)), "panic!(\"unrealized host seam '".to_string()), name.clone()), "' in ".to_string()), scope.module_name.clone()), ": this declaration's whole body is a self-call, so it has no behaviour on this target. ".to_string()), "It compiles as a refusal rather than as recursion. Give it a row in ".to_string()), "extdeps.languages.rust.emit rt_function_registry and a body in v1.runtime_rust, or delete its consumers.\")\n}".to_string()); + } + } if use_tco.clone() { { let tco_params_str = emit_tco_params( @@ -20676,10 +21094,9 @@ pub fn call_semantics_target(cs: Option>) -> Rc) -> Option { match (*target.clone()).clone() { - CallTargetIdentity::RuntimePrimitiveCall { - primitive_name: primitive_name, - .. - } => Some(primitive_name.clone()), + CallTargetIdentity::RuntimePrimitiveCall { primitive_name, .. } => { + Some(primitive_name.clone()) + } CallTargetIdentity::SourceDeclarationCall { .. } => None, CallTargetIdentity::CallableTargetUndetermined => None, } @@ -20689,6 +21106,23 @@ pub fn call_target_is_runtime_primitive(target: Rc) -> bool (call_target_runtime_primitive_name(target.clone()) != None) } +pub fn call_target_projected_declaration( + target: Rc, +) -> Option> { + match (*target.clone()).clone() { + CallTargetIdentity::RuntimePrimitiveCall { projected_from, .. } => projected_from.clone(), + CallTargetIdentity::SourceDeclarationCall { .. } => None, + CallTargetIdentity::CallableTargetUndetermined => None, + } +} + +pub fn rust_runtime_primitive_has_bridge(primitive_name: String) -> bool { + v1_rt::map_contains_key( + &crate::extdeps_languages_rust_emit::rt_functions(), + primitive_name.clone(), + ) +} + pub fn emit_rust_expr_call( expr: Rc, registry: Rc>>, @@ -22234,7 +22668,8 @@ pub fn emit_typed_call( Some(primitive_name) => primitive_name.clone(), None => "".to_string(), }; - let is_rt = ((callee_is_function_value.clone() == false) && target_is_runtime.clone()); + let is_rt = (((callee_is_function_value.clone() == false) && target_is_runtime.clone()) + && rust_runtime_primitive_has_bridge(runtime_primitive_name.clone())); let is_rt_ref_map = (is_rt.clone() && v1_rt::map_contains_key( &crate::extdeps_languages_rust_emit::rt_ref_map_functions(), @@ -22398,11 +22833,33 @@ pub fn emit_typed_call( ) } CallTargetIdentity::RuntimePrimitiveCall { - primitive_name: _, .. - } => crate::v1_compiler_emit::emit_error_expr( - "runtime call target lost its bridge identity".to_string(), - RenderTarget::Rust, - ), + primitive_name: unbridged_primitive, + projected_from, + .. + } => { + match projected_from.clone() { + Some(declared) => { + if (declared.owner_module_path.clone() == scope.module_name.clone()) { + crate::v1_compiler_emit::emit_ident( + declared.decl_name.clone(), + RenderTarget::Rust, + ) + } else { + v1_rt::concat(v1_rt::concat(v1_rt::concat("crate::".to_string(), crate::v1_compiler_emit_core_support::module_to_filename(declared.owner_module_path.clone())), "::".to_string()), crate::v1_compiler_emit::emit_ident(declared.decl_name.clone(), RenderTarget::Rust)) + } + } + None => crate::v1_compiler_emit::emit_error_expr( + v1_rt::concat( + v1_rt::concat( + "primitive ".to_string(), + unbridged_primitive.clone(), + ), + " has no v1_rt realization and no declaration to emit".to_string(), + ), + RenderTarget::Rust, + ), + } + } } }; let func_name = if callee_self_capture.clone() { @@ -23083,12 +23540,22 @@ pub fn lambda_param_type_strs( ) { None } else { - Some(render_rust_type( - param_type.clone(), - shared_types.clone(), - source_indices.clone(), - emit_info.clone(), - )) + { + let rendered = render_rust_type( + param_type.clone(), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ); + if rust_fold_rendered_type_has_any_spurious_generic( + rendered.clone(), + emit_info.fn_generic_param_names.clone(), + ) { + None + } else { + Some(rendered.clone()) + } + } } } } @@ -23722,16 +24189,28 @@ pub fn emit_rust_fold_method_call( shared_types.clone(), scope.type_env.clone().source_indices.clone(), ); - if (kv_type_str.clone() != "".to_string()) { - v1_rt::concat( + if ((kv_type_str.clone() != "".to_string()) + && rust_fold_rendered_type_has_any_spurious_generic( v1_rt::concat( - "v1_rt::rc_empty_map::<".to_string(), - kv_type_str.clone(), + v1_rt::concat("<".to_string(), kv_type_str.clone()), + ">".to_string(), ), - ">()".to_string(), - ) + emit_info.fn_generic_param_names.clone(), + )) + { + "v1_rt::rc_empty_map::<_, _>()".to_string() } else { - rust_shared_wrap_ctor("HashMap::new() /* BRIDGE: fold empty_map value type unresolved */".to_string()) + if (kv_type_str.clone() != "".to_string()) { + v1_rt::concat( + v1_rt::concat( + "v1_rt::rc_empty_map::<".to_string(), + kv_type_str.clone(), + ), + ">()".to_string(), + ) + } else { + rust_shared_wrap_ctor("HashMap::new() /* BRIDGE: fold empty_map value type unresolved */".to_string()) + } } } } else { @@ -24711,6 +25190,14 @@ pub fn emit_rust_generic_method_call( } } +pub fn rust_unrealized_service_op_refusal( + service_name: String, + method: String, + module_name: String, +) -> String { + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("panic!(\"unrealized host seam: service op ".to_string(), service_name.clone()), ".".to_string()), method.clone()), " in ".to_string()), module_name.clone()), ": the Rust realization binds a service only through an async fn parameter, and this function declares none\")".to_string()) +} + pub fn emit_typed_method_call( method_call_node: Rc, receiver: Rc, @@ -24731,152 +25218,168 @@ pub fn emit_typed_method_call( op_params, .. } => { - let var_name = - crate::v1_compiler_emit_core_support::service_var_name(svc_name.clone()); - let si = scope.type_env.clone().source_indices.clone(); - let filled_args = fill_op_default_args( - args.clone(), - op_params.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - ); - let optional_param_set = Rc::new({ - let mut __result = Vec::new(); - for p in op_params.iter().cloned() { - if (crate::v1_std_core::param_node_type_expr(p.clone()) - .return_cardinality - .clone() - == Cardinality::CardOptional) - { - __result.push(p); - } - } - __result - }) - .iter() - .cloned() - .fold( - v1_rt::rc_empty_map::(), - |acc: Rc>, p: Rc| { - v1_rt::rc_map_insert( - acc, - crate::v1_std_core::param_node_name_at(p.clone(), si.clone()), - true, - ) - }, - ); - let json_param_set = Rc::new({ - let mut __result = Vec::new(); - for p in op_params.iter().cloned() { - if (crate::v1_compiler_coercion::coerce_primitive_type( - RenderTarget::Rust, - crate::v1_std_core::authored_name_at( - si.clone(), - crate::v1_std_core::param_node_type_expr(p.clone()), - ), - crate::v1_compiler_coercion::type_reference_decl_file( - crate::v1_std_core::param_node_type_expr(p.clone()), - ), - ) == "serde_json::Value".to_string()) - { - __result.push(p); - } - } - __result - }) - .iter() - .cloned() - .fold( - v1_rt::rc_empty_map::(), - |acc: Rc>, p: Rc| { - v1_rt::rc_map_insert( - acc, - crate::v1_std_core::param_node_name_at(p.clone(), si.clone()), - true, - ) - }, - ); - let arg_strs = Rc::new({ - let mut __result = Vec::new(); - for a in filled_args.iter().cloned() { - __result.push({ - let arg_str = emit_typed_expr( - crate::v1_std_core::arg_value(a.clone()), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - 1024, - ); - let aname = match crate::v1_std_core::arg_name_at(a.clone(), si.clone()) - { - Some(n) => n.clone(), - None => "".to_string(), - }; - let param_is_json = match v1_rt::map_get(&json_param_set, aname.clone()) - { - Some(_) => true, - None => false, - }; - let param_is_optional = - match v1_rt::map_get(&optional_param_set, aname.clone()) { - Some(_) => true, - None => false, - }; - let needs_optional = (param_is_optional.clone() - && (is_already_optional( - crate::v1_std_core::arg_value(a.clone()), - emit_info.clone(), - scope.clone(), - ) == false)); - let coerced = if (param_is_json.clone() - && is_string_typed_expr( - crate::v1_std_core::arg_value(a.clone()), - si.clone(), - )) { - v1_rt::concat( - v1_rt::concat( - "serde_json::from_str(&".to_string(), - arg_str.clone(), - ), - ").unwrap()".to_string(), + if !crate::v1_compiler_infer_service::is_typed_service_call_receiver( + receiver.clone(), + scope.type_env.clone().source_indices.clone(), + ) { + rust_unrealized_service_op_refusal( + svc_name.clone(), + method.clone(), + scope.module_name.clone(), + ) + } else { + { + let var_name = crate::v1_compiler_emit_core_support::service_var_name( + svc_name.clone(), + ); + let si = scope.type_env.clone().source_indices.clone(); + let filled_args = fill_op_default_args( + args.clone(), + op_params.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ); + let optional_param_set = Rc::new({ + let mut __result = Vec::new(); + for p in op_params.iter().cloned() { + if (crate::v1_std_core::param_node_type_expr(p.clone()) + .return_cardinality + .clone() + == Cardinality::CardOptional) + { + __result.push(p); + } + } + __result + }) + .iter() + .cloned() + .fold( + v1_rt::rc_empty_map::(), + |acc: Rc>, p: Rc| { + v1_rt::rc_map_insert( + acc, + crate::v1_std_core::param_node_name_at(p.clone(), si.clone()), + true, ) - } else { - arg_str.clone() - }; - if needs_optional.clone() { - v1_rt::concat( - v1_rt::concat("Some(".to_string(), coerced.clone()), - ")".to_string(), + }, + ); + let json_param_set = Rc::new({ + let mut __result = Vec::new(); + for p in op_params.iter().cloned() { + if (crate::v1_compiler_coercion::coerce_primitive_type( + RenderTarget::Rust, + crate::v1_std_core::authored_name_at( + si.clone(), + crate::v1_std_core::param_node_type_expr(p.clone()), + ), + crate::v1_compiler_coercion::type_reference_decl_file( + crate::v1_std_core::param_node_type_expr(p.clone()), + ), + ) == "serde_json::Value".to_string()) + { + __result.push(p); + } + } + __result + }) + .iter() + .cloned() + .fold( + v1_rt::rc_empty_map::(), + |acc: Rc>, p: Rc| { + v1_rt::rc_map_insert( + acc, + crate::v1_std_core::param_node_name_at(p.clone(), si.clone()), + true, ) - } else { - coerced.clone() + }, + ); + let arg_strs = Rc::new({ + let mut __result = Vec::new(); + for a in filled_args.iter().cloned() { + __result.push({ + let arg_str = emit_typed_expr( + crate::v1_std_core::arg_value(a.clone()), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + 1024, + ); + let aname = match crate::v1_std_core::arg_name_at( + a.clone(), + si.clone(), + ) { + Some(n) => n.clone(), + None => "".to_string(), + }; + let param_is_json = + match v1_rt::map_get(&json_param_set, aname.clone()) { + Some(_) => true, + None => false, + }; + let param_is_optional = + match v1_rt::map_get(&optional_param_set, aname.clone()) { + Some(_) => true, + None => false, + }; + let needs_optional = (param_is_optional.clone() + && (is_already_optional( + crate::v1_std_core::arg_value(a.clone()), + emit_info.clone(), + scope.clone(), + ) == false)); + let coerced = if (param_is_json.clone() + && is_string_typed_expr( + crate::v1_std_core::arg_value(a.clone()), + si.clone(), + )) { + v1_rt::concat( + v1_rt::concat( + "serde_json::from_str(&".to_string(), + arg_str.clone(), + ), + ").unwrap()".to_string(), + ) + } else { + arg_str.clone() + }; + if needs_optional.clone() { + v1_rt::concat( + v1_rt::concat("Some(".to_string(), coerced.clone()), + ")".to_string(), + ) + } else { + coerced.clone() + } + }); } + __result }); - } - __result - }); - let args_str = arg_strs.clone().join(&", ".to_string()); - v1_rt::concat( - v1_rt::concat( + let args_str = arg_strs.clone().join(&", ".to_string()); v1_rt::concat( v1_rt::concat( - v1_rt::concat(var_name.clone(), ".".to_string()), - crate::v1_compiler_emit::emit_ident( - method.clone(), - RenderTarget::Rust, + v1_rt::concat( + v1_rt::concat( + v1_rt::concat(var_name.clone(), ".".to_string()), + crate::v1_compiler_emit::emit_ident( + method.clone(), + RenderTarget::Rust, + ), + ), + "(".to_string(), ), - ), - "(".to_string(), - ), - args_str.clone(), - ), - ").await?".to_string(), - ) + args_str.clone(), + ), + ").await?".to_string(), + ) + } + } } MethodSemantics::AlgebraMethodSemantics { method_def, @@ -24899,22 +25402,27 @@ pub fn emit_typed_method_call( } else { method_name_raw.clone() }; - if (method_name.clone() == "fold".to_string()) { - emit_rust_fold_method_call( - method_call_node.clone(), - fold_accumulator_type.clone(), - result_type.clone(), - receiver.clone(), - args.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - ) + let algebra_field_call = rust_callable_field_method_call( + method_name.clone(), + receiver.clone(), + args.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ); + if (algebra_field_call.clone() != None) { + match algebra_field_call.clone() { + Some(field_call) => field_call.clone(), + None => "".to_string(), + } } else { - if (method_name.clone() == "sort_by".to_string()) { - emit_rust_sort_by_method_call( + if (method_name.clone() == "fold".to_string()) { + emit_rust_fold_method_call( + method_call_node.clone(), + fold_accumulator_type.clone(), + result_type.clone(), receiver.clone(), args.clone(), registry.clone(), @@ -24924,8 +25432,8 @@ pub fn emit_typed_method_call( emit_info.clone(), ) } else { - if (method_name.clone() == "map".to_string()) { - emit_rust_map_method_call( + if (method_name.clone() == "sort_by".to_string()) { + emit_rust_sort_by_method_call( receiver.clone(), args.clone(), registry.clone(), @@ -24935,20 +25443,8 @@ pub fn emit_typed_method_call( emit_info.clone(), ) } else { - match Rc::new({ - let mut __result = Vec::new(); - for s in rust_higher_order_methods().iter().cloned() { - if (s.method_name.clone() == method_name.clone()) { - __result.push(s); - } - } - __result - }) - .first() - .cloned() - { - Some(ho_spec) => emit_rust_higher_order_method( - ho_spec.clone(), + if (method_name.clone() == "map".to_string()) { + emit_rust_map_method_call( receiver.clone(), args.clone(), registry.clone(), @@ -24956,21 +25452,33 @@ pub fn emit_typed_method_call( depth.clone(), shared_types.clone(), emit_info.clone(), - ), - None => { - if (method_name.clone() == "get".to_string()) { - emit_rust_get_method_call( - receiver.clone(), - args.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - ) - } else { - if (method_name.clone() == "with".to_string()) { - emit_rust_with_method_call( + ) + } else { + match Rc::new({ + let mut __result = Vec::new(); + for s in rust_higher_order_methods().iter().cloned() { + if (s.method_name.clone() == method_name.clone()) { + __result.push(s); + } + } + __result + }) + .first() + .cloned() + { + Some(ho_spec) => emit_rust_higher_order_method( + ho_spec.clone(), + receiver.clone(), + args.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ), + None => { + if (method_name.clone() == "get".to_string()) { + emit_rust_get_method_call( receiver.clone(), args.clone(), registry.clone(), @@ -24980,15 +25488,10 @@ pub fn emit_typed_method_call( emit_info.clone(), ) } else { - if (method_name.clone() - == "string_contains".to_string()) - { - emit_rust_generic_method_call( - "string_contains".to_string(), + if (method_name.clone() == "with".to_string()) { + emit_rust_with_method_call( receiver.clone(), args.clone(), - result_type.clone(), - true, registry.clone(), scope.clone(), depth.clone(), @@ -24996,58 +25499,26 @@ pub fn emit_typed_method_call( emit_info.clone(), ) } else { - if (method_name.clone() == "concat".to_string()) { - { - let recv_str = emit_typed_expr( - receiver.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - 1024, - ); - let arg_strs = Rc::new({ - let mut __result = Vec::new(); - for a in args.iter().cloned() { - __result.push(emit_typed_expr( - crate::v1_std_core::arg_value( - a.clone(), - ), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - 1024, - )); - } - __result - }); - let all_arg_strs = v1_rt::concat( - Rc::new(vec![recv_str.clone()]), - arg_strs.clone(), - ); - emit_nested_rt_concat( - all_arg_strs.clone(), - "".to_string(), - shared_types.clone(), - ) - } + if (method_name.clone() + == "string_contains".to_string()) + { + emit_rust_generic_method_call( + "string_contains".to_string(), + receiver.clone(), + args.clone(), + result_type.clone(), + true, + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ) } else { - if (method_name.clone() == "first".to_string()) + if (method_name.clone() == "concat".to_string()) { - emit_rust_first_method_call( - receiver.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - ) - } else { { - let recv_str_raw = emit_typed_expr( + let recv_str = emit_typed_expr( receiver.clone(), registry.clone(), scope.clone(), @@ -25056,35 +25527,76 @@ pub fn emit_typed_method_call( emit_info.clone(), 1024, ); - let recv_is_optional = (crate::v1_compiler_infer_types::resolved_type(receiver.clone()).return_cardinality.clone() == Cardinality::CardOptional); - let recv_str = if recv_is_optional - .clone() + let arg_strs = Rc::new({ + let mut __result = Vec::new(); + for a in args.iter().cloned() { + __result.push(emit_typed_expr(crate::v1_std_core::arg_value(a.clone()), registry.clone(), scope.clone(), depth.clone(), shared_types.clone(), emit_info.clone(), 1024)); + } + __result + }); + let all_arg_strs = v1_rt::concat( + Rc::new(vec![recv_str.clone()]), + arg_strs.clone(), + ); + emit_nested_rt_concat( + all_arg_strs.clone(), + "".to_string(), + shared_types.clone(), + ) + } + } else { + if (method_name.clone() + == "first".to_string()) + { + emit_rust_first_method_call( + receiver.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ) + } else { { - v1_rt::concat(recv_str_raw.clone(), v1_rt::concat(".expect(\"fail-closed: Optional receiver for method ".to_string(), v1_rt::concat(method_name.clone(), " (empty Optional at runtime)\")".to_string()))) - } else { - recv_str_raw.clone() - }; - let first_arg_str = - emit_typed_first_arg( - args.clone(), + let recv_str_raw = emit_typed_expr( + receiver.clone(), registry.clone(), scope.clone(), depth.clone(), shared_types.clone(), emit_info.clone(), + 1024, ); - match v1_rt::map_get(&crate::extdeps_languages_rust_emit::rust_method_templates(), method_name.clone()) { + let recv_is_optional = (crate::v1_compiler_infer_types::resolved_type(receiver.clone()).return_cardinality.clone() == Cardinality::CardOptional); + let recv_str = if recv_is_optional + .clone() + { + v1_rt::concat(recv_str_raw.clone(), v1_rt::concat(".expect(\"fail-closed: Optional receiver for method ".to_string(), v1_rt::concat(method_name.clone(), " (empty Optional at runtime)\")".to_string()))) + } else { + recv_str_raw.clone() + }; + let first_arg_str = + emit_typed_first_arg( + args.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ); + match v1_rt::map_get(&crate::extdeps_languages_rust_emit::rust_method_templates(), method_name.clone()) { Some(tmpl) => { - let bindings = v1_rt::rc_map_insert(crate::v1_compiler_emit::seed_bindings("recv".to_string(), recv_str.clone()), "arg".to_string(), first_arg_str.clone()); + let bindings = v1_rt::rc_map_insert(crate::v1_compiler_emit::seed_bindings("recv".to_string(), recv_str.clone()), "arg".to_string(), first_arg_str.clone()); let raw = crate::v1_compiler_emit_core_support::apply_named_template(tmpl.clone(), bindings.clone()); if rust_method_template_result_wraps_in_rc(method_name.clone()) { - rust_shared_wrap_ctor(raw.clone()) - } else { - raw.clone() - } + rust_shared_wrap_ctor(raw.clone()) + } else { + raw.clone() + } }, - None => emit_rust_generic_method_call(method_name.clone(), receiver.clone(), args.clone(), result_type.clone(), true, registry.clone(), scope.clone(), depth.clone(), shared_types.clone(), emit_info.clone()), + None => emit_rust_generic_method_call(method_name.clone(), receiver.clone(), args.clone(), result_type.clone(), rust_runtime_primitive_has_bridge(method_name.clone()), registry.clone(), scope.clone(), depth.clone(), shared_types.clone(), emit_info.clone()), } + } } } } @@ -25098,13 +25610,19 @@ if rust_method_template_result_wraps_in_rc(method_name.clone()) { } } MethodSemantics::PlainMethodSemantics => { - if ((method.clone() == "get".to_string()) - && is_collection_typed_expr( - receiver.clone(), - scope.type_env.clone().source_indices.clone(), - )) + if (rust_callable_field_method_call( + method.clone(), + receiver.clone(), + args.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ) != None) { - emit_rust_get_method_call( + match rust_callable_field_method_call( + method.clone(), receiver.clone(), args.clone(), registry.clone(), @@ -25112,83 +25630,106 @@ if rust_method_template_result_wraps_in_rc(method_name.clone()) { depth.clone(), shared_types.clone(), emit_info.clone(), - ) + ) { + Some(field_call) => field_call.clone(), + None => "".to_string(), + } } else { + if ((method.clone() == "get".to_string()) + && is_collection_typed_expr( + receiver.clone(), + scope.type_env.clone().source_indices.clone(), + )) { - let recv_str = emit_typed_expr( + emit_rust_get_method_call( receiver.clone(), + args.clone(), registry.clone(), scope.clone(), depth.clone(), shared_types.clone(), emit_info.clone(), - 1024, - ); - match v1_rt::map_get( - &crate::extdeps_languages_rust_emit::rust_method_templates(), - method.clone(), - ) { - Some(tmpl) => { - let first_arg_str = emit_typed_first_arg( - args.clone(), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - ); - let bindings = v1_rt::rc_map_insert( - crate::v1_compiler_emit::seed_bindings( - "recv".to_string(), - recv_str.clone(), - ), - "arg".to_string(), - first_arg_str.clone(), - ); - let raw = - crate::v1_compiler_emit_core_support::apply_named_template( - tmpl.clone(), - bindings.clone(), + ) + } else { + { + let recv_str = emit_typed_expr( + receiver.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + 1024, + ); + match v1_rt::map_get( + &crate::extdeps_languages_rust_emit::rust_method_templates(), + method.clone(), + ) { + Some(tmpl) => { + let first_arg_str = emit_typed_first_arg( + args.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), ); - if rust_method_template_result_wraps_in_rc(method.clone()) { - rust_shared_wrap_ctor(raw.clone()) - } else { - raw.clone() - } - } - None => { - let arg_strs = Rc::new({ - let mut __result = Vec::new(); - for a in args.iter().cloned() { - __result.push(emit_typed_expr( - crate::v1_std_core::arg_value(a.clone()), - registry.clone(), - scope.clone(), - depth.clone(), - shared_types.clone(), - emit_info.clone(), - 1024, - )); + let bindings = v1_rt::rc_map_insert( + crate::v1_compiler_emit::seed_bindings( + "recv".to_string(), + recv_str.clone(), + ), + "arg".to_string(), + first_arg_str.clone(), + ); + let raw = + crate::v1_compiler_emit_core_support::apply_named_template( + tmpl.clone(), + bindings.clone(), + ); + if rust_method_template_result_wraps_in_rc(method.clone()) { + rust_shared_wrap_ctor(raw.clone()) + } else { + raw.clone() } - __result - }); - let args_str = arg_strs.clone().join(&", ".to_string()); - v1_rt::concat( + } + None => { + let arg_strs = Rc::new({ + let mut __result = Vec::new(); + for a in args.iter().cloned() { + __result.push(emit_typed_expr( + crate::v1_std_core::arg_value(a.clone()), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + 1024, + )); + } + __result + }); + let args_str = arg_strs.clone().join(&", ".to_string()); v1_rt::concat( v1_rt::concat( v1_rt::concat( - v1_rt::concat(recv_str.clone(), ".".to_string()), - crate::v1_compiler_emit::emit_ident( - method.clone(), - RenderTarget::Rust, + v1_rt::concat( + v1_rt::concat( + recv_str.clone(), + ".".to_string(), + ), + crate::v1_compiler_emit::emit_ident( + method.clone(), + RenderTarget::Rust, + ), ), + "(".to_string(), ), - "(".to_string(), + args_str.clone(), ), - args_str.clone(), - ), - ")".to_string(), - ) + ")".to_string(), + ) + } } } } @@ -26714,6 +27255,66 @@ pub fn rust_record_field_needs_fn_rc( } } +pub fn rust_callable_field_method_call( + method_name: String, + receiver: Rc, + args: Rc>>, + registry: Rc>>, + scope: Rc, + depth: i64, + shared_types: Rc>, + emit_info: Rc, +) -> Option { + if rust_receiver_has_callable_method_field(receiver.clone(), method_name.clone(), scope.clone()) + { + { + let recv_str = emit_typed_expr_base( + receiver.clone(), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + ); + let arg_strs = Rc::new({ + let mut __result = Vec::new(); + for a in args.iter().cloned() { + __result.push(emit_cloned_arg( + crate::v1_std_core::arg_value(a.clone()), + registry.clone(), + scope.clone(), + depth.clone(), + shared_types.clone(), + emit_info.clone(), + )); + } + __result + }); + Some(v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat("(".to_string(), recv_str.clone()), + ".".to_string(), + ), + crate::v1_compiler_emit::emit_ident( + method_name.clone(), + RenderTarget::Rust, + ), + ), + ")(".to_string(), + ), + arg_strs.clone().join(&", ".to_string()), + ), + ")".to_string(), + )) + } + } else { + None + } +} + pub fn rust_receiver_has_callable_method_field( receiver: Rc, method_name: String, @@ -26725,11 +27326,14 @@ pub fn rust_receiver_has_callable_method_field( scope.type_env.clone().source_indices.clone(), receiver_type.clone(), ); - rust_record_field_needs_fn_rc( + match rust_struct_field_type_node( scope.clone(), receiver_type_name.clone(), method_name.clone(), - ) + ) { + Some(field_type) => (field_type.connective.clone() == Connective::Arrow), + None => false, + } } } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 91517d3d25a..99b310179bc 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -8417,6 +8417,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), call_semantics: Some(Rc::new(CallSemantics::PlainCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: "empty_map".to_string(), + projected_from: None, }), })), descent_evidence: None, @@ -8434,6 +8435,7 @@ match bare_m.clone() { call_semantics: Some(Rc::new(CallSemantics::PlainCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: "empty_map".to_string(), + projected_from: None, }), })), descent_evidence: None, @@ -8447,6 +8449,7 @@ match bare_m.clone() { call_semantics: Some(Rc::new(CallSemantics::PlainCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: "empty_map".to_string(), + projected_from: None, }), })), descent_evidence: None, @@ -8465,6 +8468,7 @@ match bare_m.clone() { call_semantics: Some(Rc::new(CallSemantics::PlainCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: "empty_map".to_string(), + projected_from: None, }), })), descent_evidence: None, @@ -8478,6 +8482,7 @@ match bare_m.clone() { call_semantics: Some(Rc::new(CallSemantics::PlainCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: "empty_map".to_string(), + projected_from: None, }), })), descent_evidence: None, @@ -8575,6 +8580,7 @@ let call_semantics = if (func_name.clone() == "lookup".to_string()) { Some(Rc::new(CallSemantics::LookupCallSemantics { target: Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: func_name.clone(), + projected_from: None, }), })) } else { diff --git a/src/v1/stage0/src/v1_compiler_infer_lookup.rs b/src/v1/stage0/src/v1_compiler_infer_lookup.rs index 6d37b016ff2..714d8300bf7 100644 --- a/src/v1/stage0/src/v1_compiler_infer_lookup.rs +++ b/src/v1/stage0/src/v1_compiler_infer_lookup.rs @@ -53,8 +53,7 @@ pub use crate::v1_compiler_infer_sigs::{ decide_callable_candidates, lookup_resolved_sig, parent_closure_callable_candidates, }; pub use crate::v1_compiler_infer_sigs::{ - CallableCandidate, CallableIdentity, DeclaredCallableIdentity, FuncSigLookup, ResolvedFuncEnv, - ResolvedFuncSig, + CallableCandidate, CallableIdentity, FuncSigLookup, ResolvedFuncEnv, ResolvedFuncSig, }; pub use crate::v1_compiler_infer_types::{ child_type_node, emit_map_has, enrich_kernel_type, instantiate_algebra_type, @@ -81,8 +80,9 @@ pub use crate::v1_std_core::{ with_required_cardinality, }; pub use crate::v1_std_core::{ - CallTargetIdentity, Cardinality, Connective, ErrorNode, FieldAccessStyle, FieldSummary, - FieldValueShape, InferredNode, MethodSemantics, NewlineIndex, Node, + CallTargetIdentity, Cardinality, Connective, DeclaredCallableIdentity, ErrorNode, + FieldAccessStyle, FieldSummary, FieldValueShape, InferredNode, MethodSemantics, NewlineIndex, + Node, }; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; @@ -209,6 +209,7 @@ pub fn resolved_declaration_call_target( crate::std_primitive_projection::primitive_identity_runtime_name( row.primitive.clone(), ), + projected_from: None, }) } ProjectionFidelity::ModeledProjection => { @@ -217,6 +218,7 @@ pub fn resolved_declaration_call_target( crate::std_primitive_projection::primitive_identity_runtime_name( row.primitive.clone(), ), + projected_from: Some(declared.clone()), }) } ProjectionFidelity::DivergentProjection { divergence: _, .. } => { @@ -252,6 +254,7 @@ pub fn builtin_call_target_or_undetermined(func_name: String) -> Rc Rc::new(CallTargetIdentity::RuntimePrimitiveCall { primitive_name: func_name.clone(), + projected_from: None, }), None => Rc::new(CallTargetIdentity::CallableTargetUndetermined), } @@ -267,7 +270,7 @@ pub fn callable_lookup_over_candidates( sig: sig.clone(), declared: Rc::new(DeclaredCallableIdentity { owner_module_path: func_env.name.clone(), - decl_name: name.clone(), + decl_name: crate::v1_std_core::qualified_last_segment(name.clone()), }), }), None => { @@ -616,7 +619,7 @@ pub fn borrowed_census_callable_candidate( identity: Rc::new(CallableIdentity::DeclaredCallable { identity: Rc::new(DeclaredCallableIdentity { owner_module_path: candidate.module_path.clone(), - decl_name: name.clone(), + decl_name: crate::v1_std_core::qualified_last_segment(name.clone()), }), }), sig: Rc::new(ResolvedFuncSig { @@ -699,7 +702,9 @@ pub fn func_sig_from_global_bare(type_env: Rc, name: String) -> Rc, name: String) -> Rc, } -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct DeclaredCallableIdentity { - pub owner_module_path: String, - pub decl_name: String, -} - #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[serde(tag = "_variant")] pub enum CallableIdentity { diff --git a/src/v1/stage0/src/v1_compiler_runtime_rust.rs b/src/v1/stage0/src/v1_compiler_runtime_rust.rs index 7711aa64f38..a6e3c928605 100644 --- a/src/v1/stage0/src/v1_compiler_runtime_rust.rs +++ b/src/v1/stage0/src/v1_compiler_runtime_rust.rs @@ -36,7 +36,7 @@ pub fn rt_concat_trait() -> String { } pub fn rt_string_ops() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Code-point indexing over UTF-8, with the ASCII fast path bounded by `pos`\n".to_string(), "/// rather than by the whole string. A leading run of ASCII bytes makes the byte\n".to_string()), "/// offset equal the code-point offset, so nothing past `pos` is examined and the\n".to_string()), "/// per-call whole-string `s.is_ascii()` rescan is gone (STRING-INDEX-0). Cost is\n".to_string()), "/// O(min(pos, n)) -- the fallback's own cost -- never O(n) unconditionally.\n".to_string()), "/// This is the entry point for a caller holding a bare `&str` and therefore no ASCII\n".to_string()), "/// fact: it must test, and O(min(pos, n)) is the cheapest honest test. A caller that\n".to_string()), "/// holds an `RcStr` carries the fact instead and calls `RcStr::char_at`, which is\n".to_string()), "/// O(1) on ASCII text; the interpreter routes every string index that way.\n".to_string()), "/// RESIDUAL: a left-to-right walk through THIS function is still O(n^2), and a walk\n".to_string()), "/// through the carrier is linear rather than constant per string -- a cursor surface\n".to_string()), "/// that does not re-index from zero is that residual's next rung.\n".to_string()), "pub fn char_at(s: &str, pos: i64) -> String {\n".to_string()), " let pos = pos.max(0) as usize;\n".to_string()), " let bytes = s.as_bytes();\n".to_string()), " if pos >= bytes.len() { return String::new(); }\n".to_string()), " if bytes[..=pos].is_ascii() { return String::from(bytes[pos] as char); }\n".to_string()), " s.chars().nth(pos).map(|ch| ch.to_string()).unwrap_or_default()\n".to_string()), "}\n\n".to_string()), "pub fn string_length(s: &str) -> i64 {\n".to_string()), " if s.is_ascii() { s.len() as i64 } else { s.chars().count() as i64 }\n".to_string()), "}\n\n".to_string()), "/// See `char_at`: the ASCII fast path is bounded by `end`, not by the whole string.\n".to_string()), "pub fn substring(s: &str, start: i64, end: i64) -> String {\n".to_string()), " let start = start.max(0) as usize;\n".to_string()), " let end = end.max(0) as usize;\n".to_string()), " if end <= start { return String::new(); }\n".to_string()), " let bytes = s.as_bytes();\n".to_string()), " if start >= bytes.len() { return String::new(); }\n".to_string()), " let out_end = end.min(bytes.len());\n".to_string()), " if bytes[..out_end].is_ascii() {\n".to_string()), " let take_len = out_end.saturating_sub(start);\n".to_string()), " record_substring_chars_walked(s, start, take_len);\n".to_string()), " return s[start..out_end].to_string();\n".to_string()), " }\n".to_string()), " let take_len = end.saturating_sub(start);\n".to_string()), " record_substring_chars_walked(s, start, take_len);\n".to_string()), " s.chars().skip(start).take(take_len).collect()\n".to_string()), "}\n\n".to_string()), "pub fn string_contains(s: &str, sub: String) -> bool {\n".to_string()), " s.contains(&*sub)\n".to_string()), "}\n\n".to_string()), "pub fn contains(s: String, sub: String) -> bool { string_contains(&s, sub) }\n\n".to_string()), "pub fn starts_with(s: String, prefix: String) -> bool { s.starts_with(&*prefix) }\n\n".to_string()), "pub fn ends_with(s: String, suffix: String) -> bool { s.ends_with(&*suffix) }\n\n".to_string()), "pub fn trim(s: String) -> String { s.trim().to_string() }\n\n".to_string()), "pub fn count(items: Rc>) -> i64 { items.len() as i64 }\n\n".to_string()), "pub fn str_eq(a: String, b: String) -> bool { a == b }\n\n".to_string()), "pub fn to_string(value: i64) -> String { value.to_string() }\n\n".to_string()), "/// RFC 3629 UTF-8 decode of a byte vector. Fail-closed on invalid UTF-8.\n".to_string()), "pub fn utf8_decode_bytes(bytes: &[u8]) -> Result {\n".to_string()), " String::from_utf8(bytes.to_vec()).map_err(|e| format!(\"invalid UTF-8 in access payload: {}\", e))\n".to_string()), "}\n\n".to_string()), "pub fn clamp(val: i64, min_val: i64, max_val: i64) -> i64 {\n".to_string()), " val.clamp(min_val, max_val)\n".to_string()), "}\n\n".to_string()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Code-point indexing over UTF-8, with the ASCII fast path bounded by `pos`\n".to_string(), "/// rather than by the whole string. A leading run of ASCII bytes makes the byte\n".to_string()), "/// offset equal the code-point offset, so nothing past `pos` is examined and the\n".to_string()), "/// per-call whole-string `s.is_ascii()` rescan is gone (STRING-INDEX-0). Cost is\n".to_string()), "/// O(min(pos, n)) -- the fallback's own cost -- never O(n) unconditionally.\n".to_string()), "/// This is the entry point for a caller holding a bare `&str` and therefore no ASCII\n".to_string()), "/// fact: it must test, and O(min(pos, n)) is the cheapest honest test. A caller that\n".to_string()), "/// holds an `RcStr` carries the fact instead and calls `RcStr::char_at`, which is\n".to_string()), "/// O(1) on ASCII text; the interpreter routes every string index that way.\n".to_string()), "/// RESIDUAL: a left-to-right walk through THIS function is still O(n^2), and a walk\n".to_string()), "/// through the carrier is linear rather than constant per string -- a cursor surface\n".to_string()), "/// that does not re-index from zero is that residual's next rung.\n".to_string()), "pub fn char_at(s: &str, pos: i64) -> String {\n".to_string()), " let pos = pos.max(0) as usize;\n".to_string()), " let bytes = s.as_bytes();\n".to_string()), " if pos >= bytes.len() { return String::new(); }\n".to_string()), " if bytes[..=pos].is_ascii() { return String::from(bytes[pos] as char); }\n".to_string()), " s.chars().nth(pos).map(|ch| ch.to_string()).unwrap_or_default()\n".to_string()), "}\n\n".to_string()), "pub fn string_length(s: &str) -> i64 {\n".to_string()), " if s.is_ascii() { s.len() as i64 } else { s.chars().count() as i64 }\n".to_string()), "}\n\n".to_string()), "/// The two symbol bridges. `Symbol` is declared to realize as `String` on this target\n".to_string()), "/// (extdeps.languages.rust.types), so interning and its inverse are both the identity\n".to_string()), "/// HERE and nowhere else: a target whose Symbol is a table index needs a real table, and\n".to_string()), "/// its registry rows would say so. These exist because the .dag declarations are\n".to_string()), "/// self-calls -- host seams -- and emitting a self-call produces a function that compiles\n".to_string()), "/// and never returns (std.primitive_projection symbol_lexeme_seam_disposition_note).\n".to_string()), "pub fn symbol_lexeme(sym: String) -> String {\n".to_string()), " sym\n".to_string()), "}\n\n".to_string()), "pub fn symbol_intern_lexeme(lexeme: String) -> String {\n".to_string()), " lexeme\n".to_string()), "}\n\n".to_string()), "/// See `char_at`: the ASCII fast path is bounded by `end`, not by the whole string.\n".to_string()), "pub fn substring(s: &str, start: i64, end: i64) -> String {\n".to_string()), " let start = start.max(0) as usize;\n".to_string()), " let end = end.max(0) as usize;\n".to_string()), " if end <= start { return String::new(); }\n".to_string()), " let bytes = s.as_bytes();\n".to_string()), " if start >= bytes.len() { return String::new(); }\n".to_string()), " let out_end = end.min(bytes.len());\n".to_string()), " if bytes[..out_end].is_ascii() {\n".to_string()), " let take_len = out_end.saturating_sub(start);\n".to_string()), " record_substring_chars_walked(s, start, take_len);\n".to_string()), " return s[start..out_end].to_string();\n".to_string()), " }\n".to_string()), " let take_len = end.saturating_sub(start);\n".to_string()), " record_substring_chars_walked(s, start, take_len);\n".to_string()), " s.chars().skip(start).take(take_len).collect()\n".to_string()), "}\n\n".to_string()), "pub fn string_contains(s: &str, sub: String) -> bool {\n".to_string()), " s.contains(&*sub)\n".to_string()), "}\n\n".to_string()), "pub fn contains(s: String, sub: String) -> bool { string_contains(&s, sub) }\n\n".to_string()), "pub fn starts_with(s: String, prefix: String) -> bool { s.starts_with(&*prefix) }\n\n".to_string()), "pub fn ends_with(s: String, suffix: String) -> bool { s.ends_with(&*suffix) }\n\n".to_string()), "pub fn trim(s: String) -> String { s.trim().to_string() }\n\n".to_string()), "pub fn count(items: Rc>) -> i64 { items.len() as i64 }\n\n".to_string()), "pub fn str_eq(a: String, b: String) -> bool { a == b }\n\n".to_string()), "pub fn to_string(value: i64) -> String { value.to_string() }\n\n".to_string()), "/// RFC 3629 UTF-8 decode of a byte vector. Fail-closed on invalid UTF-8.\n".to_string()), "pub fn utf8_decode_bytes(bytes: &[u8]) -> Result {\n".to_string()), " String::from_utf8(bytes.to_vec()).map_err(|e| format!(\"invalid UTF-8 in access payload: {}\", e))\n".to_string()), "}\n\n".to_string()), "pub fn clamp(val: i64, min_val: i64, max_val: i64) -> i64 {\n".to_string()), " val.clamp(min_val, max_val)\n".to_string()), "}\n\n".to_string()) } pub fn rt_collection_ops() -> String { diff --git a/src/v1/stage0/src/v1_rt.rs b/src/v1/stage0/src/v1_rt.rs index 97af46d127e..30f3c637d47 100644 --- a/src/v1/stage0/src/v1_rt.rs +++ b/src/v1/stage0/src/v1_rt.rs @@ -309,6 +309,20 @@ pub fn string_length(s: &str) -> i64 { } } +/// The two symbol bridges. `Symbol` is declared to realize as `String` on this target +/// (extdeps.languages.rust.types), so interning and its inverse are both the identity +/// HERE and nowhere else: a target whose Symbol is a table index needs a real table, and +/// its registry rows would say so. These exist because the .dag declarations are +/// self-calls -- host seams -- and emitting a self-call produces a function that compiles +/// and never returns (std.primitive_projection symbol_lexeme_seam_disposition_note). +pub fn symbol_lexeme(sym: String) -> String { + sym +} + +pub fn symbol_intern_lexeme(lexeme: String) -> String { + lexeme +} + /// See `char_at`: the ASCII fast path is bounded by `end`, not by the whole string. pub fn substring(s: &str, start: i64, end: i64) -> String { let start = start.max(0) as usize; diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index d2c7bac19b7..cb83f6cf7c0 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -229,11 +229,18 @@ impl VarBindingKind { } } +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DeclaredCallableIdentity { + pub owner_module_path: String, + pub decl_name: String, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[serde(tag = "_variant")] pub enum CallTargetIdentity { RuntimePrimitiveCall { primitive_name: String, + projected_from: Option>, }, SourceDeclarationCall { owner_module_path: String, diff --git a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs index 7525f8971a6..c2455448796 100644 --- a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs +++ b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs @@ -86,6 +86,7 @@ pub fn fixture_disposition( crate::v1_compiler_emit_rust::build_module_index(Rc::new(vec![])), v1_rt::rc_empty_map::>(), v1_rt::rc_empty_map::(), + false, ), ) } @@ -122,6 +123,7 @@ pub fn cross_module_candidate_with_export_proof_survives() -> bool { crate::v1_compiler_emit_rust::build_module_index(Rc::new(vec![])), v1_rt::rc_empty_map::>(), v1_rt::rc_empty_map::(), + false, ) == Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { provider_module: "fixture.provider".to_string(), })) @@ -234,6 +236,7 @@ pub fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> bool { "V".to_string(), "E".to_string(), ), + false, ) == Rc::new( ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { parent_enum: "E".to_string(), @@ -286,9 +289,31 @@ pub fn a_variant_whose_parent_is_ambiguous_is_not_delegated_to_nothing() -> bool "V".to_string(), "".to_string(), ), + false, ) == Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved)) } +pub fn a_known_variant_spelling_in_a_type_position_takes_the_registry_arm() -> bool { + (crate::v1_compiler_emit_rust::reference_derived_disposition_name( + crate::v1_compiler_emit_rust::reference_derived_candidate_disposition( + "V".to_string(), + "fixture.consumer".to_string(), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::>>(), + Rc::new(vec![]), + v1_rt::rc_empty_map::>(), + crate::v1_compiler_emit_rust::build_module_index(Rc::new(vec![])), + fixture_variant_type_summaries(), + v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + "E".to_string(), + ), + true, + ), + ) == "registry-absent".to_string()) +} + pub fn an_unresolved_parent_is_not_reported_as_registry_absent() -> bool { (crate::v1_compiler_emit_rust::reference_derived_disposition_name(Rc::new( ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved, @@ -311,6 +336,7 @@ pub fn non_variant_name_still_answers_registry_absent() -> bool { "V".to_string(), "E".to_string(), ), + false, ), ) == "registry-absent".to_string()) } diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 895e408187d..3949fcf8f4a 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -58,7 +58,8 @@ fn fixture_disposition(registry: Map, export_sets: Map Bool { source_indices: empty_map(), module_index: build_module_index(modules: []), type_summaries: empty_map(), - variant_to_enum: empty_map() + variant_to_enum: empty_map(), + in_type_position: false ) == CandidateSurvived { provider_module: "fixture.provider" } } @@ -136,7 +138,8 @@ test fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> Bool { source_indices: empty_map(), module_index: build_module_index(modules: []), type_summaries: fixture_variant_type_summaries(), - variant_to_enum: map_insert(empty_map(), "V", "E") + variant_to_enum: map_insert(empty_map(), "V", "E"), + in_type_position: false ) == CandidateVariantDelegatedToParent { parent_enum: "E" } } @@ -165,10 +168,29 @@ test fn a_variant_whose_parent_is_ambiguous_is_not_delegated_to_nothing() -> Boo source_indices: empty_map(), module_index: build_module_index(modules: []), type_summaries: fixture_colliding_variant_type_summaries(), - variant_to_enum: map_insert(empty_map(), "V", "") + variant_to_enum: map_insert(empty_map(), "V", ""), + in_type_position: false ) == CandidateVariantParentUnresolved } +// A TYPE POSITION CANNOT HOLD A VARIANT. The same spelling `V` that the whole-corpus type summaries +// know as a variant, referenced from one of the module's type positions, is decided by the registry +// arm -- here registry-absent, never delegated to an enum the position could not name. +test fn a_known_variant_spelling_in_a_type_position_takes_the_registry_arm() -> Bool { + reference_derived_disposition_name(disposition: reference_derived_candidate_disposition( + name: "V", + this_module_name: "fixture.consumer", + registry: empty_map(), + export_sets: empty_map(), + typed_modules: [], + source_indices: empty_map(), + module_index: build_module_index(modules: []), + type_summaries: fixture_variant_type_summaries(), + variant_to_enum: map_insert(empty_map(), "V", "E"), + in_type_position: true + )) == "registry-absent" +} + test fn an_unresolved_parent_is_not_reported_as_registry_absent() -> Bool { reference_derived_disposition_name(disposition: CandidateVariantParentUnresolved) != "registry-absent" } @@ -183,7 +205,8 @@ test fn non_variant_name_still_answers_registry_absent() -> Bool { source_indices: empty_map(), module_index: build_module_index(modules: []), type_summaries: fixture_variant_type_summaries(), - variant_to_enum: map_insert(empty_map(), "V", "E") + variant_to_enum: map_insert(empty_map(), "V", "E"), + in_type_position: false )) == "registry-absent" }