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

New Tie-Breaker Rule for Resolving Ambiguity Between Overloads Which Differ By Refness of Arguments #170

Open
AnthonyDGreen opened this issue Oct 1, 2017 · 3 comments

Comments

@AnthonyDGreen
Copy link
Contributor

AnthonyDGreen commented Oct 1, 2017

Scenario

Unlike C#, VB doesn't use a keyword at the call-site to denote whether an argument is being passed ByRef or not. That's pretty reasonable given that ByRef was the default in VB6 and even C# drops the requirement to use ref when inter-operating with COM.

All that said, despite not being CLS-compliant from time to time one sees an API which includes overloads which differ only by the refness of one or more arguments. I'm not sure why they do this; perhaps it's a versioning thing--an initial version is published with a ByVal argument and later a ByRef one is added for performance.

This puts VB users in a bad spot if they encounter one of these bad-actors because VB can't distinguish these overloads at the call-site. I've run into it myself when using the Managed DirectX library and have seen at least one customer report about it.

Note: This proposal does NOT include allowing Visual Basic to define such overloads.

Proposal

Given two methods M and N of equal specificity, and a pair of parameters Mj and Nj that match argument Aj,

  • If Aj is classified as a variable or property access and Mj is a reference (ByRef) parameter and Nj is not, Mj is more specific than Nj.
  • If Aj is classified as a value and Mj is a value (ByVal) parameter and Nj is not, Mj is more specific than Nj.
  • If at least one Mj is more specific Nj and no Nj is more specific than Mj, M is more specific than N.

Note: This does not take into account whether the variable or property being accessed is read-only.

More plainly, prefer ByRef parameters when passing "r-values" (things that can be assigned to) and ByVal parameters when passing l-values (things which aren't storage locations).

This means given these overloads:

Sub M(ByVal p As Integer)

Sub M(ByRef p As Integer)

A user can force the compiler to pick the first overload by forcing the argument to be classified as a value (e.g. parenthesizing the expression).
A user can force the compiler to pick the second overload by manually copying the value into a local variable (which is what the compiler would have done anyway).
And without user intervention the compiler will pick the overload whose argument passing convention is most appropriate for the class of expression being passed.

Alternatives

Add a ByRef modifier at the call-site. This has been requested before but was rejected due to concerns that some code would use it and some wouldn't and without strict enforcement (breaking change) it would cause more confusion than good as readers would erroneously assume code without the modifier wasn't passing ByRef.

@AdamSpeight2008
Copy link
Contributor

What about allowing ByRef or ByVal at the call-site.
In the grammar would should split out ByVal | ByRef into their own definition.

ParameterModifier
    : Parameter_Refness | 'Optional' | 'ParamArray'
    ;

Parameter_Refness
  : 'ByVal' | `ByRef`
  ;

PositionalArgumentList
    : Parameter_Refness? Expression? ( Comma Expression? )*
    ;

When the parameter argument doesn't specify a Parameter_Refness it is asserted to be ByVal.

Enforcement can be provide via an Option ByRefOverload On

@AnthonyDGreen
Copy link
Contributor Author

That's a whole lot of machinery for an edge case. We shouldn't rush into burdening the entire world with new syntax just to unblock a handful of customers inter-operating with poorly designed libraries.

@AnthonyDGreen
Copy link
Contributor Author

Update: The 12/6/2017 LDM approved this idea in principle. We decided against any new syntax for ByRef but think we're going to need this tie-breaker anyway as new libraries written in C# take advantage of the in (formally ref readonly) feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants