From 7307de2d62e1f01dbdc94b6b87c8169995a4c828 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 00:49:24 +0000 Subject: [PATCH 1/3] =?UTF-8?q?Namespace=20keystone=20(design=20doc=20?= =?UTF-8?q?=C2=A78=20step=201):=20=C2=A713=20unique-on-chain=20resolver=20?= =?UTF-8?q?in=20the=20executing=20v1=20seed,=20default-off?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NameResolutionPolicy gate: name_resolution_policy_is_namespace_only thread-local builtin (runtime_rust.dag -> v1_rt), host-side setter only, default OFF = ImportScoped byte-for-byte; registered in 04_method builtins + extdeps rust rt rows - type/value path (04_env.dag): under NamespaceOnlyY a global_bare homonym resolves to the unique binder on the ancestor chain; zero-or-multiple refuses (census-walk parity: zero-on-chain whole-pool homonym is Ambiguous, never a fabricated bind); nearest-wins preserved verbatim under the default - fn path (04_sigs/04_lookup): lookup_resolved_sig/lookup_func_sig rewritten to the 3-state FuncSigLookup (Resolved|Unresolved|Ambiguous{candidates}) so a refusal has somewhere to go; FuncSigAmbiguous never falls through to the census fallback; first-hit preserved verbatim under the default; analysis-only consumers project through func_sig_if_resolved, bind sites (ExprVar/ExprCall) match the full outcome - typed diagnostic: AmbiguousReference{name, candidates, span} in 00_core.dag with span/message arms; emitted at the reference site with the full candidate fix menu (04_resolve bare-miss, 04_infer type annotation + record-lit presence, fn bind sites) - discriminating witness: namespace_unique_on_chain_policy_test.rs — same homonym fixtures compile clean under ImportScoped, refuse typed+located under NamespaceOnlyY on both paths; unique-on-chain-still-resolves + unbound-stays-UnresolvedType controls - regen_stage0 two-generation fixed point: regen_divergence_count=0, --verify green - census refresh consumed into namespace-resolution-design.md §8/§12.3/§12.4/§13 + raw rows in docs/probes/resolution_divergence_census_2026-07-22.tsv (diverge=0, containment_ambiguous=38, owner_mismatch=0, lexical_steps={1:17175,2:1}) Co-authored-by: Brian Searls --- dag/extdeps/languages/rust/emit.dag | 1 + docs/plans/namespace-resolution-design.md | 53 +- ...esolution_divergence_census_2026-07-22.tsv | 54 + src/v1/00_core.dag | 3 + src/v1/04_env.dag | 75 +- src/v1/04_infer.dag | 61 +- src/v1/04_lookup.dag | 26 +- src/v1/04_method.dag | 1 + src/v1/04_resolve.dag | 4 +- src/v1/04_sigs.dag | 64 +- src/v1/05_emit.dag | 4 +- src/v1/runtime_rust.dag | 14 + src/v1/stage0/src/cli_run.rs | 41 +- .../stage0/src/extdeps_languages_rust_emit.rs | 2 +- src/v1/stage0/src/v1_compiler_emit.rs | 8 +- src/v1/stage0/src/v1_compiler_infer.rs | 1476 +++++++++-------- src/v1/stage0/src/v1_compiler_infer_env.rs | 178 +- src/v1/stage0/src/v1_compiler_infer_lookup.rs | 44 +- src/v1/stage0/src/v1_compiler_infer_method.rs | 5 + .../stage0/src/v1_compiler_infer_resolve.rs | 45 +- src/v1/stage0/src/v1_compiler_infer_sigs.rs | 110 +- src/v1/stage0/src/v1_compiler_runtime_rust.rs | 2 +- src/v1/stage0/src/v1_rt.rs | 17 + src/v1/stage0/src/v1_std_core.rs | 27 + src/v1/tests/src/func_env_scope_chain_test.rs | 21 +- src/v1/tests/src/lib.rs | 2 + .../namespace_unique_on_chain_policy_test.rs | 235 +++ 27 files changed, 1770 insertions(+), 803 deletions(-) create mode 100644 docs/probes/resolution_divergence_census_2026-07-22.tsv create mode 100644 src/v1/tests/src/namespace_unique_on_chain_policy_test.rs diff --git a/dag/extdeps/languages/rust/emit.dag b/dag/extdeps/languages/rust/emit.dag index 437dc6402c1..0c9d266d0f9 100644 --- a/dag/extdeps/languages/rust/emit.dag +++ b/dag/extdeps/languages/rust/emit.dag @@ -158,6 +158,7 @@ data rt_function_registry: List = [ { 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: "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 }, diff --git a/docs/plans/namespace-resolution-design.md b/docs/plans/namespace-resolution-design.md index e4fe6e14c5e..0a5c608c72c 100644 --- a/docs/plans/namespace-resolution-design.md +++ b/docs/plans/namespace-resolution-design.md @@ -332,6 +332,9 @@ values `{ import-scoped (§1c, today) , namespace-only-X , namespace-only-Y }`. 1. Land `resolve(name, position)` + the `Ambiguous`/`Unresolved` outcomes beside the current resolver, gated by the policy row (import-scoped stays the default → zero corpus churn). + **LANDED 2026-07-22** in the executing v1 seed under the §13 unique-on-chain semantics + (which supersede this section's original nearest-wins wording) — see the §13 "STEP 1 + LANDED" entry for the mechanism, edit sites, and the discriminating witness. 2. The **precise census is already run** (§5.1, declared `module` paths): the exact forced-qualification residue and the fork worklist are its outputs — steps 3–4 act on them, no re-run. @@ -629,7 +632,9 @@ from *build a fourth resolver* into *delete a fork*. ancestor walk; the tree is materialized once by fill (§7.5's fill-once discipline), so §6's "bare minimum cost" is satisfied by construction rather than by measurement. *This is the claim most worth falsifying before implementation — it is the difference between a cheap - substitution and a rewrite.* + substitution and a rewrite.* **FALSIFICATION RUN — the claim held (2026-07-22, §12.4 + delivery):** lexical_steps_histogram={1: 17175, 2: 1} over 37,521 containment hits — a + substitution, not a rewrite. - **Migration rides §8 unchanged.** When a subtree flips to `namespace-only-Y`, its runtime dispatch keys on the same index in the same motion. The two cannot drift because there is only one thing; a flip that moved resolution without moving dispatch would re-open the fork. @@ -691,6 +696,23 @@ and the lane's shape changes. This is the cheapest moment to find out. **Do not** flip any policy, edit any resolver, or change dispatch as part of this. The census is an artifact the rest of the lane consumes; §8 step 1 starts after it, informed by it. +**DELIVERED — and §8 step 1 has consumed it (2026-07-22).** The census shipped as +`resolution_divergence_census` (#6936 slice 1, #6967 slice 2); the fresh pre-step-1 run +(whole tree, `dag` + `src/v2`; raw rows in +`docs/probes/resolution_divergence_census_2026-07-22.tsv`): modules_resolved=1309, +sites_checked=40592 — **agree=33422 / diverge=0 / containment_ambiguous=38 / +containment_unresolved=0**, import_unresolved=4099 (walk-vs-infer cross-check +mismatch=0), neither_bound=3033 (builtin_or_intrinsic=1422, local_or_param=1607, +genuinely_unbound=4), agree-global-unique owner_mismatch=0. Cost shape (the §12.3 +falsification): containment hits=37521, lexical_steps_histogram={1: 17175, 2: 1}, +global_unique=20345 — the walk is O(chain depth) map hits with depth almost always 1, +so the fold is a **substitution, not a rewrite**; the claim held. One genuine §13 +fail-open surfaced by the silent-pick join (both whole-tree and closure-scoped scopes, +pre-existing on main): `gunbc.falsifier_workflow` bare `ci_repo_root_shell` first-hits +`gunbc.ci_spec` over the identical duplicate decl in `gunbc.merge_admission_produce` +(fn_parent_first_hit=1) — a real §3 fork needing consolidation or qualification, and +the live specimen of the class §8 step 1's refusal arm makes loud under the flip. + Related: [type environment: single import authority + scope cursor](type-env-single-authority-design.md) — the type-env/SymbolIndex lane design this walk-rule migration rides on · [interface summaries and the declared↔use arity family](interface-summary-declared-use-arity.md) — the `std.interface_summary` carrier consumed by interface-grain resolve. ## 13. Resolution is unique-on-chain, not nearest (operator ruling, ratified 2026-07-21) @@ -730,20 +752,43 @@ target is encoded **once** (no dual representation), an unused alias is lintable surface form is a grammar row (DESIGN.md §4, one grammar read both directions). **Import→alias transmutation** is the migration mechanism — an `import` becomes an alias node at the importing position, and the **source of truth is the walk's resolved target**, with the old import list -demoted to a cross-check. Grounded by #6936's buckets: agree=38138 / import_unresolved=1854 / -neither_bound=739 / ambiguous=53. +demoted to a cross-check. Grounded by the census buckets (#6936 first run: agree=38138 / +import_unresolved=1854 / neither_bound=739 / ambiguous=53; refreshed 2026-07-22 pre-step-1: +agree=33422 / import_unresolved=4099 / neither_bound=3033 / ambiguous=38, diverge=0 — +`docs/probes/resolution_divergence_census_2026-07-22.tsv`). **`global_bare` dies as a mechanism.** The terminal state **deletes** `symbol_index_global_unique_lookup` / `GlobalBareLookup` as *resolution* mechanisms — they survive only as a migration oracle (computing the §12.4 divergence census), then are removed. This supersedes §7's "the wall moves to *is it unique?*": the wall is *is it unique **on your chain?*** The executing seed's two interim gaps are named, not hidden: the fn path (`lookup_resolved_sig`, -`v1_compiler_infer_sigs.rs:111-117` — first-hit over `func_env.parents`, **no refusal arm at all**) +`v1_compiler_infer_sigs.rs` — first-hit over `func_env.parents`, **no refusal arm at all**) and the type/data LCP nearest-arm (`global_bare_lookup`, `v1_compiler_infer_env.rs` — refuses only on an exact tie). The §5 interim backstop plus still-hawk-65's slice-2 per-class silent-pick counts gate any widening (§12.4; do not widen blind — refusing where nothing refuses today reds latent homonyms at scale, and fn is the larger class since it has *no* refusal today). +**§8 STEP 1 LANDED (2026-07-22) — both gaps now have policy-gated strict arms in the executing +seed.** A thread-local `NameResolutionPolicy` gate (`name_resolution_policy_is_namespace_only`, +`v1_rt`, host-setter only, **default OFF = ImportScoped byte-for-byte**) forks both paths: +the type/value path (`global_bare_lookup` / `global_bare_is_ambiguous`, +`src/v1/04_env.dag`) resolves a homonym to the **unique binder on the ancestor chain** +(exactly-one resolves; zero-or-multiple refuses — zero-on-chain whole-pool homonyms are +`Ambiguous`, census-walk parity, never a fabricated bind), and the fn path is rewritten to the +3-state `FuncSigLookup = FuncSigResolved | FuncSigUnresolved | FuncSigAmbiguous{candidates}` +(`src/v1/04_sigs.dag` / `04_lookup.dag`) so a refusal finally **has somewhere to go** — the +`Absent`-as-"keep-looking" straddle is dead as a class, and `FuncSigAmbiguous` never falls +through to the census fallback. Refusals surface as the typed, located +`AmbiguousReference { name, candidates, span }` diagnostic (`00_core.dag`) at the reference +site with the full candidate fix-menu. Analysis-only consumers (provenance/descent) project +through `func_sig_if_resolved` — conservative no-enrichment, never a fabricated bind; the +semantic bind sites (`04_infer.dag` ExprVar/ExprCall) match the full outcome. Discriminating +witness: `src/v1/tests/src/namespace_unique_on_chain_policy_test.rs` — the same homonym +fixtures compile clean under ImportScoped and refuse (typed, full candidate list) under +NamespaceOnlyY on both paths, with unique-on-chain-still-resolves and +unbound-stays-`UnresolvedType` controls. No subtree is flipped; the flip (step 4) still +gates on import→alias transmutation landing with-or-before it. + **Builtins bind at root.** The root namespace is on **every** chain, so builtins bound at root are unique-on-chain everywhere — this **structurally dissolves** the prelude-shaped `neither_bound` class (#6936, 739) rather than special-casing it, pending still-hawk's confirmation of the (c) diff --git a/docs/probes/resolution_divergence_census_2026-07-22.tsv b/docs/probes/resolution_divergence_census_2026-07-22.tsv new file mode 100644 index 00000000000..097ece5ff2c --- /dev/null +++ b/docs/probes/resolution_divergence_census_2026-07-22.tsv @@ -0,0 +1,54 @@ +[resolution-divergence-census] scope=dag+src/v2 modules_resolved=1309 modules_excluded=0 +[resolution-divergence-census] sites_checked=40592 +[resolution-divergence-census] agree=33422 +[resolution-divergence-census] diverge=0 +[resolution-divergence-census] containment_ambiguous=38 +[resolution-divergence-census] containment_unresolved=0 +[resolution-divergence-census] import_unresolved=4099 +[resolution-divergence-census] neither_bound=3033 +[resolution-divergence-census] neither_bound_subclass builtin_or_intrinsic=1422 local_or_param=1607 genuinely_unbound=4 +[resolution-divergence-census] silent_pick global_bare_lcp=1045 global_bare_lcp_tie=13692 fn_parent_first_hit=1 +[resolution-divergence-census] agree_global_unique_owner_rows=16246 owner_mismatch=0 +[resolution-divergence-census] import_unresolved_infer_cross_check agree=4099 mismatch=0 +[resolution-divergence-census] cost_shape hits=37521 lexical_only=17176 global_unique=20345 lexical_steps_histogram={1: 17175, 2: 1} + +CONTAINMENT_AMBIGUOUS module=std.layout caller=render_go callee=repeat_string at=/workspace/dag/std/layout.dag@949 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.formats.dnsmasq caller=serialize_dnsmasq callee=render at=/workspace/dag/extdeps/formats/dnsmasq.dag@1829 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=extdeps.git.versioning caller=git_release_version_compare callee=nat_compare at=/workspace/dag/extdeps/git/versioning.dag@1535 via=n/a import_chain=std.nat (std.nat.nat_compare) +CONTAINMENT_AMBIGUOUS module=extdeps.git.versioning caller=git_release_version_compare callee=nat_compare at=/workspace/dag/extdeps/git/versioning.dag@1592 via=n/a import_chain=std.nat (std.nat.nat_compare) +CONTAINMENT_AMBIGUOUS module=extdeps.git.versioning caller=git_release_version_compare callee=nat_compare at=/workspace/dag/extdeps/git/versioning.dag@1645 via=n/a import_chain=std.nat (std.nat.nat_compare) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.yaml.emit caller=serialize_yaml callee=render at=/workspace/dag/extdeps/languages/yaml/emit.dag@6462 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=extdeps.version.semver caller=semver_compare_non_negative_int callee=nat_compare at=/workspace/dag/extdeps/version/semver.dag@996 via=n/a import_chain=std.nat (std.nat.nat_compare) +CONTAINMENT_AMBIGUOUS module=test.claim.machine_shape_catalog_integration_witness caller=cpu_catalog_shape callee=shape_from_catalog at=/workspace/dag/test/claim/machine_shape_catalog_integration_witness_test.dag@1032 via=n/a import_chain=extdeps.cpu.machine_shape (extdeps.cpu.machine_shape.shape_from_catalog) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@4946 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@5738 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@5878 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@6010 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@6169 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@6309 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_item callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@6433 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.languages.markdown caller=md_block callee=repeat_string at=/workspace/dag/extdeps/languages/markdown.dag@10260 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=extdeps.render.terminal caller=serialize_line callee=repeat_string at=/workspace/dag/extdeps/render/terminal.dag@3854 via=n/a import_chain=std.render (std.render.repeat_string) +CONTAINMENT_AMBIGUOUS module=test.claim.machine_shape_witness caller=shape_from_rtx5090_catalog callee=shape_from_catalog at=/workspace/dag/test/claim/machine_shape_witness_test.dag@1817 via=n/a import_chain=extdeps.gpu.machine_shape (extdeps.gpu.machine_shape.shape_from_catalog) +CONTAINMENT_AMBIGUOUS module=test.claim.machine_shape_witness caller=shape_from_m5_catalog callee=shape_from_catalog at=/workspace/dag/test/claim/machine_shape_witness_test.dag@3041 via=n/a import_chain=extdeps.gpu.machine_shape (extdeps.gpu.machine_shape.shape_from_catalog) +CONTAINMENT_AMBIGUOUS module=gunbc.gitignore_emit caller=expected_gitignore callee=render at=/workspace/dag/gunbc/gitignore_emit.dag@10701 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=v2.lens.cost caller=nat_dominates callee=nat_compare at=/workspace/src/v2/lens/cost.dag@6949 via=n/a import_chain=v2.std.nat (v2.std.nat.nat_compare) +CONTAINMENT_AMBIGUOUS module=gunbc.runner_deploy_emit caller=expected_runner_deploy_manifest callee=render at=/workspace/dag/gunbc/runner_deploy_emit.dag@7785 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.runner_deploy_emit caller=expected_runner_deploy_manifest_json callee=render at=/workspace/dag/gunbc/runner_deploy_emit.dag@8105 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.runner_deploy_emit caller=expected_runner_deploy_manifest_json callee=render at=/workspace/dag/gunbc/runner_deploy_emit.dag@8278 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=v2.compiler.emit_module caller=emit_module_append_decl callee=emit at=/workspace/src/v2/compiler/emit_module.dag@692 via=n/a import_chain=v2.compiler.emit (v2.compiler.emit.emit) +CONTAINMENT_AMBIGUOUS module=v2.compiler.emit_module caller=emit_family_append_member callee=emit at=/workspace/src/v2/compiler/emit_module.dag@2425 via=n/a import_chain=v2.compiler.emit (v2.compiler.emit.emit) +CONTAINMENT_AMBIGUOUS module=v2.compiler.emit_orchestration caller=orch_emit_from_registry callee=emit at=/workspace/src/v2/compiler/05_emit_orchestration.dag@3842 via=n/a import_chain=v2.compiler.emit (v2.compiler.emit.emit) +CONTAINMENT_AMBIGUOUS module=v2.compiler.emit_orchestration caller=orch_emit_node_spelling callee=emit at=/workspace/src/v2/compiler/05_emit_orchestration.dag@4130 via=n/a import_chain=v2.compiler.emit (v2.compiler.emit.emit) +CONTAINMENT_AMBIGUOUS module=v2.compiler.source_authority caller=semantic_ir_from_ast_with_model callee=resolve at=/workspace/src/v2/compiler/source_authority.dag@17318 via=n/a import_chain=v2.compiler.resolve (v2.compiler.resolve.resolve) +CONTAINMENT_AMBIGUOUS module=v2.compiler.emit_host caller=run_test_claim_emit_vs_eval_for_claim callee=emit at=/workspace/src/v2/compiler/emit_host.dag@18124 via=n/a import_chain=v2.compiler.emit (v2.compiler.emit.emit) +CONTAINMENT_AMBIGUOUS module=tools.emit_host_transport caller=expected_stdout_nul_run callee=repeat_string at=/workspace/dag/tools/emit_host_transport.dag@4201 via=n/a import_chain=std.render_repeat_string_bootstrap (std.render_repeat_string_bootstrap.repeat_string) +CONTAINMENT_AMBIGUOUS module=gunbc.live_deploy.emit caller=apply_systemd_unit_write_step callee=render at=/workspace/dag/gunbc/live_deploy/emit.dag@8368 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.live_deploy.emit caller=apply_tree_sync_unit_write_step callee=render at=/workspace/dag/gunbc/live_deploy/emit.dag@11075 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.live_deploy.emit caller=live_deploy_systemd_unit_for callee=render at=/workspace/dag/gunbc/live_deploy/emit.dag@28767 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.live_deploy.emit caller=live_deploy_tree_sync_unit_for callee=render at=/workspace/dag/gunbc/live_deploy/emit.dag@28916 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.falsifier_workflow caller=native_cache_cold_control_invoke callee=ci_repo_root_shell at=/workspace/dag/gunbc/falsifier_workflow.dag@5533 via=n/a import_chain=gunbc.ci_spec (gunbc.ci_spec.ci_repo_root_shell) +CONTAINMENT_AMBIGUOUS module=gunbc.fleet_converge_emit caller=fleet_converge_sh_for callee=render at=/workspace/dag/gunbc/fleet_converge_emit.dag@8529 via=n/a import_chain=std.layout (std.layout.render) +CONTAINMENT_AMBIGUOUS module=gunbc.host_runner_memory_cap_plan_emit caller=expected_runner_memory_cap_apply_sheet callee=render at=/workspace/dag/gunbc/host_runner_memory_cap_plan_emit.dag@4119 via=n/a import_chain=std.layout (std.layout.render) + +SILENT_PICK_FN_PARENT_FIRST_HIT module=gunbc.falsifier_workflow name=ci_repo_root_shell parent_matches=2 chosen_parent=gunbc.ci_spec diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index ba9742c10b4..c90dd94193a 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -155,6 +155,7 @@ type CompilerDiagnostic | VariantCollision { variant: String, enum1: String, enum2: String, span: SourceSpan } | SoleConstructorViolation { type_name: String, span: SourceSpan } | UnlistedImportUse { name: String, span: SourceSpan } + | AmbiguousReference { name: String, candidates: List, span: SourceSpan } type ErrorNode { diagnostic: CompilerDiagnostic @@ -186,6 +187,7 @@ fn diagnostic_to_span(d: CompilerDiagnostic) -> SourceSpan { VariantCollision { variant: _, enum1: _, enum2: _, span: s } => s SoleConstructorViolation { type_name: _, span: s } => s UnlistedImportUse { name: _, span: s } => s + AmbiguousReference { name: _, candidates: _, span: s } => s } } @@ -210,6 +212,7 @@ fn diagnostic_to_message(d: CompilerDiagnostic) -> String { VariantCollision { variant: v, enum1: e1, enum2: e2, span: _ } => concat("variant '", v, "' appears in both '", e1, "' and '", e2, "'") SoleConstructorViolation { type_name: t, span: _ } => concat("sole_constructor type '", t, "' cannot be constructed outside its defining module") UnlistedImportUse { name: n, span: _ } => concat("unlisted import use '", n, "' (referenced but not in any import's name list)") + AmbiguousReference { name: n, candidates: cs, span: _ } => concat("ambiguous reference '", n, "': ", to_string(value: cs |> count), " candidates: ", join(cs, separator: ", "), " — qualify by containment path, alias, or rename") } } diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index f39d3918558..979f1577294 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -1,6 +1,6 @@ module v1.compiler.infer_env -import v1.std.core { Node, NewlineIndex, InternTable, empty_intern_table, merge_intern_tables, intern, intern_find, intern_str, source_text_at, authored_name_at, find_child_named, Cardinality, Connective, ExprData, InferredNode, kernel_span, module_path_segments, param_node_name_at, param_node_type_expr } +import v1.std.core { Node, NewlineIndex, InternTable, empty_intern_table, merge_intern_tables, intern, intern_find, intern_str, source_text_at, authored_name_at, find_child_named, Cardinality, Connective, ExprData, InferredNode, kernel_span, module_path_segments, param_node_name_at, param_node_type_expr, CompilerDiagnostic, AmbiguousReference, UnresolvedType } import std.types { SourceSpan, is_kernel_type } @@ -554,6 +554,58 @@ fn global_bare_nearest_ancestor(env_module_path: String, candidates: List) -> List { + let env_segs = module_path_segments(path: env_module_path) + candidates |> filter(cand => + let cand_segs = module_path_segments(path: cand.module_path) + segment_lcp_len(a: cand_segs, b: env_segs) == (cand_segs |> count) + ) +} + +fn global_bare_unique_chain_candidate(env_module_path: String, candidates: List) -> GlobalBareCandidate? { + let chain = global_bare_chain_candidates(env_module_path: env_module_path, candidates: candidates) + match (chain |> count) == 1 { + true => chain |> first + false => Absent + } +} + +fn global_bare_policy_candidate(env_module_path: String, candidates: List) -> GlobalBareCandidate? { + if name_resolution_policy_is_namespace_only() { + global_bare_unique_chain_candidate(env_module_path: env_module_path, candidates: candidates) + } else { + global_bare_nearest_ancestor_candidate(env_module_path: env_module_path, candidates: candidates) + } +} + +fn global_bare_strict_ambiguity_candidates(env: TypeEnv, name: String) -> List { + if name_resolution_policy_is_namespace_only() == false { + return [] + } + match map_get(env.symbol_index.global_bare, name) { + Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => + let chain = global_bare_chain_candidates(env_module_path: env.module_path, candidates: cands) + if (chain |> count) == 1 { + [] + } else { + let pool = if (chain |> count) >= 2 { chain } else { cands } + pool |> map(c => concat(c.module_path, ".", name)) + } + _ => [] + } +} + +fn bare_name_miss_diagnostic(env: TypeEnv, name: String, span: SourceSpan) -> CompilerDiagnostic { + let cands = global_bare_strict_ambiguity_candidates(env: env, name: name) + if cands |> count > 0 { + AmbiguousReference { name: name, candidates: cands, span: span } + } else { + UnresolvedType { name: name, span: span } + } +} + fn global_bare_owner_module(env: TypeEnv, owner_module_path: String, name: String) -> String? { match map_get(env.symbol_index.global_bare, name) { Present { value: GlobalBareUniqueBinding { module_path: mp, binding: b } } => @@ -562,7 +614,7 @@ fn global_bare_owner_module(env: TypeEnv, owner_module_path: String, name: Strin false => Absent } Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => - match global_bare_nearest_ancestor_candidate(env_module_path: owner_module_path, candidates: cands) { + match global_bare_policy_candidate(env_module_path: owner_module_path, candidates: cands) { Present { value: cand } => match binding_declares_name(binding: cand.binding, name: name, source_indices: env.source_indices) { true => Present { value: cand.module_path } @@ -709,10 +761,17 @@ fn global_bare_lookup(env: TypeEnv, name: String) -> TypeBinding? { match map_get(env.symbol_index.global_bare, name) { Present { value: GlobalBareUniqueBinding { module_path: _, binding: binding } } => Present { value: binding } Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => { - if resolution_silent_pick_is_enabled() && (cands |> count) >= 2 { - record_global_bare_ambiguous_silent_pick(env_module_path: env.module_path, name: name, cands: cands) + if name_resolution_policy_is_namespace_only() { + match global_bare_unique_chain_candidate(env_module_path: env.module_path, candidates: cands) { + Present { value: cand } => Present { value: cand.binding } + Absent => Absent + } + } else { + if resolution_silent_pick_is_enabled() && (cands |> count) >= 2 { + record_global_bare_ambiguous_silent_pick(env_module_path: env.module_path, name: name, cands: cands) + } + global_bare_nearest_ancestor(env_module_path: env.module_path, candidates: cands) } - global_bare_nearest_ancestor(env_module_path: env.module_path, candidates: cands) } Absent => Absent } @@ -721,7 +780,11 @@ fn global_bare_lookup(env: TypeEnv, name: String) -> TypeBinding? { fn global_bare_is_ambiguous(env: TypeEnv, name: String) -> Bool { match map_get(env.symbol_index.global_bare, name) { Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => - global_bare_nearest_ancestor(env_module_path: env.module_path, candidates: cands) == none + if name_resolution_policy_is_namespace_only() { + global_bare_unique_chain_candidate(env_module_path: env.module_path, candidates: cands) == none + } else { + global_bare_nearest_ancestor(env_module_path: env.module_path, candidates: cands) == none + } Present { value: GlobalBareUniqueBinding { module_path: _, binding: _ } } => false Absent => false } diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index c0fe78666b2..18a5556cac5 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -23,7 +23,7 @@ import v1.std.core { has_inferred, is_compiler_error, InferredNode, Resolved, CompilerError, TypeVariable, Cardinality, Required, CardOptional, ErrorNode, make_error_node, is_error_diagnostic, - InternalError, TypeMismatch, VariantCollision, SoleConstructorViolation, UnresolvedType, FieldNotFound, MissingField, + InternalError, TypeMismatch, VariantCollision, SoleConstructorViolation, UnresolvedType, FieldNotFound, MissingField, AmbiguousReference, resource_use_name_at, resource_use_resource, kernel_type_set, is_kernel_type, is_child_accessor_in_model, is_tree_size_reducing, is_property_contraction, @@ -86,7 +86,7 @@ import v1.compiler.infer_env { merge_type_env_cache_guarded, GuardedTypeEnvCacheMerge, TypeEnvCacheMergeConflict, str_bindings_from_bindings, is_recursive_type, is_recursive_type_by_name, lookup_type, lookup_type_by_name, lookup_type_for, - global_bare_is_ambiguous, + global_bare_is_ambiguous, global_bare_strict_ambiguity_candidates, bare_name_miss_diagnostic, put_inductive_field, put_inductive_field_cross, merge_inductive_fields, inductive_fields_for, inductive_fields_list_to_map, GlobalBareLookupState, GlobalBareUniqueBinding, GlobalBareAmbiguousBinding, @@ -131,7 +131,8 @@ import v1.compiler.infer_resolve { } import v1.compiler.infer_sigs { ResolvedFuncSig, ResolvedFuncEnv, ResolveFuncSigsResult, - resolve_func_sigs, flatten_parent_envs + resolve_func_sigs, flatten_parent_envs, + FuncSigLookup, FuncSigResolved, FuncSigUnresolved, FuncSigAmbiguous } import v1.compiler.infer_emit_info { TypeRepr, StructRepr, EnumRepr, @@ -168,7 +169,7 @@ import v1.compiler.infer_lookup { field_summary_for_type, lookup_field_type_node, lookup_coproduct_common_field_node, product_field_result_type, map_value_type_in_env, map_key_type_in_env, set_element_type_in_env, - lookup_in_scope, lookup_func_sig, + lookup_in_scope, lookup_func_sig, func_sig_if_resolved, global_bare_callable_node } import v1.compiler.infer_access { @@ -565,6 +566,13 @@ fn inference_error(message: String, span: SourceSpan, module_name: String) -> Er make_error_node(diagnostic: InternalError { message: message, span: span }, module_name: module_name) } +fn ambiguous_reference_refusal(name: String, candidates: List, span: SourceSpan, scope: InferScope) -> InferResult { + InferResult { + typed: semantic_expr_error_node(message: concat("ambiguous reference '", name, "'"), span: span), + diagnostics: [make_error_node(diagnostic: AmbiguousReference { name: name, candidates: candidates, span: span }, module_name: scope.module_name)] + } +} + fn type_mismatch_error(expected: String, got: String, span: SourceSpan, module_name: String) -> ErrorNode { make_error_node(diagnostic: TypeMismatch { expected: expected, got: got, span: span }, module_name: module_name) } @@ -1596,7 +1604,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { } Absent => match lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name) { - Present { value: fsig } => + FuncSigResolved { sig: fsig } => if (fsig.params |> count) == 0 { ok_infer(texpr: make_named_expr_node( name: name, @@ -1616,7 +1624,9 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { name_span: span )) } - Absent => + FuncSigAmbiguous { candidates: ambiguous_fn_candidates } => + ambiguous_reference_refusal(name: name, candidates: ambiguous_fn_candidates, span: span, scope: scope) + FuncSigUnresolved => match lookup_binding_by_name(env: scope.type_env, name: name) { Present { value: gbinding } => let scope_parent = lookup_variant_parent_enum(scope: scope, name: name) @@ -1779,7 +1789,12 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { let span = texpr.span let call_args = texpr.children - let sig = body_shadow_aware_func_sig(scope: scope, func_name: func_name) + let call_sig_lookup = body_shadow_aware_func_sig(scope: scope, func_name: func_name) + match call_sig_lookup { + FuncSigAmbiguous { candidates: ambiguous_fn_candidates } => + ambiguous_reference_refusal(name: func_name, candidates: ambiguous_fn_candidates, span: span, scope: scope) + _ => + let sig = func_sig_if_resolved(lookup: call_sig_lookup) let sig_params = match sig { Present { value: s } => s.params Absent => [] @@ -2178,6 +2193,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { } } } + } ExprMethodCall { method_semantics: _ } => let method_name = expr_method_name_at(texpr: texpr, source_indices: scope.type_env.source_indices) @@ -3268,6 +3284,12 @@ fn infer_record_lit(type_name: String?, field_inits: List, span: SourceSpa && variant_owner_node(scope: scope, name: tn_str) != none let presence_name_is_ambiguous = global_bare_is_ambiguous(env: scope.type_env, name: tn_str) && map_get(scope.type_env.str_bindings, tn_str) == none + let presence_ambiguity_refusal_diags = if tn_str != "" && map_get(scope.type_env.str_bindings, tn_str) == none { + let strict_cands = global_bare_strict_ambiguity_candidates(env: scope.type_env, name: tn_str) + if strict_cands |> count > 0 { + [make_error_node(diagnostic: AmbiguousReference { name: tn_str, candidates: strict_cands, span: name_span }, module_name: scope.module_name)] + } else { [] } + } else { [] } let missing_field_diags = if tn_str == "" || (presence_fields |> count) == 0 || is_zero_field_variant_tag_reference || presence_name_is_ambiguous { [] } else { @@ -3438,7 +3460,7 @@ fn infer_record_lit(type_name: String?, field_inits: List, span: SourceSpa let type_diags = match effective_lookup { Present { value: _ } => [] Absent => [make_error_node( - diagnostic: UnresolvedType { name: type_name.value, span: span }, + diagnostic: bare_name_miss_diagnostic(env: scope.type_env, name: type_name.value, span: span), module_name: scope.module_name )] } @@ -3463,9 +3485,10 @@ fn infer_record_lit(type_name: String?, field_inits: List, span: SourceSpa span: span, name_span: name_span ) + let presence_ambiguity_diags = if (type_diags |> count) > 0 { [] } else { presence_ambiguity_refusal_diags } InferResult { typed: texpr, - diagnostics: concat(concat(concat(fi_diags, type_diags), sole_ctor_diags), missing_field_diags) + diagnostics: concat(concat(concat(concat(fi_diags, type_diags), sole_ctor_diags), missing_field_diags), presence_ambiguity_diags) } } } @@ -3683,7 +3706,7 @@ fn build_per_field_for_let(val: Node, binding_name: String, ctx: DescentContext) match val.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: val, source_indices: ctx.type_env.source_indices) - match lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee) { + match func_sig_if_resolved(lookup: lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee)) { Present { value: sig } => if sig.output_provenance |> count > 1 { let return_type = sig.inferred @@ -3701,7 +3724,7 @@ fn build_per_field_for_let(val: Node, binding_name: String, ctx: DescentContext) } ExprMethodCall { method_semantics: _ } => let callee = expr_method_name_at(texpr: val, source_indices: ctx.type_env.source_indices) - match lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee) { + match func_sig_if_resolved(lookup: lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee)) { Present { value: sig } => if sig.output_provenance |> count > 1 { let return_type = sig.inferred @@ -3851,7 +3874,7 @@ fn classify_let_value(val: Node, ctx: DescentContext) -> SubValueRelation? { } ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: val, source_indices: ctx.type_env.source_indices) - let from_provenance = match lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee) { + let from_provenance = match func_sig_if_resolved(lookup: lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee)) { Present { value: sig } => match sig.output_provenance |> first { Present { value: param_map } => @@ -4118,7 +4141,7 @@ fn classify_argument(arg_expr: Node, param_name: String, ctx: DescentContext) -> ArithmeticDescent { param: _, factor: _ } => inner_rel PreservedValue => let callee = expr_call_func_at(texpr: arg_expr, source_indices: ctx.type_env.source_indices) - let from_provenance = match lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee) { + let from_provenance = match func_sig_if_resolved(lookup: lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee)) { Present { value: sig } => match sig.output_provenance |> first { Present { value: param_map } => @@ -4441,7 +4464,7 @@ fn annotate_descent(body: Node, ctx: DescentContext) -> Node { let scrut_variant_sig = match scrut.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: scrut, source_indices: ctx.type_env.source_indices) - match lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee) { + match func_sig_if_resolved(lookup: lookup_func_sig(func_env: ctx.func_env, type_env: ctx.type_env, name: callee)) { Present { value: sig } => if map_keys(sig.variant_provenance) |> count > 0 { Present { value: sig } } else { none } @@ -5049,7 +5072,7 @@ fn method_call_args_by_name(call: Node, func_env: ResolvedFuncEnv, type_env: Typ let mname = expr_method_name_at(texpr: call, source_indices: source_indices) let recv = method_receiver(texpr: call) let mc_args = method_arg_nodes(texpr: call) - let base = match lookup_func_sig(func_env: func_env, type_env: type_env, name: mname) { + let base = match func_sig_if_resolved(lookup: lookup_func_sig(func_env: func_env, type_env: type_env, name: mname)) { Present { value: sig } => match sig.params |> first { Present { value: first_p } => @@ -5068,7 +5091,7 @@ fn method_call_args_by_name(call: Node, func_env: ResolvedFuncEnv, type_env: Typ } fn compose_callee_provenance(callee: String, arg_by_name: Map, param_names: List, param_types: Map, type_env: TypeEnv, func_env: ResolvedFuncEnv, let_prov: Map>) -> Map { - match lookup_func_sig(func_env: func_env, type_env: type_env, name: callee) { + match func_sig_if_resolved(lookup: lookup_func_sig(func_env: func_env, type_env: type_env, name: callee)) { Present { value: sig } => match sig.output_provenance |> first { Present { value: callee_prov } => @@ -5319,7 +5342,7 @@ fn collect_variant_constructors(body: Node, parent_enum: String, param_names: Li } ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: body, source_indices: type_env.source_indices) - match lookup_func_sig(func_env: func_env, type_env: type_env, name: callee) { + match func_sig_if_resolved(lookup: lookup_func_sig(func_env: func_env, type_env: type_env, name: callee)) { Present { value: sig } => if authored_name_at(source_indices: type_env.source_indices, node: sig.inferred) == parent_enum && (map_keys(sig.variant_provenance) |> count > 0) { @@ -5738,9 +5761,9 @@ type SigParamSplit { generic_names: List } -fn body_shadow_aware_func_sig(scope: InferScope, func_name: String) -> ResolvedFuncSig? { +fn body_shadow_aware_func_sig(scope: InferScope, func_name: String) -> FuncSigLookup { match map_get(scope.body_locals, func_name) { - Present { value: _ } => Absent + Present { value: _ } => FuncSigUnresolved Absent => lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: func_name) } } diff --git a/src/v1/04_lookup.dag b/src/v1/04_lookup.dag index 974510006d5..283424c8726 100644 --- a/src/v1/04_lookup.dag +++ b/src/v1/04_lookup.dag @@ -39,7 +39,7 @@ import v1.compiler.infer_types { import v1.compiler.infer_env { TypeEnv, TypeBinding, authored_name, is_recursive_type, lookup_type, lookup_type_for, - GlobalBareLookupState, borrowed_generic_param_names, global_bare_nearest_ancestor_candidate, lookup_binding_by_name, lookup_binding_by_name_local, qualified_all_but_last, qualify_borrowed_type_names, symbol_index_lookup + GlobalBareLookupState, borrowed_generic_param_names, global_bare_policy_candidate, lookup_binding_by_name, lookup_binding_by_name_local, qualified_all_but_last, qualify_borrowed_type_names, symbol_index_lookup } import v1.compiler.infer_emit_info { @@ -47,7 +47,8 @@ import v1.compiler.infer_emit_info { } import v1.compiler.infer_sigs { - ResolvedFuncSig, ResolvedFuncEnv, lookup_resolved_sig + ResolvedFuncSig, ResolvedFuncEnv, lookup_resolved_sig, + FuncSigLookup, FuncSigResolved, FuncSigUnresolved, FuncSigAmbiguous } import v1.compiler.infer_service { @@ -81,10 +82,23 @@ fn lookup_in_scope(locals: Map, name: String) -> Node? { } } -fn lookup_func_sig(func_env: ResolvedFuncEnv, type_env: TypeEnv, name: String) -> ResolvedFuncSig? { +fn lookup_func_sig(func_env: ResolvedFuncEnv, type_env: TypeEnv, name: String) -> FuncSigLookup { match lookup_resolved_sig(env: func_env, name: name) { - Present { value: sig } => Present { value: sig } - Absent => func_sig_from_global_bare(type_env: type_env, name: name) + FuncSigResolved { sig: sig } => FuncSigResolved { sig: sig } + FuncSigAmbiguous { candidates: cands } => FuncSigAmbiguous { candidates: cands } + FuncSigUnresolved => + match func_sig_from_global_bare(type_env: type_env, name: name) { + Present { value: sig } => FuncSigResolved { sig: sig } + Absent => FuncSigUnresolved + } + } +} + +fn func_sig_if_resolved(lookup: FuncSigLookup) -> ResolvedFuncSig? { + match lookup { + FuncSigResolved { sig: sig } => Present { value: sig } + FuncSigUnresolved => Absent + FuncSigAmbiguous { candidates: _ } => Absent } } @@ -105,7 +119,7 @@ fn borrowed_census_decl(type_env: TypeEnv, name: String) -> BorrowedCensusDecl? Present { value: GlobalBareUniqueBinding { module_path: mp, binding: b } } => Present { value: BorrowedCensusDecl { owner_module_path: mp, node: b.resolved } } Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => - match global_bare_nearest_ancestor_candidate(env_module_path: type_env.module_path, candidates: cands) { + match global_bare_policy_candidate(env_module_path: type_env.module_path, candidates: cands) { Present { value: cand } => Present { value: BorrowedCensusDecl { owner_module_path: cand.module_path, node: cand.binding.resolved } } Absent => none diff --git a/src/v1/04_method.dag b/src/v1/04_method.dag index e043f46e156..35c47bbfb29 100644 --- a/src/v1/04_method.dag +++ b/src/v1/04_method.dag @@ -64,6 +64,7 @@ fn builtin_function_registry() -> Map { let m = map_insert(m, "chars_to_string", string_type) let m = map_insert(m, "record_source_chars_index_lookup", unit_type) let m = map_insert(m, "resolution_silent_pick_is_enabled", bool_type) + let m = map_insert(m, "name_resolution_policy_is_namespace_only", bool_type) let m = map_insert(m, "resolution_silent_pick_record_global_bare_lcp_pick", unit_type) let m = map_insert(m, "resolution_silent_pick_record_global_bare_lcp_tie", unit_type) let m = map_insert(m, "resolution_silent_pick_record_fn_parent_first_hit", unit_type) diff --git a/src/v1/04_resolve.dag b/src/v1/04_resolve.dag index d587a169836..2aa7dd95cfc 100644 --- a/src/v1/04_resolve.dag +++ b/src/v1/04_resolve.dag @@ -43,7 +43,7 @@ import std.induction { SubValueUnknown } import v1.compiler.infer_env { TypeEnv, TypeBinding, lookup_type, lookup_type_by_name, lookup_type_for, is_recursive_type, is_recursive_type_by_name, is_recursive_type_for, authored_name, - env_with_type_variable_bindings + env_with_type_variable_bindings, bare_name_miss_diagnostic } fn is_unit_variant_node(variant: Node) -> Bool { @@ -599,7 +599,7 @@ fn resolve_node_bounded(n: Node, env: TypeEnv, module_name: String, depth: Int, Absent => NodeResolveResult { resolved: n, - diagnostics: [make_error_node(diagnostic: UnresolvedType { name: authored_name(env: env, node: n), span: n.span }, module_name: module_name)] + diagnostics: [make_error_node(diagnostic: bare_name_miss_diagnostic(env: env, name: authored_name(env: env, node: n), span: n.span), module_name: module_name)] } } } diff --git a/src/v1/04_sigs.dag b/src/v1/04_sigs.dag index e013333fe9e..995450501cb 100644 --- a/src/v1/04_sigs.dag +++ b/src/v1/04_sigs.dag @@ -67,6 +67,42 @@ type ParentSigScan { first_parent: String? } +data func_sig_lookup_outcome_note: String = "namespace-resolution-design.md 13 / 8 step 1, fn path: ResolvedFuncSig? overloaded Absent as 'keep looking' (the census fallback fires on it), so a refusal had nowhere to go — the first-hit over func_env.parents was the fn silent-pick class (fn_parent_first_hit) with NO refusal arm at all. FuncSigLookup is the 3-state outcome: FuncSigResolved binds, FuncSigUnresolved means genuinely-no-sig (census fallback may still run), FuncSigAmbiguous carries the full candidate list and REFUSES — it never falls through to a fallback. Under the default ImportScoped policy the Ambiguous arm is unreachable (first-hit preserved verbatim); under NamespaceOnlyY exactly-one match across the flat parent closure resolves, two-plus refuses. Own-module local hit stays first on both arms: the killed class is the pick among >=2 PARENT matches (the census's fn_parent_first_hit definition), not own-decl precedence. Analysis-only consumers (provenance/descent enrichment) project through func_sig_if_resolved in infer_lookup — they never fabricate a bind; the semantic bind sites in 04_infer match the full outcome and emit the typed AmbiguousReference." + +type FuncSigLookup + = FuncSigResolved { sig: ResolvedFuncSig } + | FuncSigUnresolved + | FuncSigAmbiguous { candidates: List } + +type ChainSigScan { + first_sig: ResolvedFuncSig? + owners: List +} + +fn lookup_resolved_sig_unique_across_parents(env: ResolvedFuncEnv, name: String) -> FuncSigLookup { + let scan = fold(env.parents, init: ChainSigScan { first_sig: none, owners: [] }, f: (acc, p) => + match map_get(p.local, name) { + Present { value: sig } => + ChainSigScan { + first_sig: if acc.first_sig != none { acc.first_sig } else { Present { value: sig } }, + owners: concat(acc.owners, [p.name]) + } + Absent => acc + } + ) + let owner_count = scan.owners |> count + if owner_count == 0 { + FuncSigUnresolved + } else if owner_count == 1 { + match scan.first_sig { + Present { value: sig } => FuncSigResolved { sig: sig } + Absent => FuncSigUnresolved + } + } else { + FuncSigAmbiguous { candidates: scan.owners |> map(o => concat(o, ".", name)) } + } +} + fn lookup_resolved_sig_with_telemetry(env: ResolvedFuncEnv, name: String) -> ResolvedFuncSig? { let scan = fold(env.parents, init: ParentSigScan { sig: none, match_count: 0, first_parent: none }, f: (acc, p) => match map_get(p.local, name) { @@ -94,19 +130,27 @@ fn lookup_resolved_sig_with_telemetry(env: ResolvedFuncEnv, name: String) -> Res scan.sig } -fn lookup_resolved_sig(env: ResolvedFuncEnv, name: String) -> ResolvedFuncSig? { +fn lookup_resolved_sig(env: ResolvedFuncEnv, name: String) -> FuncSigLookup { match map_get(env.local, name) { - Present { value: sig } => Present { value: sig } + Present { value: sig } => FuncSigResolved { sig: sig } Absent => - if resolution_silent_pick_is_enabled() { - lookup_resolved_sig_with_telemetry(env: env, name: name) + if name_resolution_policy_is_namespace_only() { + lookup_resolved_sig_unique_across_parents(env: env, name: name) } else { - fold(env.parents, init: none_resolved_sig(), f: (acc, p) => - match acc { - Present { value: sig } => Present { value: sig } - Absent => map_get(p.local, name) - } - ) + let first_hit = if resolution_silent_pick_is_enabled() { + lookup_resolved_sig_with_telemetry(env: env, name: name) + } else { + fold(env.parents, init: none_resolved_sig(), f: (acc, p) => + match acc { + Present { value: sig } => Present { value: sig } + Absent => map_get(p.local, name) + } + ) + } + match first_hit { + Present { value: sig } => FuncSigResolved { sig: sig } + Absent => FuncSigUnresolved + } } } } diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index c908b64970c..ef43281018d 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -64,7 +64,7 @@ import v1.compiler.infer_items { import v1.compiler.infer_service { UniqueAccum, OpEntry, is_typed_service_call_receiver, extract_typed_service_name } -import v1.compiler.infer_lookup { lookup_func_sig } +import v1.compiler.infer_lookup { lookup_func_sig, func_sig_if_resolved } import v1.compiler.infer { InferScope, @@ -329,7 +329,7 @@ fn lookup_item(registry: Map, name: String) -> ItemInfo? { } fn lookup_func_sig_in_scope(scope: InferScope, name: String) -> ResolvedFuncSig? { - lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name) + func_sig_if_resolved(lookup: lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name)) } fn typed_named_arg_matches(arg: Node, name: String, source_indices: Map) -> Bool { diff --git a/src/v1/runtime_rust.dag b/src/v1/runtime_rust.dag index b938bf39487..893da99a98f 100644 --- a/src/v1/runtime_rust.dag +++ b/src/v1/runtime_rust.dag @@ -573,6 +573,20 @@ fn rt_resolution_silent_pick_telemetry() -> String { " });\n", " });\n", "}\n\n", + "// NameResolutionPolicy gate (namespace-resolution-design.md 13, 8 step 1): default\n", + "// false = ImportScoped (today's resolver, byte-for-byte). True = NamespaceOnlyY —\n", + "// strict unique-on-chain in the type/value path and exactly-one across the fn\n", + "// parent closure, with typed Ambiguous refusals. Host-side setter only; no .dag\n", + "// surface can flip it (no escape hatch — a flip is an explicit host decision).\n", + "thread_local! {\n", + " static NAME_RESOLUTION_POLICY_NAMESPACE_ONLY: Cell = const { Cell::new(false) };\n", + "}\n\n", + "pub fn name_resolution_policy_set_namespace_only(enabled: bool) {\n", + " NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.set(enabled));\n", + "}\n\n", + "pub fn name_resolution_policy_is_namespace_only() -> bool {\n", + " NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.get())\n", + "}\n\n", "pub fn resolution_silent_pick_record_fn_parent_first_hit(\n", " env_module_path: String,\n", " name: String,\n", diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 2a040926bf6..0e5792b5218 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -19,6 +19,7 @@ use crate::v1_compiler_infer_env::{ symbol_index_lookup, GlobalBareLookupState, SymbolIndex, TypeEnv, }; use crate::v1_compiler_infer_items::{item_kind, ItemInfo, ItemKind, ResolvedGraph, TypedModule}; +use crate::v1_compiler_infer_lookup::func_sig_if_resolved; use crate::v1_compiler_infer_lookup::global_bare_callable_node; use crate::v1_compiler_infer_method::infer_builtin_call_type; use crate::v1_compiler_infer_sigs::{lookup_resolved_sig, ResolvedFuncEnv, ResolvedFuncSig}; @@ -2768,6 +2769,7 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::VariantCollision { .. } => "VariantCollision", CompilerDiagnostic::SoleConstructorViolation { .. } => "SoleConstructorViolation", CompilerDiagnostic::UnlistedImportUse { .. } => "UnlistedImportUse", + CompilerDiagnostic::AmbiguousReference { .. } => "AmbiguousReference", }; let name = match d.diagnostic.as_ref() { CompilerDiagnostic::UnresolvedImport { module_path, .. } => module_path.clone(), @@ -2791,6 +2793,7 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::VariantCollision { variant, .. } => variant.clone(), CompilerDiagnostic::SoleConstructorViolation { type_name, .. } => type_name.clone(), CompilerDiagnostic::UnlistedImportUse { name, .. } => name.clone(), + CompilerDiagnostic::AmbiguousReference { name, .. } => name.clone(), }; (class.to_string(), name) } @@ -19480,7 +19483,8 @@ pub fn resolution_divergence_census_from_ctx( } for (callee, call_node) in calls { out.sites_checked += 1; - let import_sig = lookup_resolved_sig(func_env.clone(), callee.clone()); + let import_sig = + func_sig_if_resolved(lookup_resolved_sig(func_env.clone(), callee.clone())); let import_binding = import_sig.as_ref().and_then(|sig| { import_chain_owner(&func_env, &callee) .map(|owner| fn_binding_from_sig(&owner, &callee, sig)) @@ -20503,9 +20507,9 @@ pub fn format_walk_target_alias_plan(plan: &WalkTargetAliasPlan) -> String { mod resolution_divergence_census_tests { use super::{ build_module_item_index, containment_resolve_fn_v1, containment_resolve_fn_v1_for_module, - import_chain_owner, lookup_resolved_sig, resolution_divergence_census_live, - resolution_divergence_silent_pick_refusal, whole_tree_resolved_ctx, ContainmentResolve, - ResolutionDivergenceBucket, WholeTreeCtx, + func_sig_if_resolved, import_chain_owner, lookup_resolved_sig, + resolution_divergence_census_live, resolution_divergence_silent_pick_refusal, + whole_tree_resolved_ctx, ContainmentResolve, ResolutionDivergenceBucket, WholeTreeCtx, }; use crate::v1_interpreter::ExecutionMode::Wet; @@ -20574,8 +20578,11 @@ fn caller() -> Bool { .iter() .find(|m| m.type_env.module_path == "test.posctl.middle.leaf") .expect("leaf module must resolve"); - let import_sig = - lookup_resolved_sig(leaf.func_env.clone(), "lex_target".to_string()).expect("import"); + let import_sig = func_sig_if_resolved(lookup_resolved_sig( + leaf.func_env.clone(), + "lex_target".to_string(), + )) + .expect("import"); let import_owner = import_chain_owner(&leaf.func_env, "lex_target").expect("import owner"); let import_arity = import_sig.params.len(); let item_index = build_module_item_index(&ctx); @@ -26571,13 +26578,17 @@ mod sigs_env_flat_parents { let b = w2_env(&format!("b{i}"), &[], vec![prev.clone()]); prev = w2_env(&format!("j{i}"), &[], vec![a, b]); } - let deep_hit = crate::v1_compiler_infer_sigs::lookup_resolved_sig( - prev.clone(), - "bottom_fn".to_string(), + let deep_hit = crate::v1_compiler_infer_lookup::func_sig_if_resolved( + crate::v1_compiler_infer_sigs::lookup_resolved_sig( + prev.clone(), + "bottom_fn".to_string(), + ), ); - let miss = crate::v1_compiler_infer_sigs::lookup_resolved_sig( - prev.clone(), - "absent_fn".to_string(), + let miss = crate::v1_compiler_infer_lookup::func_sig_if_resolved( + crate::v1_compiler_infer_sigs::lookup_resolved_sig( + prev.clone(), + "absent_fn".to_string(), + ), ); let _ = tx.send(( prev.parents.len(), @@ -26611,8 +26622,10 @@ mod sigs_env_flat_parents { #[test] fn flat_parents_preserve_deep_first_last_import_first_shadowing() { let read = |env: &Rc, f: &str| { - crate::v1_compiler_infer_sigs::lookup_resolved_sig(env.clone(), f.to_string()) - .map(|s| s.inferred.name.clone()) + crate::v1_compiler_infer_lookup::func_sig_if_resolved( + crate::v1_compiler_infer_sigs::lookup_resolved_sig(env.clone(), f.to_string()), + ) + .map(|s| s.inferred.name.clone()) }; let b = w2_env("b", &[("f", "FromB"), ("g", "FromBg")], vec![]); diff --git a/src/v1/stage0/src/extdeps_languages_rust_emit.rs b/src/v1/stage0/src/extdeps_languages_rust_emit.rs index 6c1d04341f0..43a661589bc 100644 --- a/src/v1/stage0/src/extdeps_languages_rust_emit.rs +++ b/src/v1/stage0/src/extdeps_languages_rust_emit.rs @@ -454,7 +454,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": "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": "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}])) .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index ca95549d11d..31746b8db65 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -38,7 +38,7 @@ use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index}; pub use crate::v1_compiler_infer_env::{GlobalBareLookupState, TypeBinding, TypeEnv}; pub use crate::v1_compiler_infer_items::{ItemInfo, ResolvedGraph, TypedModule}; -pub use crate::v1_compiler_infer_lookup::lookup_func_sig; +pub use crate::v1_compiler_infer_lookup::{func_sig_if_resolved, lookup_func_sig}; pub use crate::v1_compiler_infer_service::{ extract_typed_service_name, is_typed_service_call_receiver, }; @@ -656,7 +656,11 @@ pub fn lookup_func_sig_in_scope( scope: Rc, name: String, ) -> Option> { - lookup_func_sig(scope.func_env.clone(), scope.type_env.clone(), name.clone()) + func_sig_if_resolved(lookup_func_sig( + scope.func_env.clone(), + scope.type_env.clone(), + name.clone(), + )) } pub fn typed_named_arg_matches( diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 5c5fcd55975..837af7babaa 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -53,15 +53,16 @@ use crate::v1_compiler_infer_env::GlobalBareLookupState::{ GlobalBareAmbiguousBinding, GlobalBareUniqueBinding, }; pub use crate::v1_compiler_infer_env::{ - binding_declares_name, empty_symbol_index, empty_type_env_cache, - env_with_type_variable_bindings, global_bare_is_ambiguous, inductive_fields_for, - inductive_fields_list_to_map, is_recursive_type, is_recursive_type_by_name, - lookup_binding_by_name, lookup_type, lookup_type_by_name, lookup_type_for, - merge_inductive_fields, merge_type_env_cache, merge_type_env_cache_guarded, node_with_children, - node_with_inferred, put_inductive_field, put_inductive_field_cross, qualified_all_but_last, - qualify_borrowed_inferred, qualify_borrowed_type_names, qualify_decl_reference_positions, - str_bindings_from_bindings, symbol_index_insert, symbol_index_insert_decl, - symbol_index_insert_service, symbol_index_lookup, + bare_name_miss_diagnostic, binding_declares_name, empty_symbol_index, empty_type_env_cache, + env_with_type_variable_bindings, global_bare_is_ambiguous, + global_bare_strict_ambiguity_candidates, inductive_fields_for, inductive_fields_list_to_map, + is_recursive_type, is_recursive_type_by_name, lookup_binding_by_name, lookup_type, + lookup_type_by_name, lookup_type_for, merge_inductive_fields, merge_type_env_cache, + merge_type_env_cache_guarded, node_with_children, node_with_inferred, put_inductive_field, + put_inductive_field_cross, qualified_all_but_last, qualify_borrowed_inferred, + qualify_borrowed_type_names, qualify_decl_reference_positions, str_bindings_from_bindings, + symbol_index_insert, symbol_index_insert_decl, symbol_index_insert_service, + symbol_index_lookup, }; pub use crate::v1_compiler_infer_env::{ GlobalBareCandidate, GlobalBareLookupState, GuardedTypeEnvCacheMerge, ServiceCensusEntry, @@ -76,10 +77,11 @@ pub use crate::v1_compiler_infer_items::{ }; pub use crate::v1_compiler_infer_lookup::KnownMethodResolution; pub use crate::v1_compiler_infer_lookup::{ - field_summary_for_type, global_bare_callable_node, lookup_coproduct_common_field_node, - lookup_field_type_node, lookup_func_sig, lookup_in_scope, map_key_type_in_env, - map_value_type_in_env, product_field_result_type, resolve_known_method_node, - resolve_method_receiver_type, resolve_scrutinee_type_node, set_element_type_in_env, + field_summary_for_type, func_sig_if_resolved, global_bare_callable_node, + lookup_coproduct_common_field_node, lookup_field_type_node, lookup_func_sig, lookup_in_scope, + map_key_type_in_env, map_value_type_in_env, product_field_result_type, + resolve_known_method_node, resolve_method_receiver_type, resolve_scrutinee_type_node, + set_element_type_in_env, }; pub use crate::v1_compiler_infer_method::{ builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, @@ -103,8 +105,13 @@ pub use crate::v1_compiler_infer_service::{ is_typed_service_call_receiver, service_op_entry, }; pub use crate::v1_compiler_infer_service::{OpEntry, ServiceMethodResult, UniqueAccum}; +use crate::v1_compiler_infer_sigs::FuncSigLookup::{ + FuncSigAmbiguous, FuncSigResolved, FuncSigUnresolved, +}; pub use crate::v1_compiler_infer_sigs::{flatten_parent_envs, resolve_func_sigs}; -pub use crate::v1_compiler_infer_sigs::{ResolveFuncSigsResult, ResolvedFuncEnv, ResolvedFuncSig}; +pub use crate::v1_compiler_infer_sigs::{ + FuncSigLookup, ResolveFuncSigsResult, ResolvedFuncEnv, ResolvedFuncSig, +}; pub use crate::v1_compiler_infer_types::KernelTypeBuild; pub use crate::v1_compiler_infer_types::{ bare_map_node, bare_set_node, callable_inferred, callable_return_type, child_type_node, @@ -125,8 +132,8 @@ use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::CallSemantics::{LookupCallSemantics, PlainCallSemantics}; use crate::v1_std_core::Cardinality::{CardOptional, Required}; use crate::v1_std_core::CompilerDiagnostic::{ - FieldNotFound, InternalError, MissingField, SoleConstructorViolation, TypeMismatch, - UnresolvedType, VariantCollision, + AmbiguousReference, FieldNotFound, InternalError, MissingField, SoleConstructorViolation, + TypeMismatch, UnresolvedType, VariantCollision, }; use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::ExprData::{ @@ -920,6 +927,31 @@ pub fn inference_error( ) } +pub fn ambiguous_reference_refusal( + name: String, + candidates: Rc>, + span: Rc, + scope: Rc, +) -> Rc { + Rc::new(InferResult { + typed: semantic_expr_error_node( + v1_rt::concat( + v1_rt::concat("ambiguous reference '".to_string(), name.clone()), + "'".to_string(), + ), + span.clone(), + ), + diagnostics: Rc::new(vec![make_error_node( + Rc::new(CompilerDiagnostic::AmbiguousReference { + name: name.clone(), + candidates: candidates.clone(), + span: span.clone(), + }), + scope.module_name.clone(), + )]), + }) +} + pub fn type_mismatch_error( expected: String, got: String, @@ -3134,12 +3166,14 @@ pub fn infer_expr( } } } - None => match lookup_func_sig( + None => match (*lookup_func_sig( scope.func_env.clone(), scope.type_env.clone(), name.clone(), - ) { - Some(fsig) => { + )) + .clone() + { + FuncSigLookup::FuncSigResolved { sig: fsig, .. } => { if ((fsig.params.clone().len() as i64) == 0) { ok_infer(make_named_expr_node( name.clone(), @@ -3174,7 +3208,16 @@ pub fn infer_expr( )) } } - None => { + FuncSigLookup::FuncSigAmbiguous { + candidates: ambiguous_fn_candidates, + .. + } => ambiguous_reference_refusal( + name.clone(), + ambiguous_fn_candidates.clone(), + span.clone(), + scope.clone(), + ), + FuncSigLookup::FuncSigUnresolved => { match lookup_binding_by_name(scope.type_env.clone(), name.clone()) { Some(gbinding) => { let scope_parent = @@ -3483,500 +3526,559 @@ pub fn infer_expr( expr_call_func_at(texpr.clone(), scope.type_env.clone().source_indices.clone()); let span = texpr.span.clone(); let call_args = texpr.children.clone(); - let sig = body_shadow_aware_func_sig(scope.clone(), func_name.clone()); - let sig_params = match sig.clone() { - Some(s) => s.params.clone(), - None => Rc::new(vec![]), - }; - let has_lambda = { - let mut __found = false; - for a in call_args.clone().iter().cloned() { - if is_lambda_expr(arg_value(a.clone())) { - __found = true; - break; - } - } - __found - }; - let call_method_args = Rc::new( - call_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let call_method_name = Some(func_name.clone()); - let call_fold_info = extract_fold_init_info( - call_method_name.clone(), - call_method_args.clone(), - 2, - scope.clone(), - expected.clone(), - ); - let call_fold_acc_type = match call_fold_info.clone() { - Some(fi) => resolved_type(arg_value(fi.typed_arg.clone())), - None => error_type(), - }; - let arg_call = if ((has_lambda.clone() && ((call_args.clone().len() as i64) >= 2)) - && (sig.clone() == None)) - { - Rc::new(ArgGenericFoldState { - subst: v1_rt::rc_empty_map::>(), - results: match call_args.clone().first().cloned() { - Some(first_arg) => { - let first_result = - infer_expr(arg_value(first_arg.clone()), scope.clone(), None); - let first_type = resolved_type(first_result.typed.clone()); - let elem_type = for_each_element_type_node( - first_type.clone(), - scope.type_env.clone().source_indices.clone(), - ); - let call_elem_provenance = derive_element_provenance( - first_result.typed.clone(), - elem_type.clone(), - scope.clone(), - ); - let remaining_results = infer_method_args_with_fold( - call_method_name.clone(), - call_method_args.clone(), - call_fold_info.clone(), - call_fold_acc_type.clone(), - elem_type.clone(), - call_elem_provenance.clone(), - scope.clone(), - ); - v1_rt::concat( - Rc::new(vec![Rc::new(ArgInferResult { - typed_arg: make_arg_node( - arg_name_at( - first_arg.clone(), - scope.type_env.clone().source_indices.clone(), - ), - first_result.typed.clone(), - first_arg.span.clone(), - first_arg.span.clone(), - ), - diagnostics: first_result.diagnostics.clone(), - })]), - remaining_results.clone(), - ) - } + let call_sig_lookup = body_shadow_aware_func_sig(scope.clone(), func_name.clone()); + match (*call_sig_lookup.clone()).clone() { + FuncSigLookup::FuncSigAmbiguous { + candidates: ambiguous_fn_candidates, + .. + } => ambiguous_reference_refusal( + func_name.clone(), + ambiguous_fn_candidates.clone(), + span.clone(), + scope.clone(), + ), + _ => { + let sig = func_sig_if_resolved(call_sig_lookup.clone()); + let sig_params = match sig.clone() { + Some(s) => s.params.clone(), None => Rc::new(vec![]), - }, - }) - } else { - { - let sig_split = split_sig_params( - sig_params.clone(), - scope.type_env.clone().source_indices.clone(), - ); - let value_params = sig_split.value_params.clone(); - let generic_names = sig_split.generic_names.clone(); - let final_state = Rc::new( + }; + let has_lambda = { + let mut __found = false; + for a in call_args.clone().iter().cloned() { + if is_lambda_expr(arg_value(a.clone())) { + __found = true; + break; + } + } + __found + }; + let call_method_args = Rc::new( call_args .clone() .iter() .cloned() - .enumerate() - .map(|(i, v)| (i as i64, v)) + .skip(1 as usize) .collect::>(), - ) - .iter() - .cloned() - .fold( + ); + let call_method_name = Some(func_name.clone()); + let call_fold_info = extract_fold_init_info( + call_method_name.clone(), + call_method_args.clone(), + 2, + scope.clone(), + expected.clone(), + ); + let call_fold_acc_type = match call_fold_info.clone() { + Some(fi) => resolved_type(arg_value(fi.typed_arg.clone())), + None => error_type(), + }; + let arg_call = if ((has_lambda.clone() + && ((call_args.clone().len() as i64) >= 2)) + && (sig.clone() == None)) + { Rc::new(ArgGenericFoldState { - results: Rc::new(vec![]), subst: v1_rt::rc_empty_map::>(), - }), - |st: Rc, pair: (i64, Rc)| { - let a = pair.1.clone(); - let formal_lookup = - value_params.clone().get(pair.0.clone() as usize).cloned(); - let formal_raw = match formal_lookup.clone() { - Some(p) => param_node_type_expr(p.clone()), - None => type_variable_node("callable_param".to_string()), - }; - let has_formal = match formal_lookup.clone() { - Some(_) => true, - None => false, - }; - let formal_param_type = substitute_generics( - formal_raw.clone(), - st.subst.clone(), - scope.type_env.clone().source_indices.clone(), - ); - let expected = if has_formal.clone() { - Some(formal_param_type.clone()) - } else { - None - }; - let ar = infer_expr( - arg_value(a.clone()), - scope.clone(), - expected.clone(), - ); - let next_subst = if (is_lambda_expr(arg_value(a.clone())) - || !has_formal.clone()) - { - st.subst.clone() - } else { - if should_unify_record_lit_generics( - formal_raw.clone(), - arg_value(a.clone()), - generic_names.clone(), - scope.type_env.clone().source_indices.clone(), - ) { - unify_record_lit_generics( - formal_raw.clone(), - ar.typed.clone(), - generic_names.clone(), + results: match call_args.clone().first().cloned() { + Some(first_arg) => { + let first_result = infer_expr( + arg_value(first_arg.clone()), scope.clone(), - st.subst.clone(), - ) - } else { - unify_generics( - formal_raw.clone(), - resolved_type(ar.typed.clone()), - generic_names.clone(), + None, + ); + let first_type = resolved_type(first_result.typed.clone()); + let elem_type = for_each_element_type_node( + first_type.clone(), scope.type_env.clone().source_indices.clone(), - st.subst.clone(), - ) - } - }; - Rc::new(ArgGenericFoldState { - results: v1_rt::concat( - st.results.clone(), - Rc::new(vec![Rc::new(ArgInferResult { - typed_arg: make_arg_node( - arg_name_at( - a.clone(), - scope.type_env.clone().source_indices.clone(), + ); + let call_elem_provenance = derive_element_provenance( + first_result.typed.clone(), + elem_type.clone(), + scope.clone(), + ); + let remaining_results = infer_method_args_with_fold( + call_method_name.clone(), + call_method_args.clone(), + call_fold_info.clone(), + call_fold_acc_type.clone(), + elem_type.clone(), + call_elem_provenance.clone(), + scope.clone(), + ); + v1_rt::concat( + Rc::new(vec![Rc::new(ArgInferResult { + typed_arg: make_arg_node( + arg_name_at( + first_arg.clone(), + scope + .type_env + .clone() + .source_indices + .clone(), + ), + first_result.typed.clone(), + first_arg.span.clone(), + first_arg.span.clone(), ), - ar.typed.clone(), - a.span.clone(), - a.span.clone(), - ), - diagnostics: ar.diagnostics.clone(), - })]), - ), - subst: next_subst.clone(), - }) - }, - ); - final_state.clone() - } - }; - let arg_infer_results = arg_call.results.clone(); - let call_subst = arg_call.subst.clone(); - let typed_args = Rc::new({ - let mut __result = Vec::new(); - for air in arg_infer_results.clone().iter().cloned() { - __result.push(air.typed_arg.clone()); - } - __result - }); - let arg_diags = Rc::new({ - let mut __result = Vec::new(); - for air in arg_infer_results.clone().iter().cloned() { - __result.extend((*air.diagnostics.clone()).iter().cloned()); - } - __result - }); - let typed_arg_nodes = typed_args.clone(); - if (sig.clone() != None) { - { - let resolved_type = match sig.clone() { - Some(s) => substitute_generics( - s.inferred.clone(), - call_subst.clone(), - scope.type_env.clone().source_indices.clone(), - ), - None => error_type(), - }; - let value_params_for_check = split_sig_params( - sig_params.clone(), - scope.type_env.clone().source_indices.clone(), - ) - .value_params - .clone(); - let arg_compat_diags = - if module_skips_direct_call_arg_check(scope.module_name.clone()) { - Rc::new(vec![]) - } else { - direct_call_arg_mismatch_diags( - value_params_for_check.clone(), - typed_args.clone(), - call_subst.clone(), - scope.type_env.clone(), - scope.module_name.clone(), - ) - }; - Rc::new(InferResult { - typed: make_named_expr_node( - func_name.clone(), - Rc::new(ExprData::ExprCall { - call_semantics: Some(CallSemantics::PlainCallSemantics), - descent_evidence: None, - }), - typed_arg_nodes.clone(), - Some(Rc::new(InferredNode::Resolved { - node: resolved_type.clone(), - })), - span.clone(), - node_name_span(texpr.clone()), - ), - diagnostics: v1_rt::concat(arg_diags.clone(), arg_compat_diags.clone()), - }) - } - } else { - { - let first_arg_type = match typed_args.clone().first().cloned() { - Some(ta) => resolved_type(arg_value(ta.clone())), - None => unit_type(), - }; - let method_receiver = match typed_args.clone().first().cloned() { - Some(ta) => arg_value(ta.clone()), - None => internal_expr_error_node( - "method bridge missing receiver".to_string(), - span.clone(), - ), - }; - let call_acc_is_under_resolved = !is_fully_resolved( - call_fold_acc_type.clone(), - scope.type_env.clone().source_indices.clone(), - ); - let refined_call_fold_acc_type = if ((call_fold_info.clone() != None) - && call_acc_is_under_resolved.clone()) - { - match Rc::new({ - let mut __result = Vec::new(); - for a in typed_args.clone().iter().cloned() { - if is_lambda_expr(arg_value(a.clone())) { - __result.push(a); + diagnostics: first_result.diagnostics.clone(), + })]), + remaining_results.clone(), + ) } - } - __result + None => Rc::new(vec![]), + }, }) - .first() - .cloned() - { - Some(lambda_arg) => resolved_type(arg_value(lambda_arg.clone())), - None => call_fold_acc_type.clone(), - } } else { - call_fold_acc_type.clone() - }; - let method_resolution = resolve_known_method_node( - method_receiver.clone(), - resolve_method_receiver_type( - first_arg_type.clone(), - scope.type_env.clone(), - ), - func_name.clone(), - if (call_fold_info.clone() != None) { - Some(refined_call_fold_acc_type.clone()) - } else { - None - }, - scope.service_registry.clone(), - scope.type_env.clone().source_indices.clone(), - ); - let is_known_method = (method_resolution.result_type.clone() != None); - if (is_known_method.clone() && ((typed_args.clone().len() as i64) > 0)) { { - let receiver = method_receiver.clone(); - let remaining = Rc::new( - typed_args + let sig_split = split_sig_params( + sig_params.clone(), + scope.type_env.clone().source_indices.clone(), + ); + let value_params = sig_split.value_params.clone(); + let generic_names = sig_split.generic_names.clone(); + let final_state = Rc::new( + call_args .clone() .iter() .cloned() - .skip(1 as usize) + .enumerate() + .map(|(i, v)| (i as i64, v)) .collect::>(), + ) + .iter() + .cloned() + .fold( + Rc::new(ArgGenericFoldState { + results: Rc::new(vec![]), + subst: v1_rt::rc_empty_map::>(), + }), + |st: Rc, pair: (i64, Rc)| { + let a = pair.1.clone(); + let formal_lookup = value_params + .clone() + .get(pair.0.clone() as usize) + .cloned(); + let formal_raw = match formal_lookup.clone() { + Some(p) => param_node_type_expr(p.clone()), + None => { + type_variable_node("callable_param".to_string()) + } + }; + let has_formal = match formal_lookup.clone() { + Some(_) => true, + None => false, + }; + let formal_param_type = substitute_generics( + formal_raw.clone(), + st.subst.clone(), + scope.type_env.clone().source_indices.clone(), + ); + let expected = if has_formal.clone() { + Some(formal_param_type.clone()) + } else { + None + }; + let ar = infer_expr( + arg_value(a.clone()), + scope.clone(), + expected.clone(), + ); + let next_subst = if (is_lambda_expr(arg_value(a.clone())) + || !has_formal.clone()) + { + st.subst.clone() + } else { + if should_unify_record_lit_generics( + formal_raw.clone(), + arg_value(a.clone()), + generic_names.clone(), + scope.type_env.clone().source_indices.clone(), + ) { + unify_record_lit_generics( + formal_raw.clone(), + ar.typed.clone(), + generic_names.clone(), + scope.clone(), + st.subst.clone(), + ) + } else { + unify_generics( + formal_raw.clone(), + resolved_type(ar.typed.clone()), + generic_names.clone(), + scope.type_env.clone().source_indices.clone(), + st.subst.clone(), + ) + } + }; + Rc::new(ArgGenericFoldState { + results: v1_rt::concat( + st.results.clone(), + Rc::new(vec![Rc::new(ArgInferResult { + typed_arg: make_arg_node( + arg_name_at( + a.clone(), + scope + .type_env + .clone() + .source_indices + .clone(), + ), + ar.typed.clone(), + a.span.clone(), + a.span.clone(), + ), + diagnostics: ar.diagnostics.clone(), + })]), + ), + subst: next_subst.clone(), + }) + }, ); - let base_result_type = match method_resolution.result_type.clone() { - Some(mt) => mt.clone(), + final_state.clone() + } + }; + let arg_infer_results = arg_call.results.clone(); + let call_subst = arg_call.subst.clone(); + let typed_args = Rc::new({ + let mut __result = Vec::new(); + for air in arg_infer_results.clone().iter().cloned() { + __result.push(air.typed_arg.clone()); + } + __result + }); + let arg_diags = Rc::new({ + let mut __result = Vec::new(); + for air in arg_infer_results.clone().iter().cloned() { + __result.extend((*air.diagnostics.clone()).iter().cloned()); + } + __result + }); + let typed_arg_nodes = typed_args.clone(); + if (sig.clone() != None) { + { + let resolved_type = match sig.clone() { + Some(s) => substitute_generics( + s.inferred.clone(), + call_subst.clone(), + scope.type_env.clone().source_indices.clone(), + ), None => error_type(), }; - let method_tv = if (method_resolution.semantics.clone() == None) { - Rc::new(KernelTypeBuild { - ty: base_result_type.clone(), - diagnostics: Rc::new(vec![]), + let value_params_for_check = split_sig_params( + sig_params.clone(), + scope.type_env.clone().source_indices.clone(), + ) + .value_params + .clone(); + let arg_compat_diags = if module_skips_direct_call_arg_check( + scope.module_name.clone(), + ) { + Rc::new(vec![]) + } else { + direct_call_arg_mismatch_diags( + value_params_for_check.clone(), + typed_args.clone(), + call_subst.clone(), + scope.type_env.clone(), + scope.module_name.clone(), + ) + }; + Rc::new(InferResult { + typed: make_named_expr_node( + func_name.clone(), + Rc::new(ExprData::ExprCall { + call_semantics: Some(CallSemantics::PlainCallSemantics), + descent_evidence: None, + }), + typed_arg_nodes.clone(), + Some(Rc::new(InferredNode::Resolved { + node: resolved_type.clone(), + })), + span.clone(), + node_name_span(texpr.clone()), + ), + diagnostics: v1_rt::concat( + arg_diags.clone(), + arg_compat_diags.clone(), + ), + }) + } + } else { + { + let first_arg_type = match typed_args.clone().first().cloned() { + Some(ta) => resolved_type(arg_value(ta.clone())), + None => unit_type(), + }; + let method_receiver = match typed_args.clone().first().cloned() { + Some(ta) => arg_value(ta.clone()), + None => internal_expr_error_node( + "method bridge missing receiver".to_string(), + span.clone(), + ), + }; + let call_acc_is_under_resolved = !is_fully_resolved( + call_fold_acc_type.clone(), + scope.type_env.clone().source_indices.clone(), + ); + let refined_call_fold_acc_type = if ((call_fold_info.clone() + != None) + && call_acc_is_under_resolved.clone()) + { + match Rc::new({ + let mut __result = Vec::new(); + for a in typed_args.clone().iter().cloned() { + if is_lambda_expr(arg_value(a.clone())) { + __result.push(a); + } + } + __result }) + .first() + .cloned() + { + Some(lambda_arg) => { + resolved_type(arg_value(lambda_arg.clone())) + } + None => call_fold_acc_type.clone(), + } } else { - match (*method_resolution.semantics.clone().clone().unwrap()) - .clone() + call_fold_acc_type.clone() + }; + let method_resolution = resolve_known_method_node( + method_receiver.clone(), + resolve_method_receiver_type( + first_arg_type.clone(), + scope.type_env.clone(), + ), + func_name.clone(), + if (call_fold_info.clone() != None) { + Some(refined_call_fold_acc_type.clone()) + } else { + None + }, + scope.service_registry.clone(), + scope.type_env.clone().source_indices.clone(), + ); + let is_known_method = + (method_resolution.result_type.clone() != None); + if (is_known_method.clone() + && ((typed_args.clone().len() as i64) > 0)) + { { - MethodSemantics::AlgebraMethodSemantics { - algebra_template: at, - .. - } => match at.clone() { - Some(t) => { - if (template_return_has_variables(t.clone()) - || !is_fully_resolved( - base_result_type.clone(), + let receiver = method_receiver.clone(); + let remaining = Rc::new( + typed_args + .clone() + .iter() + .cloned() + .skip(1 as usize) + .collect::>(), + ); + let base_result_type = + match method_resolution.result_type.clone() { + Some(mt) => mt.clone(), + None => error_type(), + }; + let method_tv = if (method_resolution.semantics.clone() + == None) + { + Rc::new(KernelTypeBuild { + ty: base_result_type.clone(), + diagnostics: Rc::new(vec![]), + }) + } else { + match (*method_resolution + .semantics + .clone() + .clone() + .unwrap()) + .clone() + { + MethodSemantics::AlgebraMethodSemantics { + algebra_template: at, + .. + } => { + match at.clone() { + Some(t) => { + if (template_return_has_variables( + t.clone(), + ) || !is_fully_resolved( + base_result_type.clone(), + scope + .type_env + .clone() + .source_indices + .clone(), + )) { + { + let call_arg_types = Rc::new({ + let mut __result = + Vec::new(); + for a in remaining + .clone() + .iter() + .cloned() + { + __result.push( + resolved_type( + arg_value( + a.clone(), + ), + ), + ); + } + __result + }); + let fold_overrides = + if (call_fold_info.clone() + != None) + { + fold_override_map("FoldAccumulator".to_string(), refined_call_fold_acc_type.clone()) + } else { + empty_override_map() + }; + resolve_type_variables_from_template(t.clone(), call_arg_types.clone(), first_arg_type.clone(), fold_overrides.clone(), scope.type_env.clone().source_indices.clone()) + } + } else { + Rc::new(KernelTypeBuild { + ty: base_result_type.clone(), + diagnostics: Rc::new(vec![]), + }) + } + } + None => Rc::new(KernelTypeBuild { + ty: base_result_type.clone(), + diagnostics: Rc::new(vec![]), + }), + } + } + _ => Rc::new(KernelTypeBuild { + ty: base_result_type.clone(), + diagnostics: Rc::new(vec![]), + }), + } + }; + let bridge_result_type = method_tv.ty.clone(); + let template_subst_diags = method_tv.diagnostics.clone(); + let returns_receiver_self = + if (method_resolution.semantics.clone() == None) { + false + } else { + match (*method_resolution + .semantics + .clone() + .clone() + .unwrap()) + .clone() + { + MethodSemantics::AlgebraMethodSemantics { + algebra_template: at, + .. + } => match at.clone() { + Some(t) => { + template_return_is_receiver_self( + t.clone(), + ) + } + None => false, + }, + _ => false, + } + }; + let final_receiver = if ((returns_receiver_self.clone() + && is_fully_resolved( + bridge_result_type.clone(), + scope.type_env.clone().source_indices.clone(), + )) + && !is_fully_resolved( + first_arg_type.clone(), + scope.type_env.clone().source_indices.clone(), + )) { + Rc::new(Node { + name: receiver.name.clone(), + span: receiver.span.clone(), + ident_span: receiver.ident_span.clone(), + children: bridge_result_type.children.clone(), + connective: receiver.connective.clone(), + params: receiver.params.clone(), + inferred: Some(Rc::new(InferredNode::Resolved { + node: bridge_result_type.clone(), + })), + return_cardinality: receiver + .return_cardinality + .clone(), + uses: receiver.uses.clone(), + body: receiver.body.clone(), + transport: receiver.transport.clone(), + properties: receiver.properties.clone(), + type_annotation: receiver.type_annotation.clone(), + is_self_recursive: receiver + .is_self_recursive + .clone(), + has_non_tail_self_call: receiver + .has_non_tail_self_call + .clone(), + match_pattern: receiver.match_pattern.clone(), + expr_data: receiver.expr_data.clone(), + ident: None, + }) + } else { + receiver.clone() + }; + let remaining_arg_nodes = Rc::new({ + let mut __result = Vec::new(); + for ta in remaining.clone().iter().cloned() { + __result.push(make_arg_node( + arg_name_at( + ta.clone(), scope .type_env .clone() .source_indices .clone(), - )) - { - { - let call_arg_types = Rc::new({ - let mut __result = Vec::new(); - for a in - remaining.clone().iter().cloned() - { - __result.push(resolved_type( - arg_value(a.clone()), - )); - } - __result - }); - let fold_overrides = - if (call_fold_info.clone() != None) { - fold_override_map( - "FoldAccumulator".to_string(), - refined_call_fold_acc_type - .clone(), - ) - } else { - empty_override_map() - }; - resolve_type_variables_from_template( - t.clone(), - call_arg_types.clone(), - first_arg_type.clone(), - fold_overrides.clone(), - scope - .type_env - .clone() - .source_indices - .clone(), - ) - } - } else { - Rc::new(KernelTypeBuild { - ty: base_result_type.clone(), - diagnostics: Rc::new(vec![]), - }) - } + ), + arg_value(ta.clone()), + span.clone(), + span.clone(), + )); } - None => Rc::new(KernelTypeBuild { - ty: base_result_type.clone(), - diagnostics: Rc::new(vec![]), - }), - }, - _ => Rc::new(KernelTypeBuild { - ty: base_result_type.clone(), - diagnostics: Rc::new(vec![]), - }), - } - }; - let bridge_result_type = method_tv.ty.clone(); - let template_subst_diags = method_tv.diagnostics.clone(); - let returns_receiver_self = if (method_resolution.semantics.clone() - == None) - { - false - } else { - match (*method_resolution.semantics.clone().clone().unwrap()) - .clone() - { - MethodSemantics::AlgebraMethodSemantics { - algebra_template: at, - .. - } => match at.clone() { - Some(t) => template_return_is_receiver_self(t.clone()), - None => false, - }, - _ => false, - } - }; - let final_receiver = if ((returns_receiver_self.clone() - && is_fully_resolved( - bridge_result_type.clone(), - scope.type_env.clone().source_indices.clone(), - )) - && !is_fully_resolved( - first_arg_type.clone(), - scope.type_env.clone().source_indices.clone(), - )) { - Rc::new(Node { - name: receiver.name.clone(), - span: receiver.span.clone(), - ident_span: receiver.ident_span.clone(), - children: bridge_result_type.children.clone(), - connective: receiver.connective.clone(), - params: receiver.params.clone(), - inferred: Some(Rc::new(InferredNode::Resolved { - node: bridge_result_type.clone(), - })), - return_cardinality: receiver.return_cardinality.clone(), - uses: receiver.uses.clone(), - body: receiver.body.clone(), - transport: receiver.transport.clone(), - properties: receiver.properties.clone(), - type_annotation: receiver.type_annotation.clone(), - is_self_recursive: receiver.is_self_recursive.clone(), - has_non_tail_self_call: receiver - .has_non_tail_self_call - .clone(), - match_pattern: receiver.match_pattern.clone(), - expr_data: receiver.expr_data.clone(), - ident: None, - }) - } else { - receiver.clone() - }; - let remaining_arg_nodes = Rc::new({ - let mut __result = Vec::new(); - for ta in remaining.clone().iter().cloned() { - __result.push(make_arg_node( - arg_name_at( - ta.clone(), - scope.type_env.clone().source_indices.clone(), + __result + }); + Rc::new(InferResult { + typed: make_named_expr_node( + func_name.clone(), + Rc::new(ExprData::ExprMethodCall { + method_semantics: method_resolution + .semantics + .clone(), + }), + v1_rt::concat( + Rc::new(vec![final_receiver.clone()]), + remaining_arg_nodes.clone(), + ), + Some(Rc::new(InferredNode::Resolved { + node: bridge_result_type.clone(), + })), + span.clone(), + node_name_span(texpr.clone()), ), - arg_value(ta.clone()), - span.clone(), - span.clone(), - )); + diagnostics: v1_rt::concat( + v1_rt::concat( + arg_diags.clone(), + method_resolution.diagnostics.clone(), + ), + template_subst_diags.clone(), + ), + }) } - __result - }); - Rc::new(InferResult { - typed: make_named_expr_node( - func_name.clone(), - Rc::new(ExprData::ExprMethodCall { - method_semantics: method_resolution.semantics.clone(), - }), - v1_rt::concat( - Rc::new(vec![final_receiver.clone()]), - remaining_arg_nodes.clone(), - ), - Some(Rc::new(InferredNode::Resolved { - node: bridge_result_type.clone(), - })), - span.clone(), - node_name_span(texpr.clone()), - ), - diagnostics: v1_rt::concat( - v1_rt::concat( - arg_diags.clone(), - method_resolution.diagnostics.clone(), - ), - template_subst_diags.clone(), - ), - }) - } - } else { - if (func_name.clone() == "empty_map".to_string()) { - { - let bare_m = bare_map_node(); - match expected.clone() { + } else { + if (func_name.clone() == "empty_map".to_string()) { + { + let bare_m = bare_map_node(); + match expected.clone() { Some(exp) => if node_is_keyed_collection(exp.clone(), scope.type_env.clone().source_indices.clone()) { - Rc::new(InferResult { + Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { call_semantics: Some(CallSemantics::PlainCallSemantics), descent_evidence: None, @@ -3985,9 +4087,9 @@ pub fn infer_expr( })), span.clone(), node_name_span(texpr.clone())), diagnostics: arg_diags.clone(), }) - } else { - { - let empty_map_diags = v1_rt::concat(arg_diags.clone(), Rc::new(vec![inference_error("empty_map(): expected type is not a keyed collection".to_string(), span.clone(), scope.module_name.clone())])); + } else { + { + let empty_map_diags = v1_rt::concat(arg_diags.clone(), Rc::new(vec![inference_error("empty_map(): expected type is not a keyed collection".to_string(), span.clone(), scope.module_name.clone())])); match bare_m.clone() { Some(map_t) => Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { @@ -4010,7 +4112,7 @@ match bare_m.clone() { }), } } - }, + }, None => match bare_m.clone() { Some(map_t) => Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { @@ -4033,14 +4135,14 @@ match bare_m.clone() { }), }, } - } - } else { - if (func_name.clone() == "empty_set".to_string()) { - { - let bare_s = bare_set_node(); - match expected.clone() { + } + } else { + if (func_name.clone() == "empty_set".to_string()) { + { + let bare_s = bare_set_node(); + match expected.clone() { Some(exp) => if node_is_set_collection(exp.clone(), scope.type_env.clone().source_indices.clone()) { - Rc::new(InferResult { + Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { call_semantics: Some(CallSemantics::PlainCallSemantics), descent_evidence: None, @@ -4049,9 +4151,9 @@ match bare_m.clone() { })), span.clone(), node_name_span(texpr.clone())), diagnostics: arg_diags.clone(), }) - } else { - { - let empty_set_diags = v1_rt::concat(arg_diags.clone(), Rc::new(vec![inference_error("empty_set(): expected type is not Set<...>".to_string(), span.clone(), scope.module_name.clone())])); + } else { + { + let empty_set_diags = v1_rt::concat(arg_diags.clone(), Rc::new(vec![inference_error("empty_set(): expected type is not Set<...>".to_string(), span.clone(), scope.module_name.clone())])); match bare_s.clone() { Some(set_t) => Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { @@ -4074,7 +4176,7 @@ match bare_s.clone() { }), } } - }, + }, None => match bare_s.clone() { Some(set_t) => Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { @@ -4097,86 +4199,90 @@ match bare_s.clone() { }), }, } - } - } else { - if (infer_builtin_call_type(func_name.clone()) != None) { - { - let tier2b = infer_tier2b_builtin_with_kernel_diags( - func_name.clone(), - typed_args.clone(), - scope.clone(), - span.clone(), - ); - let bt = tier2b.bt.clone(); - let call_semantics = - if (func_name.clone() == "lookup".to_string()) { - Some(CallSemantics::LookupCallSemantics) - } else { - Some(CallSemantics::PlainCallSemantics) - }; - Rc::new(InferResult { - typed: make_named_expr_node( - func_name.clone(), - Rc::new(ExprData::ExprCall { - call_semantics: call_semantics.clone(), - descent_evidence: None, - }), - typed_arg_nodes.clone(), - Some(Rc::new(InferredNode::Resolved { - node: bt.clone(), - })), - span.clone(), - node_name_span(texpr.clone()), - ), - diagnostics: v1_rt::concat( - arg_diags.clone(), - tier2b.kernel_diags.clone(), - ), - }) - } - } else { - { - let callable_local = match v1_rt::map_get( - &scope.locals.clone(), - func_name.clone(), - ) { - Some(binding) => { - if ((binding - .resolved - .clone() - .params - .clone() - .len() - as i64) - > 0) + } + } else { + if (infer_builtin_call_type(func_name.clone()) != None) + { + { + let tier2b = + infer_tier2b_builtin_with_kernel_diags( + func_name.clone(), + typed_args.clone(), + scope.clone(), + span.clone(), + ); + let bt = tier2b.bt.clone(); + let call_semantics = if (func_name.clone() + == "lookup".to_string()) { - Some(binding.resolved.clone()) + Some(CallSemantics::LookupCallSemantics) } else { - None - } + Some(CallSemantics::PlainCallSemantics) + }; + Rc::new(InferResult { + typed: make_named_expr_node( + func_name.clone(), + Rc::new(ExprData::ExprCall { + call_semantics: call_semantics + .clone(), + descent_evidence: None, + }), + typed_arg_nodes.clone(), + Some(Rc::new(InferredNode::Resolved { + node: bt.clone(), + })), + span.clone(), + node_name_span(texpr.clone()), + ), + diagnostics: v1_rt::concat( + arg_diags.clone(), + tier2b.kernel_diags.clone(), + ), + }) } - None => None, - }; - if (callable_local.clone() != None) { + } else { { - let callable_type = match callable_local.clone() - { - Some(ct) => ct.clone(), - None => error_type(), - }; - let resolved_type = match callable_type - .inferred - .clone() - .as_deref() - .cloned() - { - Some(InferredNode::Resolved { - node: ret, - .. - }) => ret.clone(), - _ => error_type(), + let callable_local = match v1_rt::map_get( + &scope.locals.clone(), + func_name.clone(), + ) { + Some(binding) => { + if ((binding + .resolved + .clone() + .params + .clone() + .len() + as i64) + > 0) + { + Some(binding.resolved.clone()) + } else { + None + } + } + None => None, }; - Rc::new(InferResult { + if (callable_local.clone() != None) { + { + let callable_type = + match callable_local.clone() { + Some(ct) => ct.clone(), + None => error_type(), + }; + let resolved_type = match callable_type + .inferred + .clone() + .as_deref() + .cloned() + { + Some(InferredNode::Resolved { + node: ret, + .. + }) => ret.clone(), + _ => error_type(), + }; + Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { call_semantics: Some(CallSemantics::PlainCallSemantics), descent_evidence: None, @@ -4185,77 +4291,61 @@ match bare_s.clone() { })), span.clone(), node_name_span(texpr.clone())), diagnostics: arg_diags.clone(), }) - } - } else { - match infer_variant_constructor_call( - func_name.clone(), - call_args.clone(), - span.clone(), - node_name_span(texpr.clone()), - scope.clone(), - ) { - Some(ctor_result) => ctor_result.clone(), - None => { - let type_match = lookup_type_by_name( - scope.type_env.clone(), + } + } else { + match infer_variant_constructor_call( func_name.clone(), - ); - let global_bare_callable = - match type_match.clone() { - Some(_) => None, - None => global_bare_callable_node( - scope.type_env.clone(), - func_name.clone(), - ), - }; - let resolved_type = match type_match.clone() - { - Some(tn) => tn.clone(), + call_args.clone(), + span.clone(), + node_name_span(texpr.clone()), + scope.clone(), + ) { + Some(ctor_result) => { + ctor_result.clone() + } None => { - match global_bare_callable.clone() { - Some(gnode) => { - if ((gnode - .params + let type_match = + lookup_type_by_name( + scope.type_env.clone(), + func_name.clone(), + ); + let global_bare_callable = match type_match.clone() { + Some(_) => None, + None => global_bare_callable_node(scope.type_env.clone(), func_name.clone()), +}; + let resolved_type = match type_match + .clone() + { + Some(tn) => tn.clone(), + None => { + match global_bare_callable .clone() - .len() - as i64) - > 0) { - borrowed_callable_call_type(gnode.clone(), typed_arg_nodes.clone(), scope.clone()) - } else { - callable_return_type( - gnode.clone(), - ) + Some(gnode) => { + if ((gnode + .params + .clone() + .len() + as i64) + > 0) + { + borrowed_callable_call_type(gnode.clone(), typed_arg_nodes.clone(), scope.clone()) + } else { + callable_return_type(gnode.clone()) + } + } + None => error_type(), } } - None => error_type(), - } - } - }; - let call_diags = match type_match.clone() { - Some(_) => Rc::new(vec![]), - None => { - match global_bare_callable.clone() { - Some(_) => Rc::new(vec![]), - None => Rc::new( - vec![inference_error( - v1_rt::concat( - v1_rt::concat( - "function '" - .to_string(), - func_name.clone(), - ), - "' not found in scope" - .to_string(), - ), - span.clone(), - scope.module_name.clone(), - )], - ), - } - } - }; - Rc::new(InferResult { + }; + let call_diags = match type_match.clone() { + Some(_) => Rc::new(vec![]), + None => match global_bare_callable.clone() { + Some(_) => Rc::new(vec![]), + None => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat("function '".to_string(), func_name.clone()), "' not found in scope".to_string()), span.clone(), scope.module_name.clone())]), +}, +}; + Rc::new(InferResult { typed: make_named_expr_node(func_name.clone(), Rc::new(ExprData::ExprCall { call_semantics: Some(CallSemantics::PlainCallSemantics), descent_evidence: None, @@ -4264,6 +4354,8 @@ match bare_s.clone() { })), span.clone(), node_name_span(texpr.clone())), diagnostics: v1_rt::concat(arg_diags.clone(), call_diags.clone()), }) + } + } } } } @@ -6570,6 +6662,29 @@ pub fn infer_record_lit( (global_bare_is_ambiguous(scope.type_env.clone(), tn_str.clone()) && (v1_rt::map_get(&scope.type_env.clone().str_bindings.clone(), tn_str.clone()) == None)); + let presence_ambiguity_refusal_diags = if ((tn_str.clone() != "".to_string()) + && (v1_rt::map_get(&scope.type_env.clone().str_bindings.clone(), tn_str.clone()) + == None)) + { + { + let strict_cands = + global_bare_strict_ambiguity_candidates(scope.type_env.clone(), tn_str.clone()); + if ((strict_cands.clone().len() as i64) > 0) { + Rc::new(vec![make_error_node( + Rc::new(CompilerDiagnostic::AmbiguousReference { + name: tn_str.clone(), + candidates: strict_cands.clone(), + span: name_span.clone(), + }), + scope.module_name.clone(), + )]) + } else { + Rc::new(vec![]) + } + } + } else { + Rc::new(vec![]) + }; let missing_field_diags = if ((((tn_str.clone() == "".to_string()) || ((presence_fields.clone().len() as i64) == 0)) || is_zero_field_variant_tag_reference.clone()) @@ -6977,10 +7092,11 @@ pub fn infer_record_lit( let type_diags = match effective_lookup.clone() { Some(_) => Rc::new(vec![]), None => Rc::new(vec![make_error_node( - Rc::new(CompilerDiagnostic::UnresolvedType { - name: type_name.clone().unwrap(), - span: span.clone(), - }), + bare_name_miss_diagnostic( + scope.type_env.clone(), + type_name.clone().unwrap(), + span.clone(), + ), scope.module_name.clone(), )]), }; @@ -7025,14 +7141,22 @@ pub fn infer_record_lit( span.clone(), name_span.clone(), ); + let presence_ambiguity_diags = if ((type_diags.clone().len() as i64) > 0) { + Rc::new(vec![]) + } else { + presence_ambiguity_refusal_diags.clone() + }; Rc::new(InferResult { typed: texpr.clone(), diagnostics: v1_rt::concat( v1_rt::concat( - v1_rt::concat(fi_diags.clone(), type_diags.clone()), - sole_ctor_diags.clone(), + v1_rt::concat( + v1_rt::concat(fi_diags.clone(), type_diags.clone()), + sole_ctor_diags.clone(), + ), + missing_field_diags.clone(), ), - missing_field_diags.clone(), + presence_ambiguity_diags.clone(), ), }) } @@ -7499,7 +7623,11 @@ pub fn build_per_field_for_let( ExprData::ExprCall { .. } => { let callee = expr_call_func_at(val.clone(), ctx.type_env.clone().source_indices.clone()); - match lookup_func_sig(ctx.func_env.clone(), ctx.type_env.clone(), callee.clone()) { + match func_sig_if_resolved(lookup_func_sig( + ctx.func_env.clone(), + ctx.type_env.clone(), + callee.clone(), + )) { Some(sig) => { if ((sig.output_provenance.clone().len() as i64) > 1) { { @@ -7559,7 +7687,11 @@ pub fn build_per_field_for_let( } => { let callee = expr_method_name_at(val.clone(), ctx.type_env.clone().source_indices.clone()); - match lookup_func_sig(ctx.func_env.clone(), ctx.type_env.clone(), callee.clone()) { + match func_sig_if_resolved(lookup_func_sig( + ctx.func_env.clone(), + ctx.type_env.clone(), + callee.clone(), + )) { Some(sig) => { if ((sig.output_provenance.clone().len() as i64) > 1) { { @@ -7908,18 +8040,19 @@ pub fn classify_let_value(val: Rc, ctx: Rc) -> Option { let callee = expr_call_func_at(val.clone(), ctx.type_env.clone().source_indices.clone()); - let from_provenance = - match lookup_func_sig(ctx.func_env.clone(), ctx.type_env.clone(), callee.clone()) { - Some(sig) => match sig.output_provenance.clone().first().cloned() { - Some(param_map) => classify_call_via_provenance( - val.clone(), - param_map.clone(), - ctx.clone(), - ), - None => None, - }, + let from_provenance = match func_sig_if_resolved(lookup_func_sig( + ctx.func_env.clone(), + ctx.type_env.clone(), + callee.clone(), + )) { + Some(sig) => match sig.output_provenance.clone().first().cloned() { + Some(param_map) => { + classify_call_via_provenance(val.clone(), param_map.clone(), ctx.clone()) + } None => None, - }; + }, + None => None, + }; match from_provenance.clone() { Some(_) => from_provenance.clone(), None => { @@ -8515,11 +8648,11 @@ pub fn classify_argument( arg_expr.clone(), ctx.type_env.clone().source_indices.clone(), ); - let from_provenance = match lookup_func_sig( + let from_provenance = match func_sig_if_resolved(lookup_func_sig( ctx.func_env.clone(), ctx.type_env.clone(), callee.clone(), - ) { + )) { Some(sig) => { match sig.output_provenance.clone().first().cloned() { Some(param_map) => classify_call_via_provenance( @@ -9287,11 +9420,11 @@ pub fn annotate_descent(body: Rc, ctx: Rc) -> Rc { scrut.clone(), ctx.type_env.clone().source_indices.clone(), ); - match lookup_func_sig( + match func_sig_if_resolved(lookup_func_sig( ctx.func_env.clone(), ctx.type_env.clone(), callee.clone(), - ) { + )) { Some(sig) => { if ((Rc::new(v1_rt::map_keys(&sig.variant_provenance.clone())).len() as i64) @@ -10553,7 +10686,11 @@ pub fn method_call_args_by_name( let mname = expr_method_name_at(call.clone(), source_indices.clone()); let recv = method_receiver(call.clone()); let mc_args = method_arg_nodes(call.clone()); - let base = match lookup_func_sig(func_env.clone(), type_env.clone(), mname.clone()) { + let base = match func_sig_if_resolved(lookup_func_sig( + func_env.clone(), + type_env.clone(), + mname.clone(), + )) { Some(sig) => match sig.params.clone().first().cloned() { Some(first_p) => { let first_pname = param_node_name_at(first_p.clone(), source_indices.clone()); @@ -10595,7 +10732,11 @@ pub fn compose_callee_provenance( func_env: Rc, let_prov: Rc>>>>, ) -> Rc>> { - match lookup_func_sig(func_env.clone(), type_env.clone(), callee.clone()) { + match func_sig_if_resolved(lookup_func_sig( + func_env.clone(), + type_env.clone(), + callee.clone(), + )) { Some(sig) => match sig.output_provenance.clone().first().cloned() { Some(callee_prov) => Rc::new(v1_rt::map_keys(&callee_prov)).iter().cloned().fold( v1_rt::rc_empty_map::>(), @@ -11242,7 +11383,11 @@ pub fn collect_variant_constructors( }, ExprData::ExprCall { .. } => { let callee = expr_call_func_at(body.clone(), type_env.source_indices.clone()); - match lookup_func_sig(func_env.clone(), type_env.clone(), callee.clone()) { + match func_sig_if_resolved(lookup_func_sig( + func_env.clone(), + type_env.clone(), + callee.clone(), + )) { Some(sig) => { if ((authored_name_at( type_env.source_indices.clone(), @@ -12298,12 +12443,9 @@ pub struct SigParamSplit { pub generic_names: Rc>, } -pub fn body_shadow_aware_func_sig( - scope: Rc, - func_name: String, -) -> Option> { +pub fn body_shadow_aware_func_sig(scope: Rc, func_name: String) -> Rc { match v1_rt::map_get(&scope.body_locals.clone(), func_name.clone()) { - Some(_) => None, + Some(_) => Rc::new(FuncSigLookup::FuncSigUnresolved), None => lookup_func_sig( scope.func_env.clone(), scope.type_env.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 2f2edd5d429..03579f9862d 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -14,6 +14,7 @@ use crate::v1_rt::Witness; use crate::v1_rt::Witness::{Holds, Violates}; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::Cardinality::*; +use crate::v1_std_core::CompilerDiagnostic::{AmbiguousReference, UnresolvedType}; use crate::v1_std_core::Connective::*; use crate::v1_std_core::ExprData::*; use crate::v1_std_core::InferredNode::*; @@ -23,7 +24,8 @@ pub use crate::v1_std_core::{ param_node_type_expr, source_text_at, }; pub use crate::v1_std_core::{ - Cardinality, Connective, ExprData, InferredNode, InternTable, NewlineIndex, Node, + Cardinality, CompilerDiagnostic, Connective, ExprData, InferredNode, InternTable, NewlineIndex, + Node, }; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; @@ -969,6 +971,123 @@ pub fn global_bare_nearest_ancestor( } } +pub fn unique_on_chain_policy_note() -> String { + thread_local! { + static CACHED: String = { + "namespace-resolution-design.md 13 (operator-ratified 2026-07-21), landed as 8 step 1: under NameResolutionPolicy = NamespaceOnlyY (name_resolution_policy_is_namespace_only, thread-local host gate, default OFF = ImportScoped byte-for-byte), a bare homonym resolves to the UNIQUE binder on the referencing module's ancestor chain — a candidate is on the chain iff its declaring module path is a leading-segment prefix of (or equal to) TypeEnv.module_path. Exactly one on-chain candidate resolves; zero or two-plus REFUSES (Absent from lookup + global_bare_is_ambiguous true, surfaced as the typed AmbiguousReference diagnostic at the reference site) — no nearest-wins, no fallback chain: adding a nearer homonym must loudly break a reference, never silently rebind it (the 13 edit-stability invariant). The zero-on-chain whole-pool-homonym case is Ambiguous, not Unresolved, mirroring the census containment walk (cli_run.rs containment_resolve_fn_v1: lexical Unbound + GlobalBareAmbiguousBinding => Ambiguous).".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn global_bare_chain_candidates( + env_module_path: String, + candidates: Rc>>, +) -> Rc>> { + { + let env_segs = module_path_segments(env_module_path.clone()); + Rc::new({ + let mut __result = Vec::new(); + for cand in candidates.clone().iter().cloned() { + if { + let cand_segs = module_path_segments(cand.module_path.clone()); + (segment_lcp_len(cand_segs.clone(), env_segs.clone()) + == (cand_segs.clone().len() as i64)) + } { + __result.push(cand); + } + } + __result + }) + } +} + +pub fn global_bare_unique_chain_candidate( + env_module_path: String, + candidates: Rc>>, +) -> Option> { + { + let chain = global_bare_chain_candidates(env_module_path.clone(), candidates.clone()); + match ((chain.clone().len() as i64) == 1) { + true => chain.clone().first().cloned(), + false => None, + } + } +} + +pub fn global_bare_policy_candidate( + env_module_path: String, + candidates: Rc>>, +) -> Option> { + if v1_rt::name_resolution_policy_is_namespace_only() { + global_bare_unique_chain_candidate(env_module_path.clone(), candidates.clone()) + } else { + global_bare_nearest_ancestor_candidate(env_module_path.clone(), candidates.clone()) + } +} + +pub fn global_bare_strict_ambiguity_candidates(env: Rc, name: String) -> Rc> { + { + if (v1_rt::name_resolution_policy_is_namespace_only() == false) { + return Rc::new(vec![]); + } + match v1_rt::map_get(&env.symbol_index.clone().global_bare.clone(), name.clone()) + .as_deref() + .cloned() + { + Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { + candidates: cands, .. + }) => { + let chain = global_bare_chain_candidates(env.module_path.clone(), cands.clone()); + if ((chain.clone().len() as i64) == 1) { + Rc::new(vec![]) + } else { + { + let pool = if ((chain.clone().len() as i64) >= 2) { + chain.clone() + } else { + cands.clone() + }; + Rc::new({ + let mut __result = Vec::new(); + for c in pool.clone().iter().cloned() { + __result.push(v1_rt::concat( + v1_rt::concat(c.module_path.clone(), ".".to_string()), + name.clone(), + )); + } + __result + }) + } + } + } + _ => Rc::new(vec![]), + } + } +} + +pub fn bare_name_miss_diagnostic( + env: Rc, + name: String, + span: Rc, +) -> Rc { + { + let cands = global_bare_strict_ambiguity_candidates(env.clone(), name.clone()); + if ((cands.clone().len() as i64) > 0) { + Rc::new(CompilerDiagnostic::AmbiguousReference { + name: name.clone(), + candidates: cands.clone(), + span: span.clone(), + }) + } else { + Rc::new(CompilerDiagnostic::UnresolvedType { + name: name.clone(), + span: span.clone(), + }) + } + } +} + pub fn global_bare_owner_module( env: Rc, owner_module_path: String, @@ -988,19 +1107,17 @@ pub fn global_bare_owner_module( }, Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { candidates: cands, .. - }) => { - match global_bare_nearest_ancestor_candidate(owner_module_path.clone(), cands.clone()) { - Some(cand) => match binding_declares_name( - cand.binding.clone(), - name.clone(), - env.source_indices.clone(), - ) { - true => Some(cand.module_path.clone()), - false => None, - }, - None => None, - } - } + }) => match global_bare_policy_candidate(owner_module_path.clone(), cands.clone()) { + Some(cand) => match binding_declares_name( + cand.binding.clone(), + name.clone(), + env.source_indices.clone(), + ) { + true => Some(cand.module_path.clone()), + false => None, + }, + None => None, + }, None => None, } } @@ -1289,14 +1406,25 @@ pub fn global_bare_lookup(env: Rc, name: String) -> Option { - if (v1_rt::resolution_silent_pick_is_enabled() && ((cands.clone().len() as i64) >= 2)) { - record_global_bare_ambiguous_silent_pick( - env.module_path.clone(), - name.clone(), - cands.clone(), - ) + if v1_rt::name_resolution_policy_is_namespace_only() { + match global_bare_unique_chain_candidate(env.module_path.clone(), cands.clone()) { + Some(cand) => Some(cand.binding.clone()), + None => None, + } + } else { + { + if (v1_rt::resolution_silent_pick_is_enabled() + && ((cands.clone().len() as i64) >= 2)) + { + record_global_bare_ambiguous_silent_pick( + env.module_path.clone(), + name.clone(), + cands.clone(), + ) + } + global_bare_nearest_ancestor(env.module_path.clone(), cands.clone()) + } } - global_bare_nearest_ancestor(env.module_path.clone(), cands.clone()) } None => None, } @@ -1309,7 +1437,13 @@ pub fn global_bare_is_ambiguous(env: Rc, name: String) -> bool { { Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { candidates: cands, .. - }) => (global_bare_nearest_ancestor(env.module_path.clone(), cands.clone()) == None), + }) => { + if v1_rt::name_resolution_policy_is_namespace_only() { + (global_bare_unique_chain_candidate(env.module_path.clone(), cands.clone()) == None) + } else { + (global_bare_nearest_ancestor(env.module_path.clone(), cands.clone()) == None) + } + } Some(GlobalBareLookupState::GlobalBareUniqueBinding { .. }) => false, None => false, } diff --git a/src/v1/stage0/src/v1_compiler_infer_lookup.rs b/src/v1/stage0/src/v1_compiler_infer_lookup.rs index 72d1cee3ab6..4a2722061b0 100644 --- a/src/v1/stage0/src/v1_compiler_infer_lookup.rs +++ b/src/v1/stage0/src/v1_compiler_infer_lookup.rs @@ -18,16 +18,19 @@ pub use crate::v1_compiler_infer_emit_info::{ }; use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::{ - authored_name, borrowed_generic_param_names, global_bare_nearest_ancestor_candidate, - is_recursive_type, lookup_binding_by_name, lookup_binding_by_name_local, lookup_type, - lookup_type_for, qualified_all_but_last, qualify_borrowed_type_names, symbol_index_lookup, + authored_name, borrowed_generic_param_names, global_bare_policy_candidate, is_recursive_type, + lookup_binding_by_name, lookup_binding_by_name_local, lookup_type, lookup_type_for, + qualified_all_but_last, qualify_borrowed_type_names, symbol_index_lookup, }; pub use crate::v1_compiler_infer_env::{GlobalBareLookupState, TypeBinding, TypeEnv}; pub use crate::v1_compiler_infer_method::infer_builtin_call_type; pub use crate::v1_compiler_infer_service::check_service_method_call_node; pub use crate::v1_compiler_infer_service::{OpEntry, ServiceMethodResult}; pub use crate::v1_compiler_infer_sigs::lookup_resolved_sig; -pub use crate::v1_compiler_infer_sigs::{ResolvedFuncEnv, ResolvedFuncSig}; +use crate::v1_compiler_infer_sigs::FuncSigLookup::{ + FuncSigAmbiguous, FuncSigResolved, FuncSigUnresolved, +}; +pub use crate::v1_compiler_infer_sigs::{FuncSigLookup, ResolvedFuncEnv, ResolvedFuncSig}; pub use crate::v1_compiler_infer_types::{ child_type_node, emit_map_has, enrich_kernel_type, is_declared_container_alias_spelling, kernel_profile_lookup, make_container_type, method_receiver_element_node, @@ -91,10 +94,30 @@ pub fn lookup_func_sig( func_env: Rc, type_env: Rc, name: String, -) -> Option> { - match lookup_resolved_sig(func_env.clone(), name.clone()) { - Some(sig) => Some(sig.clone()), - None => func_sig_from_global_bare(type_env.clone(), name.clone()), +) -> Rc { + match (*lookup_resolved_sig(func_env.clone(), name.clone())).clone() { + FuncSigLookup::FuncSigResolved { sig: sig, .. } => { + Rc::new(FuncSigLookup::FuncSigResolved { sig: sig.clone() }) + } + FuncSigLookup::FuncSigAmbiguous { + candidates: cands, .. + } => Rc::new(FuncSigLookup::FuncSigAmbiguous { + candidates: cands.clone(), + }), + FuncSigLookup::FuncSigUnresolved => { + match func_sig_from_global_bare(type_env.clone(), name.clone()) { + Some(sig) => Rc::new(FuncSigLookup::FuncSigResolved { sig: sig.clone() }), + None => Rc::new(FuncSigLookup::FuncSigUnresolved), + } + } + } +} + +pub fn func_sig_if_resolved(lookup: Rc) -> Option> { + match (*lookup.clone()).clone() { + FuncSigLookup::FuncSigResolved { sig: sig, .. } => Some(sig.clone()), + FuncSigLookup::FuncSigUnresolved => None, + FuncSigLookup::FuncSigAmbiguous { candidates: _, .. } => None, } } @@ -131,10 +154,7 @@ pub fn borrowed_census_decl(type_env: Rc, name: String) -> Option match global_bare_nearest_ancestor_candidate( - type_env.module_path.clone(), - cands.clone(), - ) { + }) => match global_bare_policy_candidate(type_env.module_path.clone(), cands.clone()) { Some(cand) => Some(Rc::new(BorrowedCensusDecl { owner_module_path: cand.module_path.clone(), node: cand.binding.clone().resolved.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index 7ba12c23938..aa05c9f6e26 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -132,6 +132,11 @@ pub fn builtin_function_registry() -> Rc>> { "resolution_silent_pick_is_enabled".to_string(), bool_type(), ); + let m = v1_rt::rc_map_insert( + m.clone(), + "name_resolution_policy_is_namespace_only".to_string(), + bool_type(), + ); let m = v1_rt::rc_map_insert( m.clone(), "resolution_silent_pick_record_global_bare_lcp_pick".to_string(), diff --git a/src/v1/stage0/src/v1_compiler_infer_resolve.rs b/src/v1/stage0/src/v1_compiler_infer_resolve.rs index 7589ed384c7..1b452ec9cec 100644 --- a/src/v1/stage0/src/v1_compiler_infer_resolve.rs +++ b/src/v1/stage0/src/v1_compiler_infer_resolve.rs @@ -9,8 +9,9 @@ use crate::std_syntax::LiteralValue::LitInt; pub use crate::std_types::container_param_name; pub use crate::std_types::SourceSpan; pub use crate::v1_compiler_infer_env::{ - authored_name, env_with_type_variable_bindings, is_recursive_type, is_recursive_type_by_name, - is_recursive_type_for, lookup_type, lookup_type_by_name, lookup_type_for, + authored_name, bare_name_miss_diagnostic, env_with_type_variable_bindings, is_recursive_type, + is_recursive_type_by_name, is_recursive_type_for, lookup_type, lookup_type_by_name, + lookup_type_for, }; pub use crate::v1_compiler_infer_env::{TypeBinding, TypeEnv}; pub use crate::v1_compiler_infer_types::{ @@ -1739,19 +1740,33 @@ pub fn resolve_node_bounded( diagnostics: Rc::new(vec![]), }) } else { - match lookup_unit_variant_phantom_type(env.clone(), authored_name(env.clone(), n.clone())) { - Some(phantom) => Rc::new(NodeResolveResult { - resolved: phantom.clone(), - diagnostics: Rc::new(vec![]), -}), - None => Rc::new(NodeResolveResult { - resolved: n.clone(), - diagnostics: Rc::new(vec![make_error_node(Rc::new(CompilerDiagnostic::UnresolvedType { - name: authored_name(env.clone(), n.clone()), - span: n.span.clone(), -}), module_name.clone())]), -}), -} + match lookup_unit_variant_phantom_type( + env.clone(), + authored_name(env.clone(), n.clone()), + ) { + Some(phantom) => { + Rc::new(NodeResolveResult { + resolved: phantom.clone(), + diagnostics: Rc::new(vec![]), + }) + } + None => Rc::new(NodeResolveResult { + resolved: n.clone(), + diagnostics: Rc::new(vec![ + make_error_node( + bare_name_miss_diagnostic( + env.clone(), + authored_name( + env.clone(), + n.clone(), + ), + n.span.clone(), + ), + module_name.clone(), + ), + ]), + }), + } } } } diff --git a/src/v1/stage0/src/v1_compiler_infer_sigs.rs b/src/v1/stage0/src/v1_compiler_infer_sigs.rs index c3bb0ff20de..c151e885912 100644 --- a/src/v1/stage0/src/v1_compiler_infer_sigs.rs +++ b/src/v1/stage0/src/v1_compiler_infer_sigs.rs @@ -1,6 +1,7 @@ // Generated by v1 compiler -- do not edit. // Source module: v1.compiler.infer_sigs +use self::FuncSigLookup::*; pub use crate::std_induction::SubValueRelation; use crate::std_induction::SubValueRelation::*; pub use crate::v1_compiler_infer_types::emit_map_has; @@ -112,6 +113,81 @@ pub struct ParentSigScan { pub first_parent: Option, } +pub fn func_sig_lookup_outcome_note() -> String { + thread_local! { + static CACHED: String = { + "namespace-resolution-design.md 13 / 8 step 1, fn path: ResolvedFuncSig? overloaded Absent as 'keep looking' (the census fallback fires on it), so a refusal had nowhere to go — the first-hit over func_env.parents was the fn silent-pick class (fn_parent_first_hit) with NO refusal arm at all. FuncSigLookup is the 3-state outcome: FuncSigResolved binds, FuncSigUnresolved means genuinely-no-sig (census fallback may still run), FuncSigAmbiguous carries the full candidate list and REFUSES — it never falls through to a fallback. Under the default ImportScoped policy the Ambiguous arm is unreachable (first-hit preserved verbatim); under NamespaceOnlyY exactly-one match across the flat parent closure resolves, two-plus refuses. Own-module local hit stays first on both arms: the killed class is the pick among >=2 PARENT matches (the census's fn_parent_first_hit definition), not own-decl precedence. Analysis-only consumers (provenance/descent enrichment) project through func_sig_if_resolved in infer_lookup — they never fabricate a bind; the semantic bind sites in 04_infer match the full outcome and emit the typed AmbiguousReference.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(tag = "_variant")] +pub enum FuncSigLookup { + FuncSigResolved { sig: Rc }, + FuncSigUnresolved, + FuncSigAmbiguous { candidates: Rc> }, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ChainSigScan { + pub first_sig: Option>, + pub owners: Rc>, +} + +pub fn lookup_resolved_sig_unique_across_parents( + env: Rc, + name: String, +) -> Rc { + { + let scan = env.parents.clone().iter().cloned().fold( + Rc::new(ChainSigScan { + first_sig: None, + owners: Rc::new(vec![]), + }), + |acc: Rc, p: Rc| match v1_rt::map_get( + &p.local.clone(), + name.clone(), + ) { + Some(sig) => Rc::new(ChainSigScan { + first_sig: if (acc.first_sig.clone() != None) { + acc.first_sig.clone() + } else { + Some(sig.clone()) + }, + owners: v1_rt::concat(acc.owners.clone(), Rc::new(vec![p.name.clone()])), + }), + None => acc.clone(), + }, + ); + let owner_count = (scan.owners.clone().len() as i64); + if (owner_count.clone() == 0) { + Rc::new(FuncSigLookup::FuncSigUnresolved) + } else { + if (owner_count.clone() == 1) { + match scan.first_sig.clone() { + Some(sig) => Rc::new(FuncSigLookup::FuncSigResolved { sig: sig.clone() }), + None => Rc::new(FuncSigLookup::FuncSigUnresolved), + } + } else { + Rc::new(FuncSigLookup::FuncSigAmbiguous { + candidates: Rc::new({ + let mut __result = Vec::new(); + for o in scan.owners.clone().iter().cloned() { + __result.push(v1_rt::concat( + v1_rt::concat(o.clone(), ".".to_string()), + name.clone(), + )); + } + __result + }), + }) + } + } + } +} + pub fn lookup_resolved_sig_with_telemetry( env: Rc, name: String, @@ -158,20 +234,32 @@ pub fn lookup_resolved_sig_with_telemetry( } } -pub fn lookup_resolved_sig(env: Rc, name: String) -> Option> { +pub fn lookup_resolved_sig(env: Rc, name: String) -> Rc { match v1_rt::map_get(&env.local.clone(), name.clone()) { - Some(sig) => Some(sig.clone()), + Some(sig) => Rc::new(FuncSigLookup::FuncSigResolved { sig: sig.clone() }), None => { - if v1_rt::resolution_silent_pick_is_enabled() { - lookup_resolved_sig_with_telemetry(env.clone(), name.clone()) + if v1_rt::name_resolution_policy_is_namespace_only() { + lookup_resolved_sig_unique_across_parents(env.clone(), name.clone()) } else { - env.parents.clone().iter().cloned().fold( - none_resolved_sig(), - |acc: Option>, p: Rc| match acc.clone() { - Some(sig) => Some(sig.clone()), - None => v1_rt::map_get(&p.local.clone(), name.clone()), - }, - ) + { + let first_hit = if v1_rt::resolution_silent_pick_is_enabled() { + lookup_resolved_sig_with_telemetry(env.clone(), name.clone()) + } else { + env.parents.clone().iter().cloned().fold( + none_resolved_sig(), + |acc: Option>, p: Rc| match acc + .clone() + { + Some(sig) => Some(sig.clone()), + None => v1_rt::map_get(&p.local.clone(), name.clone()), + }, + ) + }; + match first_hit.clone() { + Some(sig) => Rc::new(FuncSigLookup::FuncSigResolved { sig: sig.clone() }), + None => Rc::new(FuncSigLookup::FuncSigUnresolved), + } + } } } } diff --git a/src/v1/stage0/src/v1_compiler_runtime_rust.rs b/src/v1/stage0/src/v1_compiler_runtime_rust.rs index 750b90979d8..dd8853b968a 100644 --- a/src/v1/stage0/src/v1_compiler_runtime_rust.rs +++ b/src/v1/stage0/src/v1_compiler_runtime_rust.rs @@ -92,7 +92,7 @@ pub fn rt_hash_ops() -> String { } pub fn rt_resolution_silent_pick_telemetry() -> 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(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("/// Read-only silent-pick telemetry for resolution divergence census slice 2.\n".to_string(), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct GlobalBareLcpPickSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub candidate_count: usize,\n".to_string()), " pub chosen_module_path: String,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct GlobalBareLcpTieSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub candidate_count: usize,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct FnParentFirstHitSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub parent_match_count: usize,\n".to_string()), " pub chosen_parent_module: String,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct SilentPickTelemetry {\n".to_string()), " pub global_bare_lcp_picks: std::vec::Vec,\n".to_string()), " pub global_bare_lcp_ties: std::vec::Vec,\n".to_string()), " pub fn_parent_first_hits: std::vec::Vec,\n".to_string()), "}\n\n".to_string()), "thread_local! {\n".to_string()), " static RESOLUTION_SILENT_PICK_ENABLED: Cell = const { Cell::new(false) };\n".to_string()), " static RESOLUTION_SILENT_PICK: RefCell = RefCell::new(SilentPickTelemetry::default());\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_enable() {\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| *t.borrow_mut() = SilentPickTelemetry::default());\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.set(true));\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_disable() -> SilentPickTelemetry {\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.set(false));\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| std::mem::take(&mut *t.borrow_mut()))\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_is_enabled() -> bool {\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.get())\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_global_bare_lcp_pick(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " candidate_count: i64,\n".to_string()), " chosen_module_path: String,\n".to_string()), ") {\n".to_string()), " let candidate_count = candidate_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || candidate_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().global_bare_lcp_picks.push(GlobalBareLcpPickSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " candidate_count,\n".to_string()), " chosen_module_path,\n".to_string()), " });\n".to_string()), " });\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_global_bare_lcp_tie(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " candidate_count: i64,\n".to_string()), ") {\n".to_string()), " let candidate_count = candidate_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || candidate_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().global_bare_lcp_ties.push(GlobalBareLcpTieSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " candidate_count,\n".to_string()), " });\n".to_string()), " });\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_fn_parent_first_hit(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " parent_match_count: i64,\n".to_string()), " chosen_parent_module: String,\n".to_string()), ") {\n".to_string()), " let parent_match_count = parent_match_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || parent_match_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().fn_parent_first_hits.push(FnParentFirstHitSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " parent_match_count,\n".to_string()), " chosen_parent_module,\n".to_string()), " });\n".to_string()), " });\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(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("/// Read-only silent-pick telemetry for resolution divergence census slice 2.\n".to_string(), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct GlobalBareLcpPickSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub candidate_count: usize,\n".to_string()), " pub chosen_module_path: String,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct GlobalBareLcpTieSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub candidate_count: usize,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct FnParentFirstHitSite {\n".to_string()), " pub env_module_path: String,\n".to_string()), " pub name: String,\n".to_string()), " pub parent_match_count: usize,\n".to_string()), " pub chosen_parent_module: String,\n".to_string()), "}\n\n".to_string()), "#[derive(Clone, Debug, Default, PartialEq, Eq)]\n".to_string()), "pub struct SilentPickTelemetry {\n".to_string()), " pub global_bare_lcp_picks: std::vec::Vec,\n".to_string()), " pub global_bare_lcp_ties: std::vec::Vec,\n".to_string()), " pub fn_parent_first_hits: std::vec::Vec,\n".to_string()), "}\n\n".to_string()), "thread_local! {\n".to_string()), " static RESOLUTION_SILENT_PICK_ENABLED: Cell = const { Cell::new(false) };\n".to_string()), " static RESOLUTION_SILENT_PICK: RefCell = RefCell::new(SilentPickTelemetry::default());\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_enable() {\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| *t.borrow_mut() = SilentPickTelemetry::default());\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.set(true));\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_disable() -> SilentPickTelemetry {\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.set(false));\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| std::mem::take(&mut *t.borrow_mut()))\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_is_enabled() -> bool {\n".to_string()), " RESOLUTION_SILENT_PICK_ENABLED.with(|e| e.get())\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_global_bare_lcp_pick(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " candidate_count: i64,\n".to_string()), " chosen_module_path: String,\n".to_string()), ") {\n".to_string()), " let candidate_count = candidate_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || candidate_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().global_bare_lcp_picks.push(GlobalBareLcpPickSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " candidate_count,\n".to_string()), " chosen_module_path,\n".to_string()), " });\n".to_string()), " });\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_global_bare_lcp_tie(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " candidate_count: i64,\n".to_string()), ") {\n".to_string()), " let candidate_count = candidate_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || candidate_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().global_bare_lcp_ties.push(GlobalBareLcpTieSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " candidate_count,\n".to_string()), " });\n".to_string()), " });\n".to_string()), "}\n\n".to_string()), "// NameResolutionPolicy gate (namespace-resolution-design.md 13, 8 step 1): default\n".to_string()), "// false = ImportScoped (today's resolver, byte-for-byte). True = NamespaceOnlyY —\n".to_string()), "// strict unique-on-chain in the type/value path and exactly-one across the fn\n".to_string()), "// parent closure, with typed Ambiguous refusals. Host-side setter only; no .dag\n".to_string()), "// surface can flip it (no escape hatch — a flip is an explicit host decision).\n".to_string()), "thread_local! {\n".to_string()), " static NAME_RESOLUTION_POLICY_NAMESPACE_ONLY: Cell = const { Cell::new(false) };\n".to_string()), "}\n\n".to_string()), "pub fn name_resolution_policy_set_namespace_only(enabled: bool) {\n".to_string()), " NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.set(enabled));\n".to_string()), "}\n\n".to_string()), "pub fn name_resolution_policy_is_namespace_only() -> bool {\n".to_string()), " NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.get())\n".to_string()), "}\n\n".to_string()), "pub fn resolution_silent_pick_record_fn_parent_first_hit(\n".to_string()), " env_module_path: String,\n".to_string()), " name: String,\n".to_string()), " parent_match_count: i64,\n".to_string()), " chosen_parent_module: String,\n".to_string()), ") {\n".to_string()), " let parent_match_count = parent_match_count.max(0) as usize;\n".to_string()), " if !resolution_silent_pick_is_enabled() || parent_match_count < 2 {\n".to_string()), " return;\n".to_string()), " }\n".to_string()), " RESOLUTION_SILENT_PICK.with(|t| {\n".to_string()), " t.borrow_mut().fn_parent_first_hits.push(FnParentFirstHitSite {\n".to_string()), " env_module_path,\n".to_string()), " name,\n".to_string()), " parent_match_count,\n".to_string()), " chosen_parent_module,\n".to_string()), " });\n".to_string()), " });\n".to_string()), "}\n\n".to_string()) } pub fn rust_runtime_source() -> String { diff --git a/src/v1/stage0/src/v1_rt.rs b/src/v1/stage0/src/v1_rt.rs index a1cb9797964..7f49e891b4b 100644 --- a/src/v1/stage0/src/v1_rt.rs +++ b/src/v1/stage0/src/v1_rt.rs @@ -155,6 +155,23 @@ pub fn resolution_silent_pick_record_global_bare_lcp_tie( }); } +// NameResolutionPolicy gate (namespace-resolution-design.md 13, 8 step 1): default +// false = ImportScoped (today's resolver, byte-for-byte). True = NamespaceOnlyY — +// strict unique-on-chain in the type/value path and exactly-one across the fn +// parent closure, with typed Ambiguous refusals. Host-side setter only; no .dag +// surface can flip it (no escape hatch — a flip is an explicit host decision). +thread_local! { + static NAME_RESOLUTION_POLICY_NAMESPACE_ONLY: Cell = const { Cell::new(false) }; +} + +pub fn name_resolution_policy_set_namespace_only(enabled: bool) { + NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.set(enabled)); +} + +pub fn name_resolution_policy_is_namespace_only() -> bool { + NAME_RESOLUTION_POLICY_NAMESPACE_ONLY.with(|e| e.get()) +} + pub fn resolution_silent_pick_record_fn_parent_first_hit( env_module_path: String, name: String, diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index 760e7efbf3f..e28075c954c 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -440,6 +440,11 @@ pub enum CompilerDiagnostic { name: String, span: Rc, }, + AmbiguousReference { + name: String, + candidates: Rc>, + span: Rc, + }, } impl CompilerDiagnostic { pub fn span(&self) -> Rc { @@ -463,6 +468,7 @@ impl CompilerDiagnostic { CompilerDiagnostic::VariantCollision { span: __val, .. } => __val.clone(), CompilerDiagnostic::SoleConstructorViolation { span: __val, .. } => __val.clone(), CompilerDiagnostic::UnlistedImportUse { span: __val, .. } => __val.clone(), + CompilerDiagnostic::AmbiguousReference { span: __val, .. } => __val.clone(), } } } @@ -499,6 +505,7 @@ pub fn diagnostic_to_span(d: Rc) -> Rc { CompilerDiagnostic::VariantCollision { span: s, .. } => s.clone(), CompilerDiagnostic::SoleConstructorViolation { span: s, .. } => s.clone(), CompilerDiagnostic::UnlistedImportUse { span: s, .. } => s.clone(), + CompilerDiagnostic::AmbiguousReference { span: s, .. } => s.clone(), } } @@ -706,6 +713,26 @@ pub fn diagnostic_to_message(d: Rc) -> String { v1_rt::concat("unlisted import use '".to_string(), n.clone()), "' (referenced but not in any import's name list)".to_string(), ), + CompilerDiagnostic::AmbiguousReference { + name: n, + candidates: cs, + .. + } => v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat("ambiguous reference '".to_string(), n.clone()), + "': ".to_string(), + ), + (cs.clone().len() as i64).to_string(), + ), + " candidates: ".to_string(), + ), + cs.clone().join(&", ".to_string()), + ), + " — qualify by containment path, alias, or rename".to_string(), + ), } } diff --git a/src/v1/tests/src/func_env_scope_chain_test.rs b/src/v1/tests/src/func_env_scope_chain_test.rs index bce84b7c973..1bd5de20b30 100644 --- a/src/v1/tests/src/func_env_scope_chain_test.rs +++ b/src/v1/tests/src/func_env_scope_chain_test.rs @@ -12,7 +12,7 @@ use v1_compiler::cli_run::{ use v1_compiler::v1_compiler_compile::{compile_to_resolved, SourceFile}; use v1_compiler::v1_compiler_infer::{infer_expr, InferScope}; use v1_compiler::v1_compiler_infer_items::{ResolvedGraph, TypedModule}; -use v1_compiler::v1_compiler_infer_lookup::lookup_func_sig; +use v1_compiler::v1_compiler_infer_lookup::{func_sig_if_resolved, lookup_func_sig}; use v1_compiler::v1_compiler_infer_sigs::{lookup_resolved_sig, ResolvedFuncEnv, ResolvedFuncSig}; use v1_compiler::v1_interpreter::{self, ExecutionMode, Value}; use v1_compiler::v1_std_core::{authored_name_at, diagnostic_to_message}; @@ -79,13 +79,16 @@ fn assert_rc_identity_across_import_chain( ) { let def_mod = typed_module_by_name(&graph.modules, source_indices, "test.func_env_rc_definer"); let use_mod = typed_module_by_name(&graph.modules, source_indices, "test.func_env_rc_consumer"); - let def_sig = lookup_resolved_sig(def_mod.func_env.clone(), "shared_fn".to_string()) - .expect("definer local shared_fn"); - let use_sig = lookup_func_sig( + let def_sig = func_sig_if_resolved(lookup_resolved_sig( + def_mod.func_env.clone(), + "shared_fn".to_string(), + )) + .expect("definer local shared_fn"); + let use_sig = func_sig_if_resolved(lookup_func_sig( use_mod.func_env.clone(), use_mod.type_env.clone(), "shared_fn".to_string(), - ) + )) .expect("consumer lookup shared_fn"); assert!( Rc::ptr_eq(&def_sig, &use_sig), @@ -290,11 +293,11 @@ fn func_env_dropped_parent_chain_fails_lookup() { "test.func_env_rc_consumer", ); assert!( - lookup_func_sig( + func_sig_if_resolved(lookup_func_sig( consumer.func_env.clone(), consumer.type_env.clone(), "shared_fn".to_string() - ) + )) .is_some(), "sanity: imported shared_fn must resolve with intact parent chain" ); @@ -314,11 +317,11 @@ fn func_env_dropped_parent_chain_fails_lookup() { ..(*consumer.type_env).clone() }); assert!( - lookup_func_sig( + func_sig_if_resolved(lookup_func_sig( stripped.clone(), census_stripped_env.clone(), "shared_fn".to_string() - ) + )) .is_none(), "perturbation: stripping parents from a real import consumer must break \ imported name lookup (chain-walk is load-bearing, not decorative)" diff --git a/src/v1/tests/src/lib.rs b/src/v1/tests/src/lib.rs index d8088c93c0d..4caa12ca243 100644 --- a/src/v1/tests/src/lib.rs +++ b/src/v1/tests/src/lib.rs @@ -105,6 +105,8 @@ mod measure_value_arg_unit_collapse_test; #[cfg(test)] mod module_authority_resolution_test; #[cfg(test)] +mod namespace_unique_on_chain_policy_test; +#[cfg(test)] mod nested_fold_generic_param_emit_test; mod nested_list_alias_emit_test; #[cfg(test)] diff --git a/src/v1/tests/src/namespace_unique_on_chain_policy_test.rs b/src/v1/tests/src/namespace_unique_on_chain_policy_test.rs new file mode 100644 index 00000000000..c05ee4885a2 --- /dev/null +++ b/src/v1/tests/src/namespace_unique_on_chain_policy_test.rs @@ -0,0 +1,235 @@ +//! §8-step-1 discriminating witness (namespace-resolution-design.md §13, operator-ratified +//! 2026-07-21): the executing v1 seed resolver carries a NameResolutionPolicy gate — +//! default OFF = ImportScoped (today's nearest-wins / first-hit, byte-for-byte), ON = +//! NamespaceOnlyY strict unique-on-chain with typed `AmbiguousReference` refusals. +//! +//! The homonym fixtures below are the discriminating inputs: the SAME sources compile +//! clean under ImportScoped and refuse (typed, located, full candidate list) under +//! NamespaceOnlyY — on both the type/value path (ancestor-chain homonym resolved by +//! nearest-wins today) and the fn path (first-hit over `func_env.parents`, the +//! `fn_parent_first_hit` silent-pick class, which had NO refusal arm at all). +//! Controls pin the boundary: a chain-unique homonym still resolves under the strict +//! policy, and a genuinely-unbound name stays `UnresolvedType` — the refusal is typed, +//! never a blanket. + +use std::rc::Rc; + +use v1_compiler::v1_compiler_compile::{compile_to_resolved, SourceFile}; +use v1_compiler::v1_rt::name_resolution_policy_set_namespace_only; +use v1_compiler::v1_std_core::{diagnostic_to_message, is_error_diagnostic}; + +/// Panic-safe policy bracket: the gate is thread-local and each test compiles on its +/// own thread, so enable/reset needs no cross-test lock — only drop-safety. +struct NamespaceOnlyGuard; + +impl NamespaceOnlyGuard { + fn enable() -> Self { + name_resolution_policy_set_namespace_only(true); + NamespaceOnlyGuard + } +} + +impl Drop for NamespaceOnlyGuard { + fn drop(&mut self) { + name_resolution_policy_set_namespace_only(false); + } +} + +fn src(path: &str, content: &str) -> Rc { + Rc::new(SourceFile { + path: path.to_string(), + content: content.to_string(), + }) +} + +fn error_diag_messages(sources: Vec>) -> Vec { + let resolved = compile_to_resolved(Rc::new(sources.into())); + resolved + .diagnostics + .iter() + .filter(|d| is_error_diagnostic(d.diagnostic.clone())) + .map(|d| diagnostic_to_message(d.diagnostic.clone())) + .collect() +} + +/// Chain homonym on BOTH paths. `fixchain` and `fixchain.mid` are both ancestors of the +/// referencing module `fixchain.mid.leaf`: +/// - type/value path: bare `Homonym` — ImportScoped resolves nearest (`fixchain.mid`), +/// NamespaceOnlyY sees 2 binders on the chain and refuses; +/// - fn path: bare `pick()` with two glob imports both providing it — ImportScoped +/// first-hits over the flat parent closure, NamespaceOnlyY refuses on 2 matches. +fn homonym_fixture() -> Vec> { + vec![ + src( + "fixchain.dag", + "module fixchain\ntype Homonym { tag: Int }\n", + ), + src( + "fixchain_mid.dag", + "module fixchain.mid\ntype Homonym { other: Int }\n", + ), + src( + "fixfns_one.dag", + "module fixfns.one\nfn pick() -> Int { 1 }\n", + ), + src( + "fixfns_two.dag", + "module fixfns.two\nfn pick() -> Int { 2 }\n", + ), + src( + "leaf.dag", + "module fixchain.mid.leaf\nimport fixfns.one\nimport fixfns.two\nfn use_homonym(x: Homonym) -> Homonym { x }\nfn call_pick() -> Int { pick() }\n", + ), + ] +} + +/// Control fixture: homonym whose candidates put EXACTLY ONE binder on the referencing +/// chain (`Duo` in ancestor `fixchain.mid` vs non-ancestor `fixother`) — must resolve +/// under BOTH policies (the strict rule is unique-on-chain, not no-homonyms-anywhere). +fn unique_on_chain_fixture() -> Vec> { + vec![ + src( + "fixchain_mid.dag", + "module fixchain.mid\ntype Duo { d: Int }\n", + ), + src("fixother.dag", "module fixother\ntype Duo { e: Int }\n"), + src( + "leaf.dag", + "module fixchain.mid.leaf\nfn use_duo(x: Duo) -> Duo { x }\n", + ), + ] +} + +/// Control fixture: whole-pool homonym with ZERO binders on the referencing chain +/// (`Stray` declared only in two non-ancestor siblings). ImportScoped already refuses +/// (all-disjoint LCP tie) as `UnresolvedType`; NamespaceOnlyY refuses as the honest +/// `AmbiguousReference` with the full pool — mirroring the census containment walk +/// (lexical Unbound + whole-pool-ambiguous => Ambiguous, never a fabricated bind). +fn zero_on_chain_fixture() -> Vec> { + vec![ + src("fixother.dag", "module fixother\ntype Stray { a: Int }\n"), + src("fixother2.dag", "module fixother2\ntype Stray { b: Int }\n"), + src( + "leaf.dag", + "module fixchain.mid.leaf\nfn use_stray(x: Stray) -> Stray { x }\n", + ), + ] +} + +/// Control fixture: a genuinely-unbound name (declared nowhere). +fn unbound_fixture() -> Vec> { + vec![src( + "leaf.dag", + "module fixchain.mid.leaf\nfn use_missing(x: NoSuchTypeAnywhere) -> NoSuchTypeAnywhere { x }\n", + )] +} + +#[test] +fn import_scoped_default_resolves_homonym_fixture_clean() { + let diags = error_diag_messages(homonym_fixture()); + assert!( + diags.is_empty(), + "default ImportScoped policy must preserve today's behavior verbatim \ + (nearest-wins type resolution + first-hit fn resolution); got {diags:?}" + ); +} + +#[test] +fn namespace_only_refuses_chain_homonym_on_type_path() { + let _guard = NamespaceOnlyGuard::enable(); + let diags = error_diag_messages(homonym_fixture()); + let homonym_refusals: Vec<&String> = diags + .iter() + .filter(|m| m.contains("ambiguous reference 'Homonym'")) + .collect(); + assert!( + !homonym_refusals.is_empty(), + "NamespaceOnlyY must refuse the 2-binders-on-chain type homonym with a typed \ + AmbiguousReference; got {diags:?}" + ); + let listing = homonym_refusals[0]; + assert!( + listing.contains("fixchain.Homonym") && listing.contains("fixchain.mid.Homonym"), + "the refusal must carry the FULL candidate list (fix menu, §13); got {listing}" + ); +} + +#[test] +fn namespace_only_refuses_fn_parent_homonym_at_call_site() { + let _guard = NamespaceOnlyGuard::enable(); + let diags = error_diag_messages(homonym_fixture()); + let pick_refusals: Vec<&String> = diags + .iter() + .filter(|m| m.contains("ambiguous reference 'pick'")) + .collect(); + assert!( + !pick_refusals.is_empty(), + "NamespaceOnlyY must refuse the 2-parent-matches fn homonym (the \ + fn_parent_first_hit silent-pick class) with a typed AmbiguousReference; got {diags:?}" + ); + let listing = pick_refusals[0]; + assert!( + listing.contains("fixfns.one.pick") && listing.contains("fixfns.two.pick"), + "the fn refusal must carry both parent candidates; got {listing}" + ); +} + +#[test] +fn namespace_only_unique_on_chain_still_resolves() { + let _guard = NamespaceOnlyGuard::enable(); + let diags = error_diag_messages(unique_on_chain_fixture()); + assert!( + diags.is_empty(), + "exactly-one-binder-on-chain must RESOLVE under NamespaceOnlyY (the strict rule \ + is unique-on-chain, not no-homonyms-anywhere); got {diags:?}" + ); +} + +#[test] +fn zero_on_chain_homonym_discriminates_the_diagnostic_label() { + let import_scoped = error_diag_messages(zero_on_chain_fixture()); + assert!( + import_scoped + .iter() + .any(|m| m.contains("unresolved type 'Stray'")), + "ImportScoped all-disjoint LCP tie refuses as UnresolvedType today; got {import_scoped:?}" + ); + assert!( + !import_scoped.iter().any(|m| m.contains("ambiguous")), + "default policy must not mint AmbiguousReference; got {import_scoped:?}" + ); + + let _guard = NamespaceOnlyGuard::enable(); + let strict = error_diag_messages(zero_on_chain_fixture()); + let stray: Vec<&String> = strict + .iter() + .filter(|m| m.contains("ambiguous reference 'Stray'")) + .collect(); + assert!( + !stray.is_empty(), + "NamespaceOnlyY labels the zero-on-chain whole-pool homonym honestly as \ + Ambiguous (census walk parity), never a mislabeled UnresolvedType; got {strict:?}" + ); + assert!( + stray[0].contains("fixother.Stray") && stray[0].contains("fixother2.Stray"), + "the refusal must carry the full pool candidate list; got {}", + stray[0] + ); +} + +#[test] +fn namespace_only_keeps_genuinely_unbound_as_unresolved_not_ambiguous() { + let _guard = NamespaceOnlyGuard::enable(); + let diags = error_diag_messages(unbound_fixture()); + assert!( + diags + .iter() + .any(|m| m.contains("unresolved type 'NoSuchTypeAnywhere'")), + "a name bound NOWHERE stays UnresolvedType under the strict policy \ + (Ambiguous and Unresolved are distinct states, §5); got {diags:?}" + ); + assert!( + !diags.iter().any(|m| m.contains("ambiguous")), + "no fabricated ambiguity for an unbound name; got {diags:?}" + ); +} From 6f7408c3e222e81acac3d5d76336bc57705bc8ee Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 03:07:45 +0000 Subject: [PATCH 2/3] Retrigger CI (regen gate hit runner-local 'spawn rustfmt: Text file busy' race) Co-authored-by: Brian Searls From 1b1cbb771ccd8278e414fe2378f1ee8baeff2cde Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 23 Jul 2026 06:28:29 +0000 Subject: [PATCH 3/3] WIP: Brief A wrapu --- dag/test/retirement/model.dag | 1 + ...on_chain_policy_retained_nonmigratable.dag | 9 ++ src/v1/stage0/src/cli_run.rs | 94 ++++++++++++++++--- 3 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 dag/test/retirement/namespace_unique_on_chain_policy_retained_nonmigratable.dag diff --git a/dag/test/retirement/model.dag b/dag/test/retirement/model.dag index ba1c7460edc..975a886376a 100644 --- a/dag/test/retirement/model.dag +++ b/dag/test/retirement/model.dag @@ -3,6 +3,7 @@ module test.retirement.model type RetirementDisposition = DeleteRedundant { covered_by: String } | DeleteLowValue { reason: String } + | RetainedNonMigratable { reason: String } type TestModuleRetirement { module: String diff --git a/dag/test/retirement/namespace_unique_on_chain_policy_retained_nonmigratable.dag b/dag/test/retirement/namespace_unique_on_chain_policy_retained_nonmigratable.dag new file mode 100644 index 00000000000..39928411565 --- /dev/null +++ b/dag/test/retirement/namespace_unique_on_chain_policy_retained_nonmigratable.dag @@ -0,0 +1,9 @@ +module test.retirement.namespace_unique_on_chain_policy_retained_nonmigratable + + +data namespace_unique_on_chain_policy_retention: TestModuleRetirement = TestModuleRetirement { + module: "namespace_unique_on_chain_policy_test.rs", + disposition: RetainedNonMigratable { + reason: "Six discriminating witnesses for the section 8 step 1 namespace-only unique-on-chain resolver gate (name_resolution_policy_set_namespace_only thread-local host behavior). A .dag witness cannot reach the policy toggle, so this v1 seed module must stay .rs until src/v1 is deleted. Dissolves when the v1 seed and its host-level policy gate retire at the terminal collapse." + } +} diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 0e5792b5218..ff25b46d6c3 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -23430,14 +23430,14 @@ fn test_migration_debt_stem_covered(v1_stem: &str, floor_stems: &[String]) -> bo // Second stem source (typed retirement path): a `_retired.dag` declaration under the // corpus records a reviewed, typed retirement (delete-redundant / delete-low-value) for a v1 // test module whose behavior does NOT migrate to an exact-stem floor `*_test.dag` witness. A -// retired stem covers the module identically to a floor-witness stem — it excludes the module -// from the debt roster and authorizes its delete through the delete-guard. The typed disposition -// and its justification live in the `.dag` decl (`test.retirement.model`, single authority, -// type-checked by the compile-clean gate); this guard reads only the filename stem, exactly as -// it reads floor witnesses. A file counts only if it actually *constructs* a `TestModuleRetirement` -// (the `TestModuleRetirement {` constructor form) — an empty stub, or one that merely imports the -// type without declaring a retirement (`{ TestModuleRetirement }`), cannot silence the guard. The -// compile-clean gate independently type-checks the constructed value against `test.retirement.model`. +// retired stem authorizes its delete through the delete-guard and excludes the module from the +// debt roster. The typed disposition and its justification live in the `.dag` decl +// (`test.retirement.model`, single authority, type-checked by the compile-clean gate); this +// guard reads only the filename stem, exactly as it reads floor witnesses. A file counts only +// if it actually *constructs* a `TestModuleRetirement` (the `TestModuleRetirement {` +// constructor form) — an empty stub, or one that merely imports the type without declaring a +// retirement (`{ TestModuleRetirement }`), cannot silence the guard. The compile-clean gate +// independently type-checks the constructed value against `test.retirement.model`. fn test_migration_retired_stems() -> Vec { let mut stems: Vec = corpus_dag_files() .into_iter() @@ -23456,9 +23456,33 @@ fn test_migration_retired_stems() -> Vec { stems } -// The covered set consumed by both the debt roster and the delete-guard: floor-witness stems -// (migrate path) unioned with retired stems (delete path). One union, two consumers. -fn test_migration_covered_stems() -> Vec { +// Third stem source (typed retention path): a `_retained_nonmigratable.dag` declaration +// records a v1 seed test that cannot migrate to a floor witness (host-level runtime behavior) +// and must stay `.rs` until `src/v1` deletes. A retained stem excludes the module from the +// debt roster ONLY — it never authorizes delete through the delete-guard (that would be a +// fail-open: a kept test marked delete-authorized). Distinct filename suffix from +// `_retired.dag` so the two consumers stay split by construction. +fn test_migration_retained_nonmigratable_stems() -> Vec { + let mut stems: Vec = corpus_dag_files() + .into_iter() + .filter(|(_, content)| content.contains("TestModuleRetirement {")) + .filter_map(|(path, _)| { + let file_name = std::path::Path::new(&path) + .file_name() + .and_then(|n| n.to_str())?; + file_name + .strip_suffix("_retained_nonmigratable.dag") + .map(|s| s.to_string()) + }) + .collect(); + stems.sort(); + stems.dedup(); + stems +} + +// Delete-guard authorize set: floor-witness stems (migrate path) unioned with retired stems +// (delete path). Retained-non-migratable stems are deliberately absent. +fn test_migration_delete_guard_covered_stems() -> Vec { let mut stems = test_migration_debt_floor_stems(); stems.extend(test_migration_retired_stems()); stems.sort(); @@ -23466,9 +23490,19 @@ fn test_migration_covered_stems() -> Vec { stems } +// Debt-roster exclude set: floor witnesses, delete retirements, and retained-non-migratable +// classifications. Wider than the delete-guard authorize set by design. +fn test_migration_debt_covered_stems() -> Vec { + let mut stems = test_migration_delete_guard_covered_stems(); + stems.extend(test_migration_retained_nonmigratable_stems()); + stems.sort(); + stems.dedup(); + stems +} + fn build_test_migration_debt_report() -> TestMigrationDebtReport { let dir = test_migration_debt_v1_test_dir(); - let floor_stems = test_migration_covered_stems(); + let floor_stems = test_migration_debt_covered_stems(); let mut entries = Vec::new(); let read_dir = match std::fs::read_dir(&dir) { Ok(rd) => rd, @@ -23645,7 +23679,7 @@ fn test_migration_delete_guard_uncovered_deletes_inner() -> Result, if base_rev == head_rev { return Ok(Vec::new()); } - let floor_stems = test_migration_covered_stems(); + let floor_stems = test_migration_delete_guard_covered_stems(); let deleted = test_migration_delete_guard_deleted_v1_test_paths(&base, &head)?; let mut violations = Vec::new(); for path in deleted { @@ -24296,7 +24330,39 @@ mod test_migration_debt_tests { !test_migration_debt_floor_stems().iter().any(|s| s == stem), "stem must be covered only via retirement, not a floor witness" ); - assert!(test_migration_covered_stems().iter().any(|s| s == stem)); + assert!(test_migration_delete_guard_covered_stems().iter().any(|s| s == stem)); + } + + // Retained-non-migratable: debt-exclude only, never delete-authorize. + #[test] + fn retained_nonmigratable_stem_excludes_debt_but_not_delete_guard() { + let stem = "namespace_unique_on_chain_policy"; + assert!( + test_migration_retained_nonmigratable_stems() + .iter() + .any(|s| s == stem), + "retained-nonmigratable declaration must contribute its stem" + ); + assert!( + test_migration_debt_covered_stems().iter().any(|s| s == stem), + "retained stem must exclude the module from the debt roster" + ); + assert!( + !test_migration_delete_guard_covered_stems() + .iter() + .any(|s| s == stem), + "retained stem must NOT authorize delete through the guard" + ); + } + + #[test] + fn retained_nonmigratable_module_is_not_debt() { + assert!( + !test_migration_debt_module_names() + .iter() + .any(|m| m == "namespace_unique_on_chain_policy_test.rs"), + "a retained-non-migratable module must drop out of the debt roster" + ); } // The retired module no longer appears in the debt roster (the retirement excluded it).