Skip to content

csharp: a relative qualified name is read from the site's namespace, not the file's (#1906) - #1910

Open
swapnilpaliwal-sd wants to merge 1 commit into
apps/integration-0.1.10from
fix/1906-cs-ns-relative
Open

swapnilpaliwal-sd wants to merge 1 commit into
apps/integration-0.1.10from
fix/1906-cs-ns-relative

Conversation

@swapnilpaliwal-sd

@swapnilpaliwal-sd swapnilpaliwal-sd commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What was wrong

The relative qualified-name candidate (type_qn_tail + module_ns_chain, rank 1 in type_name_cand) is keyed per file: module_ns_chain holds every namespace the file declares a type in, plus their prefixes. In a file declaring P.Models, P.Other.Models, Z.Models and a class in P, Models.Report written in P bound to all three Report types. C# reads the first segment from the namespaces enclosing the site, innermost outward, and the innermost level that has a match wins.

What changed

  • type_name_out_of_scope(prov, ns, name, arity, group): for each site namespace and dotted written name, the candidates are ranked by the length of the prefix they were found under. Only the longest survives. A full match (the global level) survives only when no enclosing namespace matched. It is demand-driven from the dotted names each file writes (type_name_written_dotted, keyed by module so the probe reuses type_name_resolves' existing index).
  • The four site-level consumers drop what the site's enclosing type's namespace rules out: type_ref_resolves, ref_names_type, expr_new_type and heritage_resolves (the written-name clause).
  • Prune-only: type_name_resolves stays per file and unchanged. A site only removes answers from it. A site whose enclosing type is not known removes nothing.
  • No exported relation changed, and SCHEMA.md is unchanged (checked with --print-schema).

Case and controls

tests/cases/csharp/a-relative-name-is-read-from-the-sites-namespace: one file declares P.Models, P.Other.Models and Z.Models, with sites in P, in P.Other and in a type nested in P.Other. A second file declares only namespace P.

  • Site in P → P.Models.Report only. The P.Other child is not in scope.
  • Sites in P.Other and in the nested type → P.Other.Models.Report only. The outer P.Models.Report is not merged in.
  • Control: Z.Models.Report is never named.
  • Control: the single-namespace file keeps its one correct [resolved] target. It passes on both base and fix.

Base: 1/6 checks (only the single-namespace control). Fix: 6/6.

Suites

  • tests/run.py --lang csharp --jobs 3: 325/325 checks in 61 cases, including a-namespace-name-is-read-from-its-namespace (6/6).
  • graph/test/csharp/run-tests.sh (Roslyn oracle): 22/22 cases, plus the self-tests, entry-points, library-resolution, mediator, mock-constructor, overload, pipelines and remote-edge tests. All ok.

Cost (compiled engine, devrun.sh facts, solve wall time, base vs fix interleaved ×3)

subject base (s) fix (s)
C# library A (~460 files, 84k exprs) 17.3 / 17.2 / 15.4 14.4 / 17.2 / 17.5
C# library B (~410 files, 45k exprs) 9.1 / 9.9 / 10.0 8.1 / 10.5 / 9.7

The difference is within noise (the machine was loaded). New relation sizes on A / B: type_name_written_dotted 1081 / 818 (per module on B), type_name_scope_demand 32 on B, type_name_scope_level 74 / 87, ns_read_level 103 / 107, type_ref_scope_type 12.6k / 9.8k, type_name_out_of_scope 0 / 16, and every *_dropped relation 0 / 0. type_name_resolves (1.20M / 887k), type_ref_resolves, ref_names_type, expr_new_type and heritage_resolves are identical to base. Neither subject has a multi-namespace file with a clashing relative name, so their edges are unchanged.

A first version keyed the demand on the name alone, which needs a new index on type_name_resolves. In one run it measured about 1.7x the base solve time; that run was not isolated from other load. Keying the demand on (module, name) brought it back within noise.

Not changed

The relative using rule (module_in_scope via module_ns_chain) is also per file, but so is every using here. The IR does not record which namespace block a using sits in, so it cannot be scoped per site without parser support.

Fixes #1906 (close by hand on merge: integration branch)

…not the file's

`Models.Report` written inside namespace P, in a file that also declares
P.Other.Models and Z.Models, bound to all three Report types: the relative
candidate (type_qn_tail + module_ns_chain) is keyed per FILE, so every
namespace the file declares a type in qualified every name in it. C# reads the
first segment from the namespaces enclosing the site, innermost outward, and
the innermost level with a match wins.

- type_name_out_of_scope(ns, name, arity, group): per site namespace and
  dotted written name, candidates are ranked by the prefix they were found
  under; only the longest survives, and a full match survives only when no
  enclosing namespace matched. Demand-driven from names a file writes.
- type_ref_resolves, ref_names_type, expr_new_type and heritage_resolves drop
  what the site's enclosing type's namespace rules out. Prune-only: the
  file-level answer is unchanged and a site with no known enclosing type drops
  nothing.

Case a-relative-name-is-read-from-the-sites-namespace fails 5/6 on the base
(the single-namespace control passes on both) and passes 6/6.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant