You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
csharp: a namespace-relative qualified type name does not resolve, though the bare and fully-qualified spellings both do #1119
new Models.Report() — a type named by a namespace-RELATIVE qualified name — does not
resolve. The unqualified and the fully-qualified spellings of the same construction
both do.
namespaceP.Models{publicsealedclassReport{}}namespaceP{publicsealedclassCalls{publicModels.ReportPartially()=>newModels.Report();// ambiguous_unknownpublicP.Models.ReportFully()=>newP.Models.Report();// CONTROL — resolvespublicMoneyPlain()=>newMoney();// CONTROL — resolves}}
The two controls bracket it: the resolver handles a bare name and a fully-qualified
one, and loses the spelling in between — the one C# programmers actually write once a
project has more than one namespace, because it is the shortest unambiguous form.
Why
type_name_resolves / name_to_type in graph/csharp/engine/resolution/type-resolution.dl match a written name against a
type's simple name and against its fully-qualified name. A relative qualification is
neither: Models.Report has to be joined onto each namespace in scope at the site — P here — before it is either.
Why it matters
The site is not merely unresolved, it is unresolved with a type name in hand, which
is the case most likely to be read as an engine blind spot rather than a lookup gap.
Measured on a real multi-service subject: 7 of 106 sites resolved to nothing are
object creations written this way.
Acceptance
A clause that resolves A.B.C against the namespaces in scope at the site, and a
control that an ambiguous relative name — the same suffix reachable from two
namespaces in scope — produces both candidates rather than one arbitrary winner.
Raising this to P1. Measuring a real multi-service subject for a different reason
turned up what this gap actually costs, and it is not the object creations it was
filed for.
A generated gRPC client is reached by exactly this spelling. The stub is a
nested type inside a static class inside the generated namespace, and the field that
holds it is declared the way the generator's own README declares it:
usingShop.Client.Generated;// the namespace IS in scopeprivateGenerated.Service.ServiceClient_client;// namespace-relative + nested
...
await _client.GetAsync(request,headers);
The field's type does not resolve, so the receiver is untyped and every call through
the stub is labelled external: and boundary_lib — while the generated client
class is right there in the parsed source. On the subject, all 10 edges through the
in-source gRPC stub are labelled as leaving the source.
That matters for the diagnosis as much as for the count: the obvious reading is that
gRPC needs its own handling, and it does not. The intra-process hop from a client
stub to the generated class is ordinary name resolution, and it fails for the reason
filed here. (The hop from one service's stub to ANOTHER service's implementation is
a cross-process link and genuinely out of scope for a client-only engine — same
class as the cross-service work on the Java side.)
So the acceptance needs one more dimension than originally written: a NESTED
type reached through a relative qualification, A.B.C where B is a type rather
than a namespace. The two are spelled identically and only the resolver can tell
them apart, so a fix that only walks namespaces closes half of this.
Verified on the 0.1.9 branch: new Models.Report() inside namespace P resolves to P.Models.Report; the nested generated-client shape under using Shop.Client; resolves to Shop.Client.Generated.Service.ServiceClient.GetAsync (known_edge); the ambiguous control yields both types and the other controls still resolve. Fixed by #1881, shipped in 0.1.9 (#1904). One over-approximation the fix introduced is tracked separately in #1906.
Activity
swapnilpaliwal-sd commentedon Sep 20, 2026
Raising this to P1. Measuring a real multi-service subject for a different reason
turned up what this gap actually costs, and it is not the object creations it was
filed for.
A generated gRPC client is reached by exactly this spelling. The stub is a
nested type inside a static class inside the generated namespace, and the field that
holds it is declared the way the generator's own README declares it:
The field's type does not resolve, so the receiver is untyped and every call through
the stub is labelled
external:andboundary_lib— while the generated clientclass is right there in the parsed source. On the subject, all 10 edges through the
in-source gRPC stub are labelled as leaving the source.
That matters for the diagnosis as much as for the count: the obvious reading is that
gRPC needs its own handling, and it does not. The intra-process hop from a client
stub to the generated class is ordinary name resolution, and it fails for the reason
filed here. (The hop from one service's stub to ANOTHER service's implementation is
a cross-process link and genuinely out of scope for a client-only engine — same
class as the cross-service work on the Java side.)
So the acceptance needs one more dimension than originally written: a NESTED
type reached through a relative qualification,
A.B.CwhereBis a type ratherthan a namespace. The two are spelled identically and only the resolver can tell
them apart, so a fix that only walks namespaces closes half of this.
swapnilpaliwal-sd commentedon Oct 10, 2026
Verified on the 0.1.9 branch:
new Models.Report()inside namespace P resolves toP.Models.Report; the nested generated-client shape underusing Shop.Client;resolves toShop.Client.Generated.Service.ServiceClient.GetAsync(known_edge); the ambiguous control yields both types and the other controls still resolve. Fixed by #1881, shipped in 0.1.9 (#1904). One over-approximation the fix introduced is tracked separately in #1906.