dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.92k stars 4.02k forks source link

What language proposals would benefit from CLR changes? #420

Closed gafter closed 7 years ago

gafter commented 9 years ago

If we were to plan for one language feature that requires a revision of the CLR, then we might as well do as many of them at the same time as make sense. What changes would we consider for C# 7 and VB 15 that would benefit from CLR support? See #166 for related discussion. This is a pared-down list for us to select from.

  1. Virtual extension methods (see #73, #258)
  2. Generic constraints about static methods or constructors (see also #129, #154).
  3. Array slicing (see #120)
  4. Language support for tuples (see #347) and function types (no issue yet) might benefit from CLR unification across assemblies.
  5. Traits (see #60) aka structural interfaces (see #154) or mixins (no issue yet)

Those that we would likely not do in this timeframe include

  1. Make void a first-class type with one value; see #234 (Suggested by @ashmind) (Given how long the CLR has been around, it is probably too late for APIs to benefit, as those that would benefit most have already been written)
  2. Covariance and contravariance for classes (see #171)
  3. Allow |, &, and ~ operators on a type parameter with the enum constraint (see #262)
  4. Intersection types (#2146, though likely not that syntax) and/or union types.
  5. Support generic indexers (see #523)
  6. Higher-kinded polymorphism (see #2212)
sharwell commented 9 years ago

I noticed you omitted the generic indexers request. Was that intentional, or perhaps it is missing because I didn't submit a complete proposal?

ashmind commented 9 years ago

Language support for tuples might benefit from CLR unification across assemblies; see #347.

Just to add, other uses are function types and ASP.NET's [AssemblyNeutral]. Would it be useful for you if I outlined a design suggestion? And if I do, should it go here or to https://github.com/dotnet/coreclr ?

AdamSpeight2008 commented 9 years ago

A de-facto Option(Of T).

gafter commented 9 years ago

@sharwell Can you please point me to the indexers issue you're talking about?

@ashmind Language suggestions belong here. CLR suggestions belong there. I'll add function types to the list of language features that would benefit from unification.

@AdamSpeight2008 I don't know what you're suggesting that would benefit from CLR support.

mikedn commented 9 years ago

262 might also need runtime support. Enum constraints are supported in CLR but to be really useful it would be nice to also support bitwise operations for flags. & and | work but ~ sometimes requires a conversion that cannot be expressed in IL.

gafter commented 9 years ago

@mikedn Added to the list (as something we probably would not do).

sharwell commented 9 years ago

@gafter It's the first comment in #166. The motivating example is currently in one of my Stack Overflow answers, but if you feel it merits further discussion I can write the proposal separately here.

MgSam commented 9 years ago

Rather than do #161, which seems like a big change to the language for minimal benefit, I'd like to see the C# and CLR teams work together to come up with a non-garbage collected option for .NET development. This is probably outside the C# 7.0 timeframe though.

gafter commented 9 years ago

@sharwell If you could collect the most relevant bits into an issue/proposal that would be great.

orthoxerox commented 9 years ago

Structural typing and intersection/union types. Right now structural typing is de facto supported in the compiler (foreach and await), but it could be elevated into either anonymous checks or into capabilities (implicitly implemented interfaces).

Intersection types would be useful for those cases when you need to work with objects that implement more than one interface. Of course, if you had capabilities, you could use them instead.

Union types are less useful at this stage, since existing libraries don't use them.

gafter commented 9 years ago

@orthoxerox Structural typing is already number 6 on the list.

biqas commented 9 years ago

Hi,

Serialization/Deserialization to express on IL level would have huge benefits in through-puts, and interop between IL/None-IL-Languages.

There is already a project from Microsoft: https://github.com/Microsoft/bond which is dealing with it, but it is just on language level.

gafter commented 9 years ago

@biqas I don't know enough about what you're suggesting to add it to the list. Can you please create a new issue describing what you're suggesting in more detail, and link to it in this thread?

biqas commented 9 years ago

@gafter

hope it is little bit more info's Binary layout schemas and transformations on IL-Level https://github.com/dotnet/roslyn/issues/526

gafter commented 9 years ago

@biqas #526 doesn't propose anything for C# of VB. It is a CLR/IL change proposal. This is a list of language changes.

biqas commented 9 years ago

@gafter yes you are right it was not proposing anything for C#, so you can remove it from here (sorry got the topic wrong:-) ). Maybe there will be or is already a something similiar for only CLR/IL, if so it would be fit there.

grokys commented 9 years ago

Generic indexers would be incredibly useful for Perspex. We use indexers to allow binding in initialization lists which means at the moment we have to forego compile-time checking. See https://github.com/grokys/Perspex/blob/master/Docs/intro.md#attached-properties-and-binding-pt-2 for an example.

sharwell commented 9 years ago

@gafter I'd like to make a proposal regarding StackOverflowException. Where would be the most appropriate place to put that?

gafter commented 9 years ago

@sharwell I have no idea because I don't know what you have in mind. If you want to propose a library/API change then you want https://github.com/dotnet/coreclr/

qwertie commented 9 years ago

I like the concept of "CLR unification across assemblies", assuming it means structural type unification. I have written about the pain of the fact that Predicate<T> is different from Func<T,bool> and that an IList<T> is not a IReadOnlyList<T> (although the latter problem is obviously harder to solve in the runtime). There are lots of stupid thorny scenarios where it would be nice to be able to efficiently cast a value between two identical types that are not explicitly related.

The CLR was supposed to be a "common language runtime", so I think any features which allow more languages to be supported should be prioritized. To that end, I would like to see things like references that can point to managed or unmanaged memory (as required by D), dynamic interfaces via fat pointers (required by Go and Rust), efficiently growable slices (D again), covariant return types (any well-thought-out language), and union/intersection types (Ceylon, although this feature can be simulated without CLR support). Any forms of structural type equivalency might also help support new programming languages.

But among the easy-to-implement CLR changes, I like default interface implementations the most.

Tuples are already in the BCL, so why would we need to change the CLR? It would be nice to have a mutable value type version for optimization purposes though - MTuple<A,B,C>?

I already implemented slices as a value type in my Loyc.Core library - CLR support could make them more efficient, but they need not be prioritized as you can write a slice type already, and you should be able to add slices to the CLR later in a backward-compatible way. Note that read-only slices could benefit from CLR support for covariant structs.

Here's a meaningful CLR feature related to slices: allow classes and structs to contain a fixed-length array of any type (not just unmanaged types). A reference to that array would be a slice. This is sometimes useful for implementing "exotic" data structures efficiently (another useful feature for exotic data structures: define IntRef<T>, a union of an IntPtr with a managed reference, which is treated as an IntPtr when the low bit is set to 1 and a reference to T otherwise. This feature is feasible only if it can be shown not to slow down the garbage collector).

I can understand if it's hard to allow System.Void itself to be a normal value for backward compatibility reasons, but why not add a new System.@void or System.Empty value to the BCL? The CLR could improve support for this type by allowing zero-size structures. That way, for example, Dictionary<T, @void> would be no less efficient than HashSet<T>.

Here are other features I would like to see in the CLR:

P.S. It seems like some people suggesting features are unaware that the terminology they have chosen has already been used with different meaning in academic papers and existing programming languages. The so-called "templates or structural interfaces" feature is not like any template feature I'm familiar with (nor is it like what I would expect structural interfaces to mean - interfaces identified by their structure, not by their name). I don't know why traits were grouped with this. "Virtual extension methods" are an awful name. "extension method" implies that a third party can create one, but "virtual extension methods" are not proposed to have this property. They should be called default interface implementations.

jdh30 commented 9 years ago

"not do...union types"

That's a real shame. There's significant room for improvement in the representation and handling of closed union types on .NET. With C# 7 getting pattern matching the logical progression is to union types. Maybe even an option type to replace null! :-)

rojepp commented 9 years ago

Union types is the thing I miss most when I have to do C#. I realise that without the pattern matching to go with it, it is pretty much useless, like tuples. Closed union types can not be overstated.

HaloFour commented 9 years ago

@rojepp The Cω language from Microsoft Research included support for this:

choice {int; Button;} x = new Button();
choice {int; Button;} y = 1;
if (x is Button) { ... } // true
if (x is int) { ... } // false
alexpolozov commented 9 years ago

@jdh30 @rojepp In the current pattern matching proposal discriminated unions are encoded in the same way as in Scala — with inheritance and case classes.

jdh30 commented 9 years ago

@apskim: Fine by me. Any representation of unions and pattern matching in C# would make the language infinitely more usable for me as I basically don't use C# because it lacks these features.

rmschroeder commented 9 years ago

Support for higher kinds. Support for an effect system. SMT solver plug in for types -- Code contracts built in, extended. Pex functionality built in for declaring laws.

qwertie commented 9 years ago

"Closed" union types? Isn't that a synonym for Algebraic Data Types? I prefer the strictly more powerful concept of open union types, as in Ceylon. Anyway, yes, pattern matching and ADTs don't need CLR changes.

P.S. I do wonder where one is supposed to propose CLR changes. Under Roslyn doesn't seem right.

gafter commented 9 years ago

@qwertie closed union types enable the compiler to prove a set of options (cases) is complete. Having said that, the existing "records" proposal is open union types.

qwertie commented 9 years ago

@gafter I take it you aren't familiar with Ceylon's union & intersection types. If you were, you'd know that the compiler knows what cases are possible in any given situation and can check whether all cases have been handled. AFAICT, Ceylon's union type system is strictly more flexible than (closed) ADTs and doesn't sacrifice anything (except possibly type inference, which is not relevant when we're talking about CLR features, not compiler features. In the interest of full honesty, Ceylon-style union types are sort-of incompatible with Rust-style closed-union value types, but that's not really an issue since the CLR does doesn't support the latter anyway.)

dsaf commented 9 years ago

Shouldn't there be a CLR-change label? #4505

alrz commented 8 years ago

I dig "function types", isn't it under consideration, @gafter?

gafter commented 8 years ago

Nobody on the language design team is championing function types.

CyrusNajmabadi commented 8 years ago
  1. Is covariance/contravariance for methods included in this list? Is that subsumed by Covariance and contravariance for classes (see #171)?
  2. Are structural delegates (as opposed to Nominal delegates) subsumed by any of those items?
  3. Variadic generics? (i.e. so you don't need Func<>... Func<,,,,,,,,,,> etc.

If not, i'd like to see those on the list. Thanks!

Note: i'm only interested in these features if they can be backfilled over what existing stuff we have, and not if they require totally new APIs to be build. i.e. if we added variadic generics, but couldn't replace the existing Func/Action goop with it, then I don't see it actually being that useful. Same with structural delegates. I don't want to have to rewrite APIs or produce a new set of delegate types. I want a way for our existing delegates to gain structural semantics in the runtime, and to able to use that and work with that from C#.

Thanks!

gafter commented 8 years ago

@CyrusNajmabadi Covariance for method return types (#357) would use bridge methods and does not require CLR changes. There is no proposal for contravariance of methods.

There is no proposal for structural function types (which I find somewhat surprising). I gather you mean a shorthand notation for the platform types named Func and Action. I think it would be nice to be able to write int=>int as a shorthand for Func<int, int>, though there are likely to be interesting issues finding an unambiguous syntax. If that is what do, no CLR changes are required.

The two proposals possibly related to varadic generics (#5058 and #2212) aren't detailed enough to be evaluated as proposed language features. Both probably require CLR changes. I believe F# is hoping to get CLR support for higher-kinded generics.

CyrusNajmabadi commented 8 years ago

@gafter

  1. As long as we have some solution for Covariant Return Types, i'm happy. CLR support seems the cleanest. But using bridge methods is fine with me (and I guess means they'll work downlevel) :)
  2. I'm not referring to a shorthand for delegates, but to have structural delegates. i.e. I want Predicate<string> and Func<string,bool> to be the same. I don't want to have to write ConditionalWeakTable<Blah, Fooby>.CreateValueCallback when I could just say Func<Blah,Fooby>. I find it so annoying that there are nominal semantics here and that I can't interchange any of these guys.
  3. I'll let F# to the advanced work with generics. Though do you think that the CLR would make changes here with just F# asking for it? Seems like we'd want C# to possibly go along as well to make it more likely.
gafter commented 8 years ago

@CyrusNajmabadi

  1. Removing the return type from the CLR's view of a method's signature would be a breaking change. How else would we get CLR support?
  2. Making Predicate<string> and Func<string, bool> be the same type would be a breaking change.
  3. Apparently the CLR changes for higher-kinded generics aren't that large. There are many other things worth doing before that for C#.
alrz commented 8 years ago

Making Predicate<string> and Func<string, bool> be the same type would be a breaking change.

Wouldn't this possibly make function types more likely to happen? I think having a T => bool implicitly convertible to both would address the compatibility issue.

CyrusNajmabadi commented 8 years ago

I don't see why something being a breaking change matters. I'm all for breaking changes if the value is high enough. Not doing so leads to stagnation. TS, for example, was will to take on breaking changes with the latest release because the value was felt to be high enough.

  1. "How else would we get CLR support?" I'm not sure what you mean. We'd get CLR support by having them allow overriding of methods with other methods that have a covariant return type (and possibly contravariant parameter types... though I care less about this use case.)
  2. It would be a breaking change. I don't care**. I think the value is high enough to warrant it. Especially if we do the work to make it easy and seamless to move your code forward. I think there is remarkably little value in the current system, and there is high cost across all of the .Net ecosystem because of this decision. Much could be simplified and made far clearer if we did this. Regardless, this wasn't the thread of "What language proposals would benefit from CLR changes and wouldn't be breaking changes". It's "What language proposals would benefit from CLR changes. To me, the proposal of having structural delegates would benefit from CLR changes :)
  3. "There are many other things worth doing before that for C#." Sure. That doesn't mean it shouldn't be listed though.

Effectively, all of our features we do at any version were simply "not worth doing" the version before. And yet we still got around to them. That's what backlogs are for :)

_: Heck, we changed how "foreach variable capture" worked. That was a breaking change, but the value was there. And, indeed, I would say that we reallllllly_ should have made "comparing value to null is an error" even though it was a breaking change.

Breaking changes are a consideration with language changes. But they are not, in and of themselves, a blocker of them.

HaloFour commented 8 years ago

Would be nice if the CLR could at least make it very inexpensive to convert between structurally-equivalent delegate types rather than having to incur the double-invoke penalty. Then C# could potentially permit implicit conversion between them. I think that would largely satisfy these requests.

gafter commented 8 years ago

@CyrusNajmabadi TS has never taken a binary incompatible change. That would be like changing the semantics of the existing JS language (TS's equivalent to our "CLR support") in a way that changes the behavior of existing code. Making an incompatible CLR change is unlikely to be considered beyond this discussion.

orthoxerox commented 8 years ago

@HaloFour I think the current implementation of CLR can already invoke structurally compatible delegates if you tell it to in CIL, since the function pointers are the same in the end. However, it's an unsupported feature of this specific implementation. Would be nice to obtain a guarantee that this will continue to work.

HaloFour commented 8 years ago

@orthoxerox Yes, but you'll still get an exception if you attempt to combine them with another delegate instance.

nietras commented 8 years ago

I would add "Unmanaged generic type constraint + generic pointers" to this list, as per https://github.com/dotnet/coreclr/issues/2322

qwertie commented 7 years ago

In what sense are structural delegates a breaking change at the CLR level? It seems to me like all you have to do is let the verifier succeed in doing assignments and calls where otherwise it would fail.

Here's another item for my wish list: the ceq opcode should be allowed to bitwise compare two arbitrary value types (and create an mscorlib function for invoking this opcode from C#). The fact that I can't do this decreases the efficiency of my VList's SmartSelect method and I'm sure other people must be affected by this too.

gafter commented 7 years ago

Now being tracked at https://github.com/dotnet/csharplang/issues/317