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
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
…ion/MethodTypeInference.cs Co-authored-by: Fred Silberberg <fred@silberberg.xyz>
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.
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
|
This is overall looking good. I do need to take another pass through the tests, but @jjonescz it's ready for another full look from you. |
| { | ||
| _constraintExactBounds = new HashSet<TypeWithAnnotations>[methodTypeParameters.Length]; | ||
| _constraintUpperBounds = new HashSet<TypeWithAnnotations>[methodTypeParameters.Length]; | ||
| _constraintLowerBounds = new HashSet<TypeWithAnnotations>[methodTypeParameters.Length]; |
There was a problem hiding this comment.
Should we allocate these lazily only when needed?
| // type parameters mentioned in Xj's constraints, by ensuring Xj is fixed first. | ||
| if (IsFeatureTypeParameterInferenceFromConstraintsEnabled && | ||
| iParam != jParam && | ||
| ConstraintContainsTypeParameter(_methodTypeParameters[jParam], _methodTypeParameters[iParam])) |
There was a problem hiding this comment.
It looks like we might be adding dependencies based on constraints even for parameters that already had ordinary bounds. That can break for example the following case which previously compiled fine and would now result in an error:
using System;
using System.Collections.Generic;
class Program
{
static void M<T, U>(U u, T t, Func<T, U> f)
where U : IEnumerable<T>
{
}
static void Main()
{
M(new List<int>(), 1, x => (IEnumerable<int>)null);
}
}Can we avoid this break, or is it intentional?
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