Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create overload-resolution-priority.md #627

Merged
merged 2 commits into from
Nov 15, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions proposals/overload-resolution-priority.md
Original file line number Diff line number Diff line change
@@ -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
<OverloadResolutionPriority(1)>
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 dissallowed in some locations?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: disallowed


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.