Add notes for extensions WG 2023-04-27 (#7163)

This commit is contained in:
Julien Couvreur 2023-05-10 08:15:02 -07:00 коммит произвёл GitHub
Родитель 0376b4cc50
Коммит 03e8bba247
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 167 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,167 @@
# Extensions 2023-02-21
## Problem with lookup rules on implicit extensions
The issue is that the proposed rules would allow a member from a base implicit extension
to be found by extension member lookups in two way: via import and via inheritance.
This would result into having duplicates (same member appears twice in lookup) or conflicts
(hidden member conflicts with hiding member).
Below are a few examples.
### Various examples
```
// No definition in Derived
implicit extension Base for object
{
void M() { }
}
implicit extension Derived for object : Base
{
}
object o = ...;
o.M();
```
```
// Hiding in Derived
implicit extension Base for object
{
void M() { }
}
implicit extension Derived for object : Base
{
new void M() { }
}
object o = ...;
o.M(); // ambiguous
```
```
// Better in Base
implicit extension Base for object
{
void M(int i) { }
}
implicit extension Derived for object : Base
{
void M(double d) { }
}
object o = ...;
o.M(1); // Which does it pick?
```
Beyond those invocation scenarios, there are also non-invocation scenarios:
```
// Non invocation
implicit extension Base for object
{
int Prop => 0;
}
implicit extension Derived for object : Base
{
}
object o = ...;
o.Prop; // find it twice, but it's only Base.Prop, so okay
```
```
// Non invocation, shadowing
implicit extension Base for object
{
int Prop => 0;
}
implicit extension Derived for object : Base
{
new long Prop => 0; // warning so you put "new"
}
object o = ...;
o.Prop; // ambiguity (this is an argument for pruning)
derived.Prop; // would find Derived.Prop
```
### Options
We considered a few different ways of handling this:
First, we thought of relying on overload resolution to prefer more derived members. But this doesn't work for non-invocation scenarios.
Then we considered removing/pruning base extensions from the set of candidates during extension member lookup.
This means a member from a base extension is only visible via inheritance, but not via import when it
is hidden.
But then we realized that existing lookup rules have to solve this already, as they are able to deal with
interface members in multi-inheritance scenarios, or in scenario with a type parameter with two interface constraints.
Here's an example to illustrate the current behavior:
```
I2 i2 = default!;
var r2 = i2.Prop; // Finds I2.Prop, infers long
I4 i4 = default!;
var r4 = i4.Prop; // Finds I2.Prop, infers long
public interface I1 { public int Prop { get; } }
public interface I2 : I1 { new public long Prop { get; } }
public interface I3 : I1 { }
public interface I4 : I2, I3 { }
```
We tracked down the relevant rule from member lookup:
"Next, members that are hidden by other members are removed from the set."
Conclusion: We'll incorporate a similar rule in extension member lookup.
## Unification of base extensions?
For interfaces, we check that directly implemented interfaces cannot unify.
For example:
```
interface I<T> { }
class C<U> : I<int>, I<U> // error
{
void I<int>.M() { }
void I<U>.M() { }
}
C<int>.M() // allowing such unifying scenario would cause a problem here (because we would have two V-tables to merge)
```
Do we similarly need to check for potential unification of base extensions?
The scenario would be:
```
explicit extension E<T> for object { }
explicit extension E2<U> for object : E<int>, E<U>
{
void M() { }
}
```
We couldn't think of a problem with allowing this at the moment. Lookup on `E2<int>` would work.
But if we ended up having some special support for extension types in the runtime, we could have a problem.
Conclusion: Let's move ahead to unblock basic scenario and revisit
## Mixing implicit/explicit in a hierarchy
Do we need some check on implicit/explicit consistency in an extension hierarchy?
For example:
```
implicit extension Base for object { }
explicit extension Derived for object : Base { }
```
```
explicit extension Base for object { }
implicit extension Derived for object : Base { } // why? this may be scoped in a namespace
```
Conclusion: Some of the mixing scenarios seem questionable, but adding a restriction
would not give much benefit/protection. So we'll allow any mix of implicit/explicit-ness.