Add design notes

This commit is contained in:
Mads Torgersen 2017-06-01 16:50:17 -07:00
parent efe200cf04
commit de225ff217
2 changed files with 28 additions and 29 deletions

View file

@ -1,47 +1,42 @@
# C# Language Design Notes for Apr 11, 2017
## Agenda
***Raw notes, yet to be cleaned up - read at your own peril***
1. Runtime behavior of ambiguous default implementation
# Runtime behavior of ambiguous default implementation
#406 issues
The feature is intended to work when a new default-implemented member is added to an interface `I`, even when an implementing class `C` is not recompiled. So the runtime needs to know about default implementations and be able to find them.
## Binary compat 1
In the case of overrides, there may be diamond-hierarchy cases where the compiler knows of only one override, but one is added later to another interface. The implementations are now ambiguous, and a recompilation would cause an ambiguity, but it would seem desirable that the runtime should choose "the one the compiler new about"; that, somehow, that knowledge would be baked in to the compiled class `C`.
The essence is: Does the compiler encode where it gets its implementation from? The answer is no: the runtime gets to look it up. This is similar to classes.
Starting out with these type declarations in separate assemblies:
## Binary compat 2
``` c#
interface I1 { void M() { Impl1 } }
interface I2 : I1 { override void M() { Impl2 } }
interface I3 : I1 { }
class C : I2, I3 { }
```
This is in some way a counterexample, because it *would* be resolved if the compilation of C had encoded its choice.
Everyone's happy, and `C`'s implementation of `M` would unambiguously come from `I2`.
Stake in the ground: fine to fail at compile time, but not at runtime!
Now `I3` is modified and recompiled:
You can't always bake in, because the class may be compiled before the default-implemented member was even added to the interface.
``` c#
interface I3 : I1 { override void M() { Impl3 } }
```
What should happen at runtime? When `C` was compiled, it "thought" everything was alright. At runtime it isn't. Can the compilation of `C` bake in a preference based on its understanding of the world at the time of compilation? Should it?
Choices:
It is not obvious how this would work. What if the default implementation `C` depends on is moved, deleted or overridden? Should it just be a "vague" preference in case of ambiguity, to get the runtime on the right track?
1. Error
2. Deterministically pick one of the most specific ones
3. Choice baked into class (can't always work)
1. and if fails, then 1
2. and if fails, then 2
This seems complicated, fragile and fraught with peril, but ending up with an ambiguity at runtime is also bad.
This example is represented by `IEnumerable<T>`, `IReadOnlyList<T>` and `IList<T>` in the BCL today.
Regardless, there will always be runtime ambiguities; "baking in" preferences would only address a subset. Two open questions:
Either way, there remains a choice between 1 and 2. Is it better to fail fast, or have arbitrary behavior?
1. Should we try to help resolve ambiguities by baking in compile time preferences? Unresolved.
2. Should we fail or pick an "arbitrary" implementation in case of inevitable ambiguities at runtime? Unresolved. Bad to error. Bad to run "arbitrary" code.
We can imagine situations where it's quite unfortunate that other code than "expected" gets run.
Further work:
- Understand what Java *actually* does, versus specs, and what fallout there has been
- Understand where these examples apply to our BCL, for example
Poll:
Runtime pick: 11
Error: 11
Burn in then pick: 111
Bake in and fail: 11
Not very decisive!
We should look more deeply into what Java does here. There must be accumulated insight already on this topic.

View file

@ -145,3 +145,7 @@ Design some remaining 7.1 features
2. Inferred tuple element names
3. Tuple element names in generic constraints
## Apr 11, 2017
[C# Language Design Notes for Apr 11, 2017](LDM-2017-04-11.md)
1. Runtime behavior of ambiguous default implementation