diff --git a/proposals/overload-resolution-priority.md b/proposals/overload-resolution-priority.md new file mode 100644 index 0000000..5927f06 --- /dev/null +++ b/proposals/overload-resolution-priority.md @@ -0,0 +1,125 @@ +# Overload Resolution Priority + +## Summary +[summary]: #summary + +We introduced a new attribute, `System.Runtime.CompilerServices.OverloadResolutionPriority`, that can be used by API authors to adjust the relative priority of +overloads within a single type as a means of steering API consumers to use specific APIs, even if those APIs would normally be considered ambiguous or otherwise +not be chosen by overload resolution rules. See [Overload Resolution Priority in C#](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-13.0/overload-resolution-priority.md). + +## Motivation +[motivation]: #motivation + +API authors often run into an issue of what to do with a member after it has been obsoleted. For backwards compatibility purposes, many will keep the existing member around +with `ObsoleteAttribute` set to error in perpetuity, in order to avoid breaking consumers who upgrade binaries at runtime. This particularly hits plugin systems, where the +author of a plugin does not control the environment in which the plugin runs. The creator of the environment may want to keep an older method present, but block access to it +for any newly developed code. However, `ObsoleteAttribute` by itself is not enough. The type or member is still visible in overload resolution, and may cause unwanted overload +resolution failures when there is a perfectly good alternative, but that alternative is either ambiguous with the obsoleted member, or the presence of the obsoleted member causes +overload resolution to end early without ever considering the good member. For this purpose, we want to have a way for API authors to guide overload resolution on resolving the +ambiguity, so that they can evolve their API surface areas and steer users towards performant APIs without having to compromise the user experience. + +## Detailed Design +[detailed-design]: #detailed-design + +### Overload resolution priority + +We define a new concept, ***overload_resolution_priority***, which is used during the process of overload resolution. ***overload_resolution_priority*** is a 32-bit integer +value. All methods have an ***overload_resolution_priority*** of 0 by default, and this can be changed by applying +[`OverloadResolutionPriorityAttribute`](#systemruntimecompilerservicesoverloadresolutionpriorityattribute) to a method. We update section +[Overloaded Method Resolution](https://github.com/dotnet/vblang/blob/main/spec/overload-resolution.md#overloaded-method-resolution) of the VB specification as +follows (change in **bold**): + +> 2. Next, eliminate all members from the set that are inaccessible or not applicable (Section [Applicability To Argument List](overload-resolution.md#applicability-to-argument-list)) to the argument list + +> **3. Then, the reduced set of candidate members is grouped by declaring type. +> If the member is an override, the declaring type and the ***overload_resolution_priority*** come from the least-derived declaration of that member. +> Within each group:** + +> - **A maximum ***overload_resolution_priority*** among candidates that do not utilize narrowing conversions or narrowing delegate relaxation is determined.** +> - **All members that have a lower ***overload_resolution_priority*** than the maximum found during the previous step, if any, within its declaring type group are removed.** + +> **The reduced groups are then recombined into the set of candidates.** + +> **4**. Next, if one or more arguments are `AddressOf` or lambda expressions, then calculate the *delegate relaxation levels* for each such argument as below. If the worst (lowest) delegate relaxation level in `N` is worse than the lowest delegate relaxation level in `M`, then eliminate `N` from the set. The delegate relaxation levels are as follows: + +As an example, this feature would cause the following code snippet to print "I1", rather than failing compilation due to an ambiguity: + +```vb +Public Interface I1 +End Interface +Public Interface I2 +End Interface +Public Interface I3 + Inherits I1, I2 +End Interface + +Public Class C + + Public Shared Sub M(x As I1) + System.Console.WriteLine("I1") + End Sub + Public Shared Sub M(x As I2) + System.Console.WriteLine("I2") + End Sub +End Class + +Public Class Program + Shared Sub Main() + Dim i3 As I3 = Nothing + C.M(i3) + End Sub +End Class +``` + +Negative numbers are allowed to be used, and can be used to mark a specific overload as worse than all other default overloads. + +The **overload_resolution_priority** of a member comes from the least-derived declaration of that member. **overload_resolution_priority** is not +inherited or inferred from any interface members a type member may implement, and given a member `Mx` that implements an interface member `Mi`, no +warning is issued if `Mx` and `Mi` have different **overload_resolution_priorities**. + +### `System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute` + +There is the following attribute in the BCL: + +```cs +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +public sealed class OverloadResolutionPriorityAttribute(int priority) : Attribute +{ + public int Priority => priority; +} +``` + +All methods in VB have a default ***overload_resolution_priority*** of 0, unless they are attributed with `OverloadResolutionPriorityAttribute`. If they are +attributed with that attribute, then their ***overload_resolution_priority*** is the integer value provided to the first argument of the attribute. + +### Langversion Behavior + +Overload Resolution process does not perform filtering by ***overload_resolution_priority*** in VB < 17.13. +No errors or warnings issued by the Overload Resolution process due to the fact that ***overload_resolution_priority*** +was ignored in this case. + + +## Open Questions + +### Should `OverloadResolutionPriorityAttribute` be disallowed in some locations? + +By analogy with C# we could make it an error to apply `OverloadResolutionPriorityAttribute` to the following locations: + +* Property, or event accessors +* Conversion operators +* Finalizers +* Shared constructors +* Overriding properties +* Overriding methods + +Attributes encountered on these locations in metadata effectively will have no impact in VB code. + +Reporting an error might be a breaking change because VB allowed the attribute at any location before. + +### Langversion Behavior + +Should a langversion errors be issued when `OverloadResolutionPriorityAttribute` is applied? + +Reporting the error might be a breaking change because VB allowed the attribute in the previous version.