Implement type parameter inference from constraints - #84655
Conversation
Promote the generic constraints of inferred type parameters into method type inference, so that constraint-only type parameters can be inferred from the values of the type parameters they constrain (dotnet/csharplang#9453). For example, given: void M<TEnumerable, TElement>(TEnumerable t) where TEnumerable : IEnumerable<TElement> a call M(new List<int>()) now infers TElement = int from the constraint. Two changes to MethodTypeInference: * DependsDirectlyOn: Xi depends on Xj when Xi occurs in Xj's constraint, so a constrained parameter is fixed before the parameters mentioned in its constraint. * Fix: after a type parameter is fixed to V, perform a lower-bound inference from V into each of its constraint types, seeding bounds for the (later-fixed) parameters that appear there. Both are gated behind LanguageVersion.Preview via the new IDS_FeatureTypeParameterInferenceFromConstraints feature id. Adds a dedicated test suite (TypeParameterInferenceFromConstraintsTests) covering positive, negative, ambiguity, nullable, async-lambda/Task<T>, overload-resolution, cyclic/self-referential, and multi-level transitive constraint scenarios, and updates 9 existing nullable tests whose inferred results legitimately improve under the feature. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR extends C# method type inference (preview-only) to allow inferring type parameters that appear only in generic constraints, and adds/updates compiler tests and feature-localization resources to support the new behavior.
Changes:
- Update
MethodTypeInferenceto (1) incorporate constraint-based dependencies and (2) seed inference bounds from fixed type parameters into their constraint types (gated by a new preview feature ID). - Add a new dedicated test suite for “type parameter inference from constraints” and update existing nullable reference type tests whose inference results change under preview.
- Add a new compiler feature string (
IDS_FeatureTypeParameterInferenceFromConstraints) and propagate it to localized.xlffiles.
Show a summary per file
| File | Description |
|---|---|
| src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs | Implements preview-gated constraint-based dependency ordering and constraint-driven lower-bound seeding during fixing. |
| src/Compilers/CSharp/Portable/Errors/MessageID.cs | Adds a new preview feature ID for gating the behavior. |
| src/Compilers/CSharp/Portable/CSharpResources.resx | Adds the feature string for diagnostics/feature gating text. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf | Adds localized XLF entry for the new feature string. |
| src/Compilers/CSharp/Test/Semantic/Semantics/TypeParameterInferenceFromConstraintsTests.cs | New test suite covering positive/negative and edge cases for the new inference behavior. |
| src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs | Updates expected diagnostics where preview inference results (and downstream nullability diagnostics) change. |
Copilot's findings
- Files reviewed: 18/18 changed files
- Comments generated: 1
| comp1.VerifyDiagnostics( | ||
| // (9,9): warning CS8631: The type 'TB1?' cannot be used as type parameter 'TM2' in the generic type or method 'B<TB1>.M1<TM1, TM2>(TM1, TM2)'. Nullability of type argument 'TB1?' doesn't match constraint type 'TB1'. | ||
| // M1(b2, a2); // 1 | ||
| Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "M1").WithArguments("B<TB1>.M1<TM1, TM2>(TM1, TM2)", "TB1", "TM2", "TB1?").WithLocation(9, 9), |
There was a problem hiding this comment.
Consider also verifying the original diagnostics under LangVersion=14
| } | ||
| """; | ||
|
|
||
| CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( |
There was a problem hiding this comment.
For a langversion-focused test, consider testing the same snippet under Regular14, RegularNext and RegularPreview.
There was a problem hiding this comment.
Regular15 now that that work has merged.
There was a problem hiding this comment.
This test can probably be combined with Basic_IEnumerable under the different langversions.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andy Gocke <angocke@microsoft.com>
Fix preview constraint inference in simultaneous waves while preserving ordinary argument and output inference bounds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1fcf8092-6a5f-485e-891d-26fe95919661
|
Test plan: #84868 |
Use the specification terms ordinary bounds, effective bounds, and selected fixing parameters throughout the implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1fcf8092-6a5f-485e-891d-26fe95919661
| private readonly HashSet<TypeWithAnnotations>[] _upperBounds; | ||
| private readonly HashSet<TypeWithAnnotations>[] _lowerBounds; | ||
|
|
||
| // Constraint-derived bounds are used only when the corresponding type parameter |
There was a problem hiding this comment.
Consider nullable-enabling these.
|
|
||
| if (IsFeatureTypeParameterInferenceFromConstraintsEnabled) | ||
| { | ||
| if (!FixSelectedParameters(needsFixing, ref useSiteInfo)) |
There was a problem hiding this comment.
It's unclear from this code what the actual difference is here; I think a combination of more clear method names and some comments would help clarify why the new path calls a method that appears, at first read, like it should handle both old and new paths.
| } | ||
|
|
||
| Debug.Assert(IsUnfixed(iParam)); | ||
| if (!HasEffectiveBound(iParam) || !FixParameter(iParam, ref useSiteInfo)) |
There was a problem hiding this comment.
Shouldn't this be HasOrdinaryBound, to match prior language version code?
| } | ||
| """; | ||
|
|
||
| CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( |
There was a problem hiding this comment.
Regular15 now that that work has merged.
| } | ||
| """; | ||
|
|
||
| CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( |
There was a problem hiding this comment.
This test can probably be combined with Basic_IEnumerable under the different langversions.
| """; | ||
|
|
||
| CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( | ||
| // (11,9): error CS0311: The type 'System.Collections.Generic.List<int>' cannot be used as type parameter 'TEnumerable' in the generic type or method 'C.M<TEnumerable, TElement>(TEnumerable, TElement)'. There is no implicit reference conversion from 'System.Collections.Generic.List<int>' to 'System.Collections.Generic.IEnumerable<string>'. |
There was a problem hiding this comment.
This isn't a particularly good error, and definitely not what I would have expected out of fixing. We know from ordinary bounds that TEnumerable must be List<int>; the issue is in TElement. Why would we then be erroring on TEnumerable?
…ion/MethodTypeInference.cs Co-authored-by: Fred Silberberg <fred@silberberg.xyz>
|
|
||
| // C# preview features. | ||
| case MessageID.IDS_FeatureUnsafeEvolution: // https://github.com/dotnet/roslyn/issues/82546: keep this in preview until C# 16 | ||
| case MessageID.IDS_FeatureTypeParameterInferenceFromConstraints: |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 617fedad-8306-4126-b532-e273eecf2248
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 617fedad-8306-4126-b532-e273eecf2248
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 617fedad-8306-4126-b532-e273eecf2248
333fred
left a comment
There was a problem hiding this comment.
Looks like a few comments from previous review are still unaddressed.
| the disabled-feature boundary; retain an explicit `RegularPreview` case when | ||
| testing the feature's language-version gate. |
There was a problem hiding this comment.
retain an explicit
RegularPreviewcase when testing the feature's language-version gate.
This guidance seems incorrect? To test the gate, you want to test across N and RegularNext.
Promote the generic constraints of inferred type parameters into method type inference, so that constraint-only type parameters can be inferred from the values of the type parameters they constrain (dotnet/csharplang#9453).
For example, given:
a call
M(new List<int>())now infersTElement = intfrom the constraint.The implementation extends
MethodTypeInferencein three ways:Xidepends onXjwhenXioccurs in a constraint ofXj, so the constraining parameter is fixed first. Reflexive constraint edges are excluded.The new behavior is gated behind
LanguageVersion.PreviewviaIDS_FeatureTypeParameterInferenceFromConstraints; existing language versions retain the original sequential path.The dedicated
TypeParameterInferenceFromConstraintsTestssuite covers positive, negative, ambiguity, nullability, async-lambda/Task<T>, overload-resolution, cyclic/self-referential, order-permutation, and multi-level transitive constraint scenarios. Existing nullable baselines retain ordinary-inference behavior, while the constraint-only output-inference case now succeeds under the feature.Specification update: dotnet/csharplang#10307
Microsoft Reviewers: Open in CodeFlow