This way, we don't need the MapToMergedBounds logic to split the merged list back into lower/upper.
Also, this commit avoids the quadratic merge-everything-with-everything else -- instead we use a dictionary to compare only types that are equivalent to begin with.
This is the same approach as Roslyn MethodTypeInference.Fix/AddAllCandidates.
* merge object/dynamic distinctions like we do with tuple element names. This fixes BestCommonType(object, dynamic).
* add a test that `new[] { 1, null }` has the "best common type" = `int`. The conversion error from `null` to `int` only happens later, it's not related to the best common type computation.
The span and tuple tests need types the legacy reference mscorlib predates, so
each of them opened System.Runtime.dll from the ref-assembly toolset into its own
SimpleCompilation - five copies of the same block, five reads per test run. One
shared lazy compilation covers all of them, and it includes the test assembly so
the operator and extension-method fixtures the conversion tests rely on resolve.
Part of #829.
Assisted-by: Claude:claude-opus-5:Claude Code
Auditing against the first-class-span-types proposal turned up three
deviations, each now pinned by resolver unit tests whose expectations were
established by compiling probe programs with the C# 14 compiler.
Lower-bound type inference recursed into Span<T> targets as another
lower-bound inference, but Span<T> is invariant and the spec demands an
exact element inference there: M<T>(Span<T>, T) with (Span<string>, object)
must fail inference (CS0411), not unify to T=object.
Better-conversion-target compared ReadOnlySpan element types where the spec
compares the span types, admitting numeric and user-defined element
conversions the span types do not share: overloads taking ReadOnlySpan<int>
and ReadOnlySpan<long> are ambiguous (CS0121), not resolvable. The general
mutual-convertibility rule already implements the spec's span-type test, so
the element-level block is simply removed; the ReadOnlySpan-over-Span
identity rule stays, since it deliberately inverts that general rule.
The explicit span conversion did not exist at all, and with it the rule that
user-defined conversions are not considered between span-convertible types.
The visible consequence: string[] to Span<object> classified as an implicit
user-defined conversion via op_Implicit(object[]) plus array covariance,
where the compiler reports CS0266 - only the explicit span conversion
exists. Span conversions are also no longer considered for extension
receivers during method group conversion (CS0123), while invocations keep
them.
Part of #829.
Assisted-by: Claude:claude-fable-5:Claude Code
When fixing a type parameter, Roslyn merges the tuple element names of
bounds that are identical apart from those names: names are kept where
all bounds agree and dropped where they conflict (MergeTupleNames in
Roslyn's MethodTypeInference.cs). The C# standard does not describe
this step. Without it, fixing either kept the first bound's names
verbatim or, with two exact bounds differing only in names, failed
outright - so inferred tuple types could carry names csc would not
produce. All merged-name expectations are csc-verified.
Nullability is deliberately not merged: Roslyn derives it from the
variance of the position, which this implementation does not track, so
bounds that differ in it stay distinct and fixing fails as before
rather than inventing an annotation.
Assisted-by: Claude:claude-fable-5:Claude Code
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
The C# standard does not mention tuple element names in type inference,
but csc merges names across bounds that differ only by them: names are
kept where all bounds agree and dropped where they conflict (Roslyn's
MergeTupleNames). All three expectations are verified against csc.
The two live tests are red at this commit: without merging, fixing
keeps the first bound's element names verbatim. The multiple-exact-
bounds case additionally requires AddExactBound to compare bounds
modulo element names; it stays ignored until that is implemented.
Assisted-by: Claude:claude-fable-5:Claude Code
Walks the inference algorithm of the standard (draft-v11, 12.6.3) and
adds a test per rule that the revived NRefactory suite did not already
exercise: exact inference for ref parameters and its non-applicability
of the base-type walk, explicit lambda parameter types, exact/upper
bound inference through arrays, nullables and variance nesting, the
unique-base-type restriction, value-type elements forcing exact
inference, conflicting exact bounds, and best common type.
Two rules are pinned as ignored tests because the implementation does
not follow the standard yet: a value argument to an 'in' parameter
infers no bound (12.6.3.7 wants a lower-bound inference), and tuple
literals are not inferred elementwise (12.6.3.7/12.6.3.8). Both tests
assert the csc-verified result and should go green when the rules are
implemented.
Nullable unwrapping in exact and upper-bound inferences needs no
dedicated code path in this implementation: T? is represented as the
constructed type Nullable<T>, so the constructed-type case already
produces the elementwise exact inference the standard asks for; the
new tests pin that equivalence. Function-pointer inference rules and
the explicit-return-type inference of 12.6.3.15 remain untested: the
former needs a MetadataModule to construct FunctionPointerType, the
latter is not representable in LambdaResolveResult.
Assisted-by: Claude:claude-fable-5:Claude Code
The direct unit tests for TypeInference were lost when the NRefactory
sources were replaced by the NuGet package (e88120cb4); since then the
class had no dedicated coverage and ConversionTests still pointed to a
test that no longer existed. Ported to the current type system API and
NUnit constraint asserts. The two tests NRefactory ignored on .NET 4.5
now pin the covariant IReadOnlyList<T> results, since the test
compilation uses the 4.5-era reference mscorlib; the common-subtype list
test gains the ReadOnlyCollectionBuilder<T> candidates contributed by
System.Core, which the NRefactory compilation did not reference.
Also includes the seven tests that only exist in upstream
icsharpcode/NRefactory (async lambdas, NullablePick, CoContraPick,
bug 9300, user-defined-conversion bounds). Upstream wrote them against
its source-based resolver harness, which this repo does not have, so
they are reexpressed as direct InferTypeArguments calls using mock
lambdas and helper types declared in the test assembly. Upstream's
InferFromImplicitAsyncLambda was missing its [Test] attribute and never
actually ran; here it does.
Assisted-by: Claude:claude-fable-5:Claude Code