Merge from dotnet/csharplang

Merge from dotnet/csharplang
This commit is contained in:
AlekseyTs 2020-03-20 13:03:33 -07:00 committed by GitHub
commit d6718accc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1393 additions and 151 deletions

View file

@ -1,111 +1,6 @@
Features Added in C# Language Versions
====================
# [C# 1.0](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#.NET_.282002.29) - Visual Studio .NET 2002
- Classes
- Structs
- Interfaces
- Events
- Properties
- Delegates
- Expressions
- Statements
- Attributes
- Literals
# [C# 1.2](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12) - Visual Studio .NET 2003
- Dispose in foreach
- foreach over string specialization
# [C# 2](https://msdn.microsoft.com/en-us/library/7cz8t42e(v=vs.80).aspx) - Visual Studio 2005
- Generics
- Partial types
- Anonymous methods
- Iterators
- Nullable types
- Getter/setter separate accessibility
- Method group conversions (delegates)
- Static classes
- Delegate inference
# [C# 3](https://msdn.microsoft.com/en-us/library/bb308966.aspx) - Visual Studio 2008
- Implicitly typed local variables
- Object and collection initializers
- Auto-Implemented properties
- Anonymous types
- Extension methods
- Query expressions
- Lambda expression
- Expression trees
- Partial methods
# [C# 4](https://msdn.microsoft.com/en-us/magazine/ff796223.aspx) - Visual Studio 2010
- Dynamic binding
- Named and optional arguments
- Co- and Contra-variance for generic delegates and interfaces
- Embedded interop types ("NoPIA")
# [C# 5](https://blogs.msdn.microsoft.com/mvpawardprogram/2012/03/26/an-introduction-to-new-features-in-c-5-0/) - Visual Studio 2012
- Asynchronous methods
- Caller info attributes
- foreach loop was changed to generates a new loop variable rather than closing over the same variable every time
# [C# 6](https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6) - Visual Studio 2015
- [Draft Specification online](https://github.com/dotnet/csharplang/blob/master/spec/README.md)
- Compiler-as-a-service (Roslyn)
- Import of static type members into namespace
- Exception filters
- Await in catch/finally blocks
- Auto property initializers
- Default values for getter-only properties
- Expression-bodied members
- Null propagator (null-conditional operator, succinct null checking)
- String interpolation
- nameof operator
- Dictionary initializer
# [C# 7.0](https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/) - Visual Studio 2017
- [Out variables](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/out-var.md)
- [Pattern matching](https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md)
- [Tuples](https://github.com/dotnet/roslyn/blob/master/docs/features/tuples.md)
- [Deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md)
- [Discards](https://github.com/dotnet/roslyn/blob/master/docs/features/discards.md)
- [Local Functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/local-functions.md)
- [Binary Literals](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/binary-literals.md)
- [Digit Separators](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/digit-separators.md)
- Ref returns and locals
- [Generalized async return types](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)
- More expression-bodied members
- [Throw expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md)
# [C# 7.1](https://blogs.msdn.microsoft.com/dotnet/2017/10/31/welcome-to-c-7-1/) - Visual Studio 2017 version 15.3
- [Async main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md)
- [Default expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md)
- [Reference assemblies](https://github.com/dotnet/roslyn/blob/master/docs/features/refout.md)
- [Inferred tuple element names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md)
- [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md)
# [C# 7.2](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) - Visual Studio 2017 version 15.5
- [Span and ref-like types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md)
- [In parameters and readonly references](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md)
- [Ref conditional](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md)
- [Non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md)
- [Private protected accessibility](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md)
- [Digit separator after base specifier](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md)
# C# 7.3 - Visual Studio 2017 version 15.7
- `System.Enum`, `System.Delegate` and [`unmanaged`](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/blittable.md) constraints.
- [Ref local re-assignment](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/ref-local-reassignment.md): Ref locals and ref parameters can now be reassigned with the ref assignment operator (`= ref`).
- [Stackalloc initializers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/stackalloc-array-initializers.md): Stack-allocated arrays can now be initialized, e.g. `Span<int> x = stackalloc[] { 1, 2, 3 };`.
- [Indexing movable fixed buffers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/indexing-movable-fixed-fields.md): Fixed buffers can be indexed into without first being pinned.
- [Custom `fixed` statement](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md): Types that implement a suitable `GetPinnableReference` can be used in a `fixed` statement.
- [Improved overload candidates](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/improved-overload-candidates.md): Some overload resolution candidates can be ruled out early, thus reducing ambiguities.
- [Expression variables in initializers and queries](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md): Expression variables like `out var` and pattern variables are allowed in field initializers, constructor initializers and LINQ queries.
- [Tuple comparison](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/tuple-equality.md): Tuples can now be compared with `==` and `!=`.
- [Attributes on backing fields](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/auto-prop-field-attrs.md): Allows `[field: …]` attributes on an auto-implemented property to target its backing field.
# C# 8.0 - .NET Core 3.0 and Visual Studio 2019 version 16.3
- [Nullable reference types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/nullable-reference-types-specification.md): express nullability intent on reference types with `?`, `notnull` constraint and annotations attributes in APIs, the compiler will use those to try and detect possible `null` values being dereferenced or passed to unsuitable APIs.
- [Default interface members](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/default-interface-methods.md): interfaces can now have members with default implementations, as well as static/private/protected/internal members except for state (ie. no fields).
@ -121,3 +16,106 @@ Features Added in C# Language Versions
- [Alternative interpolated verbatim strings](https://github.com/dotnet/csharplang/issues/1630): `@$"..."` strings are recognized as interpolated verbatim strings just like `$@"..."`.
- [Obsolete on property accessors](https://github.com/dotnet/csharplang/issues/2152): property accessors can now be individually marked as obsolete.
- [Permit `t is null` on unconstrained type parameter](https://github.com/dotnet/csharplang/issues/1284)
# C# 7.3 - Visual Studio 2017 version 15.7
- `System.Enum`, `System.Delegate` and [`unmanaged`](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/blittable.md) constraints.
- [Ref local re-assignment](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/ref-local-reassignment.md): Ref locals and ref parameters can now be reassigned with the ref assignment operator (`= ref`).
- [Stackalloc initializers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/stackalloc-array-initializers.md): Stack-allocated arrays can now be initialized, e.g. `Span<int> x = stackalloc[] { 1, 2, 3 };`.
- [Indexing movable fixed buffers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/indexing-movable-fixed-fields.md): Fixed buffers can be indexed into without first being pinned.
- [Custom `fixed` statement](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md): Types that implement a suitable `GetPinnableReference` can be used in a `fixed` statement.
- [Improved overload candidates](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/improved-overload-candidates.md): Some overload resolution candidates can be ruled out early, thus reducing ambiguities.
- [Expression variables in initializers and queries](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md): Expression variables like `out var` and pattern variables are allowed in field initializers, constructor initializers and LINQ queries.
- [Tuple comparison](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/tuple-equality.md): Tuples can now be compared with `==` and `!=`.
- [Attributes on backing fields](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/auto-prop-field-attrs.md): Allows `[field: …]` attributes on an auto-implemented property to target its backing field.
# [C# 7.2](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) - Visual Studio 2017 version 15.5
- [Span and ref-like types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md)
- [In parameters and readonly references](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md)
- [Ref conditional](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md)
- [Non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md)
- [Private protected accessibility](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md)
- [Digit separator after base specifier](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md)
# [C# 7.1](https://blogs.msdn.microsoft.com/dotnet/2017/10/31/welcome-to-c-7-1/) - Visual Studio 2017 version 15.3
- [Async main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md)
- [Default expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md)
- [Reference assemblies](https://github.com/dotnet/roslyn/blob/master/docs/features/refout.md)
- [Inferred tuple element names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md)
- [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md)
# [C# 7.0](https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/) - Visual Studio 2017
- [Out variables](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/out-var.md)
- [Pattern matching](https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md)
- [Tuples](https://github.com/dotnet/roslyn/blob/master/docs/features/tuples.md)
- [Deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md)
- [Discards](https://github.com/dotnet/roslyn/blob/master/docs/features/discards.md)
- [Local Functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/local-functions.md)
- [Binary Literals](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/binary-literals.md)
- [Digit Separators](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/digit-separators.md)
- Ref returns and locals
- [Generalized async return types](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)
- More expression-bodied members
- [Throw expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md)
# [C# 6](https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6) - Visual Studio 2015
- [Draft Specification online](https://github.com/dotnet/csharplang/blob/master/spec/README.md)
- Compiler-as-a-service (Roslyn)
- Import of static type members into namespace
- Exception filters
- Await in catch/finally blocks
- Auto property initializers
- Default values for getter-only properties
- Expression-bodied members
- Null propagator (null-conditional operator, succinct null checking)
- String interpolation
- nameof operator
- Dictionary initializer
# [C# 5](https://blogs.msdn.microsoft.com/mvpawardprogram/2012/03/26/an-introduction-to-new-features-in-c-5-0/) - Visual Studio 2012
- Asynchronous methods
- Caller info attributes
- foreach loop was changed to generates a new loop variable rather than closing over the same variable every time
# [C# 4](https://msdn.microsoft.com/en-us/magazine/ff796223.aspx) - Visual Studio 2010
- Dynamic binding
- Named and optional arguments
- Co- and Contra-variance for generic delegates and interfaces
- Embedded interop types ("NoPIA")
# [C# 3](https://msdn.microsoft.com/en-us/library/bb308966.aspx) - Visual Studio 2008
- Implicitly typed local variables
- Object and collection initializers
- Auto-Implemented properties
- Anonymous types
- Extension methods
- Query expressions
- Lambda expression
- Expression trees
- Partial methods
# [C# 2](https://msdn.microsoft.com/en-us/library/7cz8t42e(v=vs.80).aspx) - Visual Studio 2005
- Generics
- Partial types
- Anonymous methods
- Iterators
- Nullable types
- Getter/setter separate accessibility
- Method group conversions (delegates)
- Static classes
- Delegate inference
# [C# 1.2](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12) - Visual Studio .NET 2003
- Dispose in foreach
- foreach over string specialization
# [C# 1.0](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#.NET_.282002.29) - Visual Studio .NET 2002
- Classes
- Structs
- Interfaces
- Events
- Properties
- Delegates
- Expressions
- Statements
- Attributes
- Literals

View file

@ -0,0 +1,159 @@
# C# Language Design Meeting for Jan. 15th, 2020
## Agenda
1. Working with data
2. Record feature breakdown
## Discussion
### Working with data
As we discuss records, we want to go over a design document we produced a number of years ago
called "working with data." This document lays out how, when we design features, we inherently
express a "path of least resistance," which consists of the features that seem easiest or
shortest to use to accomplish a given problem.
Link: https://github.com/dotnet/csharplang/issues/3107
The document argues that, as we find particular patterns to be more effective at building
software, we should make the forms we find to be more effective simpler or shorter to express in
the language. We should not "change" our opinions, meaning make old syntax illegal, but we should
"level the playing field" by making other forms simpler.
The conclusion of the design document is that we should favor
1. Immutable members in records by default
1. Any features from records that we separate should not make the simple syntax longer
### Record feature breakdown
We've also been working on breaking down the individual features
of records and determining how independent they can or should be.
Notes: https://github.com/dotnet/csharplang/issues/3137
There seem to be the following somewhat separable parts of records
1. Value-based equality
2. Construction boilerplate
3. Object initializers
4. Nondestructive mutation
5. Data-friendly defaults
#### Value equality
It's been proposed that a `key` modifier could be applied to signal that value-based equality is
being generated based on the members which have it. This works in many cases,
but if the absence of the `key` modifier means inherited equality, we're not sure
that's the semantics we want. It would also not allow value-based equality to be
"specified" in the base class in some sense, enforcing value equality for the deriving
type. Whether this is valuable or blocking is an open question.
#### Construction boilerplate
Creating a constructor to assign members of a container is one of the largest sources
of repetitive boilerplate, e.g.
```C#
public class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
```
You can imagine various points on this spectrum to simplify the boilerplate,
```C#
public class Point
{
public key int X { get; }
public key int Y { get; }
public Point(X, Y); // name matching and type absence implies initialization
}
```
Which removes the duplication of naming the same elements multiple times or,
```C#
public class Point(X, Y)
{
public key int X { get; }
public key int Y { get; }
}
```
Which removes the constructor name duplication and we could go further to remove
property name duplication,
```C#
public class Point(
public key int X { get; }
public key int Y { get; }
);
```
Going all the way to the original position deconstruction
```C#
public class Point(int X, int Y);
```
Where we pick a point in this space seems to correspond to the perceived benefits
of the orthogonality of the feature. If the construction shorthand is useful for
many scenarios outside of the record scenarios, it's practical to expand it.
### Object initializers
One benefit to object initializers is that they don't refer to a constructor directly,
only to the properties. This sidesteps a weakness in C#, where constructor initialization in inheritance requires repetition. Without constructors the simple
relation
```C#
public abstract class Person
{
public string Name { get; }
}
public class Student : Person
{
public string ID { get;}
}
```
has no repetition. Each class states only the properties that are essential, and
for derived classes all the base properties are inherited without repetition.
Once you add constructors this breaks down
```C#
public abstract class Person
{
public string Name { get; }
public Person(string name)
{
Name = name;
}
}
public class Student : Person
{
public string ID { get; }
public Student(string id, string name)
: base(name)
{
Id = id;
}
}
```
Now the derived classes have to repeat everything from the base, causing brittleness
along the boundary. If we were to imagine some improvement to object initializers,
then defining a constructor would not be required.
On the other hand, this also removes one of the main benefits for having a constructor,
namely that you can validate the whole state of the object before producing it.

View file

@ -0,0 +1,108 @@
# C# Language Design Notes for Jan. 22, 2020
## Agenda
1. Top-level statements and functions
2. Expression Blocks
## Discussion
### Top-level statements and functions
https://github.com/dotnet/csharplang/issues/3117
Three main scenarios:
1. Simple programs are simple -- remove the boilerplate for Main
2. Top-level functions. Members outside of a class.
3. Scripting/interactive. Submission system allows state preservation across evaluations.
Unfortunately, some of these proposals interact in difficult ways.
If you write
```C#
int x = ...;
```
is `x` now a global mutable variable for the entire program? Or is it a
local variable in a generated Main method?
#### Proposal: Simple programs
The proposal is to prioritize (1) and (3) and remove boilerplate, while
enabling use in scripting/interactive scenarios.
To address (1), we would allow a single file in the compilation to contain top-level statements,
and any file to contain top-level local functions, which would be in scope in all files, but it
would be an error to refer to them.
There's wide consensus that (1) is very useful. There's the case of small programs, where you
really just want to write a few statements and not have to write the boilerplate of classes and
Main. It's also a very large learning burden in that just to write "Hello, World" requires
explaining methods, classes, static, etc.
(3) is also important partly because there are a number of products and scenarios
currently using the scripting system. We should keep that in mind to make sure that
we don't prevent a large number of use cases from ever using the new system.
We think (2) is interesting and worth considering. It may not be the highest priority,
but we need to make sure we don't rule it out entirely. We also think that if we add
(1) it seems likely that some people would want (2) much sooner.
If we do want to make space for (2) we should make sure to look at lookup rules very carefully.
The C# lookup rules are very complicated and including new ones for top-level members could
include subtle ways that change new code.
When we designed scripting we had experience that copying back-and-forth from interactive and the
main program is very useful and important. Because the syntax used here is similar to local
functions and it's not currently proposed that accessibility modifiers are legal, this would
create a difference when copying code between standard C# and the interactive dialect, since
presumably those declarations would now be illegal.
#### Block expressions
https://github.com/dotnet/csharplang/issues/3137
We're revisiting the earlier discussions and there is a proposal for how we could
make blocks legal as expressions. The proposed precedence would be the lowest
possible, so many ambiguities or breaking changes would be avoided.
Examples:
```C#
var x = { ; 3 }; // int x = 3
var x = { {} 3 }; // int x = 3
```
Note that a final expression is required, so the `'` or `{}` are necessary as "starting
statements".
The most notable restrictions are that you cannot branch out, meaning that `return`, `yield break`, and `yield return`, and `goto` would be illegal in this proposal.
Something which was brought up before is whether to use a "trailing expression" to
produce a value, or introduce some sort of statement to produce the evaluation
expression. If we used a `break e;` syntax, the above could look like
```C#
var x = { break 3; };
```
One problem is turning the block into a lambda, where `break` would have to be changed to
`return`. On the other hand, if this code were introduced directly into the method, `return`
would actually produce different, valid semantics. `break e;` would be an error in both contexts,
instead of producing different code.
The precedence doesn't have agreement. Some people think that the precedence is
still too high and that we should almost always require a parenthesized wrapper
expression, except in specific cases where we think it's clear. Other people think
that this is too low and they want to use them in more places.
**Conclusion**
We don't think we have enough information about the restrictions we're working under. One way to
make progress would be to construct a list of the potential ambiguities in using the `{}` as an
expression term.

View file

@ -0,0 +1,106 @@
# C# Language Design for Jan. 29, 2020
## Agenda
Record -- With'ers
## Discussion
In this meeting we'd like to talk about Mads's write-up of the "With-ers" feature, as it relates
to records. Multiple variations have been proposed, but the suggestion generally takes the form
of a `with` expression that can return a copy of a data type, with selective elements changed.
Write-up: https://github.com/dotnet/csharplang/issues/3137
The first thing we learned is that the fundamental problem we're trying to solve is
"non-destructive mutation."
There are two approaches we've thought of: direct copy and then direct modification, and creation of a new type based on the values of the old type.
1. Direct copy. We might call this "copy-and-update" because we copy the new data type exactly,
then update the new type with required changes. The basic implementation would be to use
MemberwiseClone, and then overwrite select properties.
2. Create a new type. we call this "constructing through virtual factories." If the type supports
a constructor, this approach would call the constructor using the new values, or the existing
ones if nothing new is given. The construction would be virtual so that derived types would not
lose state when called through the base type.
There are advantages and disadvantages to each proposal.
(1) is simple but seemingly dangerous. There are often internal constraints to a type which must
be preserved for correctness. Usually this is enforced through the type constructor and
visibility of modification. That would not necessarily be available here.
(2) does construction similar to conventional construction today, so it doesn't introduce as many
safety concerns. On the other hand, the contract looks a lot more complicated. To make the
feature seem simple on the surface, it looks like we imply a lot of implicit dependency. For
example,
```C#
public data class Point(int X, int Y);
var p2 = p1 with { Y = 2 };
```
Would generate
```C#
public class Point(int X, int Y)
{
public virtual Point With(int X, int Y) => new Point(X, Y);
}
var p2 = p1.With(p1.X, 2);
```
The first requirement is that an auto-generated `With` method must have a primary constructor, in
order to know which constructor to call. Alternatively, we could have a `With` method generated
for every constructor, although that would require a syntax to signal that `With` methods should
be generated in the absence of a primary constructor.
The compiler also needs to know that the `X` and `Y` parameters of the `With` method correspond
to particular properties, so it can fill in the defaults in the `with` expression. Otherwise we
would need some way of signifying which of the parameters are meant to be "defaults":
```C#
public class Point(int X, int Y)
{
public virtual Point With(bool[] provided, int X, int Y)
{
return new Point(provided[0] ? X : this.X, provided[1] ? Y : this.Y);
}
}
var p2 = p1.With(new bool { false, true}, default, 2);
```
We also need to figure out which `With` method to call at a particular call site. One way is to
construct an equivalent call and perform overload resolution. Another way would be to pick a
particular `With` method as primary, and always use that one in overload resolution.
This also has some of the same compatibility challenges that we've seen in other areas.
Particularly, if you add members to the record, there will be a new `With` method with a new
signature. This would break existing binaries referencing the old `With` method. In addition, if
you add a new `With` method, the old one would still be chosen by overload resolution, if
overload resolution is performed, as long as unspecified properties in the `with` expression are
default values.
On the other hand, this is also a general problem with backwards compatibility overloads. We'll
need to investigate whether we want to add a general purpose mechanism for handling backwards
compatibility and if we want to introduce a special case for With-ers specifically.
What all of the above interdependency implies is that we need a significant amount of syntax or
"hints" about what to do during autogeneration. We previously expressed interest in providing
orthogonality for as many of the "record" features as possible. A conclusion is that
auto-generated With-ers require or suggest many of syntactic and semantic components of records
themselves. When we try to separate the feature entirely, we require user opt-in to specify the
"backing" state of the With-er. This seems to imply that auto-generation should
not be a general, orthogonal feature, but a specific property of records.
However, we don't have to give up orthogonality entirely. The requirements for auto-generated
With-ers doesn't imply anything about manually written With-ers. Auto-generation seems possible
in records because the syntax ties the state to the public interface. Manual specification looks
just like the components of records that can be written explicitly in regular classes, like
constructors themselves. If we do want to pursue this avenue, we should try to limit
the complexity of the pattern as much as possible. It's not too bad if it's fully
generated by the compiler, but it can't be very complicated if we want users to write
it themselves.

View file

@ -0,0 +1,61 @@
# C# LDM for Feb. 3, 2020
## Agenda
Value equality
## Discussion
We split our discussion between two proposals, which end up being very
similar.
### 'key' equality proposal
https://github.com/dotnet/csharplang/pull/3127
Q: Is comparing `System.Type`s slow? Does it require reflection?
A: `typeof` does not require reflection and comparing `Type`s is fast.
Q: Why use a KeyEquals method?
A: To signify that value equals is used and delegate to the base equality,
when the base opts-in to value equality. By having a well-known signature
in metadata, no special attributes are required for derived types to discover
the pattern.
Q: Is KeyEquals necessary? Can we use `EqualityContractOrigin` to figure
out that the type implements value equality?
A: Yes, that seems like it should work.
*Discussion*
There's some concern that modifying the public surface area of the type
itself without any type of modifier is too much magic. If we have some
sort of modifier that goes on the type, in addition to the "key" members,
it would be clear that the type implements value equality from the type
declaration, in addition to the member declarations.
This dovetails into records as a whole in that it would allow the feature sets to be separable.
If a type could have value equality or be a record, the features could be combined to produce a
value record, or the value equality could be left off to allow a record with reference equality.
There's some disagreement on whether this is a positive or a negative. If you view a record as
appropriately having value equality, this is a negative, or vice versa.
### 'value' equality proposal
https://github.com/dotnet/csharplang/issues/3137
The most visible difference here is that `value` is the name of the modifier, instead of `key`.
This more accurately reflects the term "value equality", but it's unfortunate that we already
have the term "value type" which has a completely different meaning in the language.
At the moment the proposal also doesn't include the "extra" members, like a strongly
typed Equals, the `==`/`!=` operators, and `IEquatable` interface implementation.
There's an open question as to whether this feature is preferred for a discriminated
union scenario or not. We have two examples in Roslyn of discriminated unions, our
symbol tree and our bound tree, and they have almost completely different equality
contracts.

View file

@ -0,0 +1,133 @@
# C# LDM for Feb. 5, 2020
## Agenda
1. Dependent nullability attribute
2. Null checking in unconstrained generics
## Discussion
### Nullability
Dependent calls:
We'd like to be able to support patterns like:
```C#
if (x.HasValue)
{
x.Value // should not warn when HasValue is true
}
```
We would add attributes to support these use cases: `EnsuresNotNull` and `EnsuresNotNullWhen` (to parallel the existing `NotNull` attributes). The
proposal as stands is to name the fields or properties and that would be
the inputs and outputs for the flow analysis. We propose that the lookup
rules for resolving the names in these instances would be similar to the
rules for the `DefaultMemberAttribute`.
This could also be used for helpers in constructor initialization, where today constructors which
only call `base` are required to initialize all members, even if a helper initializes some of the
members.
There's a follow-up question: should you be able to specify that members of the parameter are
not-null after the call? For example,
```C#
class Point
{
public object? X;
}
static void M([EnsuresNotNull(nameof(Point.X))]Point p) { ... }
```
We could also allow it for nested annotation
```C#
class Point
{
public object? X;
}
class Line
{
public Point P1;
public Point P2;
}
static void M([EnsuresNotNull("P1.X", "P1.Y")]Line l) { ... }
static bool M([EnsuresNotNullWhen(true, "P1.X", "P1.Y")]Line l) { ... }
```
The nested names could also be used for return annotations.
However, we're not sure this is worth it. We see the usefulness in theory,
but we're not sure how often it would actually be used. If we want to leave
the space for later, we could produce an error when writing an attribute with
an unsupported string form.
Similarly, if we don't want to support referring to members of types through
the parameters, as in the first example, we can also provide an error for these
scenarios. Or, we could say that the initial proposal is qualitatively different
from all these scenarios:
```C#
[MemberNotNull(nameof(X))]
void M() { }
```
For the situations in parameters and return types we are referring to the target the attribute is
being applied to, while the original proposal is returning to the containing type, somewhat
unrelated to the location of the attribute.
We're also not sure exactly what the name or shape of these attributes would
look like. We think this could be valuable, but we'd like to decide with
the full context of other attributes we're considering.
**Conclusion**
We see the usefulness of the original scenario, but the broadening we're not sure
on the return on investment. Let's support the original scenario through a new
attribute, `MemberNotNull`, that only has members as valid attribute targets to start.
If we find users still hit significant limitations without the parameter and return
type support, we can consider broadening in a future release.
### Pure non-null check in generic
```C#
public T Id<T>(T t)
{
if (t is null) Console.WriteLine();
return t; // warn?
}
```
The question here is whether we should consider `T` to move to `MaybeDefault` after
checking for `null`. We never do this today.
The scenario where a "pure" null check would come into play is:
```C#
public T Id<T>(T t) where T : notnull
{
if (t is null) Console.WriteLine();
return t;
}
```
It appears that this does not warn today, which looks like a bug. The analogous
scenario for `T??` is
```C#
public T ID<T>(T t)
{
if (t is default) Console.WriteLine();
return t;
}
```
However, this is illegal as there is no way to check if `T` is `default(T)`.
**Conclusion**
The original scenario should not warn.

View file

@ -0,0 +1,48 @@
# C# Language Design for Feb. 10, 2020
# Agenda
Records
# Discussion
We're continuing our attempt to draw out the dependencies and individual features inside records.
When going through the list, what stands out is:
- Looking at `with`, we need to figure out what's mentionable in the `with` expression.
- We need to figure out exactly what we want for how primary constructors fit into records
There are a number of positives and negatives of primary constructors. On the negative side,
a non-record primary constructor seems to consume syntactic space that could be used for
records. If we think that records are the overwhelmingly common scenario, then it seems like
using the shortest syntax for the most common feature is useful. On the positive side, primary
constructors alone seem to support a simpler way of writing private implementation details.
Separate from the value as a whole, there's some desire to have a special keyword just for
records. That is, even if we didn't do primary constructors, it could be valuable to have
an explicit modifier, like `data` to signify that this type has special behavior.
One possible pivot is to eliminate some composition syntax entirely, by creating a new type
of declaration, `record`, e.g.
```C#
record Point(int X, int Y);
```
This would be equivalent to the syntax that we've been discussing with `data`, namely
```C#
data class Point(int X, int Y);
```
but since the `class` keyword is implied by default, the most common scenario would be
just about as short as the shorter `class Point(int X, int Y)` form.
**Conclusion**
After taking everything into account, we think having an new keyword for records is good both for
leaving space for non-record primary constructors, and also to serve as a clear signifier of
record semantics.

View file

@ -0,0 +1,116 @@
# C# Language Design for Feb 12, 2020
## Agenda
Records
## Discussion
### Value equality
Proposal: use the `key` keyword previously mentioned, but also
require it on the type declaration as well, e.g.
```C#
key class HasValueEquality
{
public key int X { get; }
}
```
There are a number of things we could pivot on
```C#
key class HasValueEquality1 { public key int X { get; } }
class HasValueEquality2 { public key int X { get; } }
key class HasValueEquality3 { public key X { get; } }
class HasValueEquality4 : IEquatable<HasValueEquality4> { public int X { get; } }
```
----
```C#
record Point1(int X); // Implies value equality over X
record Point2a(int X); // Implies inherited equality
key record Point2b1(int X); // Implies value equality over X
key record Point2b2a(int X); // Implies "empty" value equality
key record Point2b2b(key int X); // Implies value equality over X
key class Point3a(int X); // implies record + value equality over X
data class Point3b(int X); // implies record with inherited equality
```
#### Equality default
We originally considered adding value equality on records both because it's difficult to
implement yourself and it fits the semantics we built for records in general. We want to validate
that these things are still true, and new considerations, namely whether it is the appropriate
default for records and whether it should be available to other types, like regular classes.
We left off in the previous discussion asking whether value equality is not just
an inconvenient default, but actively harmful for key scenarios for records. Some examples
we came up with are either classes with large numbers of members, where value equality may
be unnecessary and slow, and circular graphs, where using value equality could cause
infinite recursion.
These do seem bad, but it's not obvious that these scenarios either fit perfectly with the
canonical record, or if the consequences are necessarily worse than default reference equality.
Certainly producing infinite recursion in object graphs is bad, but silently incorrect behavior
due to inaccurate reference equality is also harmful, in the same sense. It's also easier
to switch from value equality to reference equality than it is to switch from reference equality
to value equality, due to the complex requirements in a value equality contract.
**Conclusion**
Value equality seems a reasonable default, as long as they are immutable by default, and that
there is a reasonable way to opt-in to a different equality.
#### Separable value equality
Given that we like value equality as a default, we have to decide if we want a separable equality
feature as well. This is important for the scenario:
```C#
record Point1(int X)
{
public int X { get; }
}
```
if there's a separate `key` feature, we need to decide if the substituted property should
require, allow, or disallow the `key` modifier, e.g.
```C#
record Point1(int X)
{
public key int X { get; }
}
```
We also need to decide what such a "separable" equality feature would look like, and if it has a
difference between records and other classes. We could add a `key` feature for non-records, and
disallow `key` entirely in records. The members of a record equality would then not be
customizable.
The individual `key` modifiers on non-records seem deceptively complicated.
A common case is "opt-in everything". `key` modifiers wouldn't improve much on this, as they
would be necessary on every element. On the other hand, there are often computed properties that
may be seen as part of "everything", but not part of the equality inputs. The plus of record
primary constructors is that they identify the "core" inputs to the type.
Individual `key` modifiers also do not help with the large custom classes that are written today
where it's easy to forget to add new members to equality. With a `key` modifier you can still
forget to add the modifier to a new member.
These decisions play into records as a whole because they affect the uniformity of record and
non-record behavior. If records are defined by their "parameters", namely in this syntax the
primary constructor parameters and identically named properties, then no other members should
be a part of the equality. However, that would imply members in the body are not automatically
included. For regular classes, it seems backwards. Members are not generally included, they have
to be added specifically.
On the other hand, if we prioritize uniformity, general members in record bodies would be included
in equality, which would harm a view of records as consisting primarily of the "record inputs."

View file

@ -0,0 +1,82 @@
# C# Language Design for Feb. 19, 2020
## Agenda
State-based Value Equality
## Discussion
Proposal: https://github.com/dotnet/csharplang/issues/3213
* We haven't decided (yet) to add support for value equality on all
classes (separate from records)
* The behavior is actually that all fields _declared_ in the class are
members in the value equality, not all fields in the class (since inherited fields are not
included)
* Inheritance would be implemented using the previously described
proposals using the `EqualityContract` property
* Records wouldn't behave differently, except that they have `value` by
default
* The main difference with how records work in other places is that the semantics
of a record is otherwise decided by the members of the primary constructor, while in this
proposal the members of the record primary constructor have no special contribution to the value
equality semantics
* There's an evolution risk where we want to provide more complex things, like deep
equality, but these features don't support enough complexity to add it. Instead, we end up just
adding more keywords or more attributes. Consider array fields. The default equality is reference
equality, but sequence equality isn't particularly rare. How would users customize that?
A new keyword? Attribute? Writing Equals manually?
* Turns out we're finding a lot of customization pivots. String comparison is another one.
If we want to support all these scenarios attributes could be better. If we could use
attributes to supply an EqualityComparer that would be almost completely customizable.
* If equality is this complicated, should we only support simple generated equality for
records? Can we leave more complicated scenarios to tooling, like source generators?
Record equality: use the "primary" members or use all fields?
* Using all the fields is consistent with how structs work
* Using the "primary" members mirrors how the generation of `With` or other things
generated by a record with a primary constructor
* There does seem to be a possibility that after you get to a certain size, positional
records are less useful. In that case we want a path to the nominal record. If we do want the
nominal path, it's generally desirable that we want as little "record" syntax as possible.
If we choose the struct "use all the fields" approach, then we could use exactly the
same mechanism for both the "nominal" and the "positional" records.
* The nominal record syntax that has been floated is
```C#
record Point { int X; int Y; }
```
which generates
```C#
record Point
{
public int X { get; init; }
public int Y { get; init; }
}
```
Aside from the shorthand for properties, this generates Equals, GetHashCode and some form
of "With", which doesn't seem much different from proposals for a separable value equality. Is
there really much point in separating these proposals?
* One completely opposite possibility: bypass the question by prohibiting private members in the
positional record entirely
**Conclusion**
No hard decisions yet. Leaning slightly towards using "all declared fields" as the metric for
value equality. There's some support for the "no private fields approach."

View file

@ -0,0 +1,41 @@
# C# Language Design for Feb. 24, 2020
## Agenda
1. Nominal records proposal
## Discussion
### Nominal records
https://github.com/dotnet/csharplang/issues/3226
We've been trying to leave space open for something we're calling "nominal records" where the
conceit is that we establish some new system for constructing types based on names, instead of
the order of parameters in a constructor.
Here we have a refreshed nominal records proposal to examine and consider.
The proposal says:
> The main thing you lose out on with nominal construction is a centralized place - the
constructor body - for validation. Property setters can have member-wise validation, but
cross-member holistic validation is not possible. However, for a feature such as records that is
for data not behaviors, that seems to be a particularly small sacrifice.
We don't necessarily agree that this is a small restriction, and there may be some way to add
support for it.
When it comes to the `With` we do need to decide what members are copied over in non-destructive
mutation. One strategy is to use the "surface area" of the object, which is defined as the
constructor parameters, along with the public fields and properties that have some sort of
"setter".
Alternatively, we could copy over the state of the object. This would be equivalent to the use
of the `MemberwiseClone` approach as we discussed in previous design meetings.
**Conclusion**
There are many details to work out, but there's consensus that we want to investigate adding
nominal records in the future.

View file

@ -4,9 +4,9 @@
## Schedule when convenient
- Is `e is dynamic` a "pure" null check? (Neal)
- https://github.com/dotnet/csharplang/issues/2608 module initializers (Neal)
- https://github.com/dotnet/csharplang/issues/2910 base(T) (Neal)
- https://github.com/dotnet/csharplang/issues/140 `field` keyword in properties (cyrusn)
## Recurring topics
@ -14,36 +14,53 @@
- *Triage milestones*
- *Design review*
## April 15, 2020
- Is `e is dynamic` a "pure" null check? (Neal)
- Reconsider: Target-typing ?: when the natural type isn't convertible to the target type. (Neal)
- https://github.com/dotnet/csharplang/issues/2926 Target-typed simple identifier (Neal)
- Reconsider: Inferred type of an or pattern is the "common type" of the two inferred types (Neal)
## April 13, 2020
## April 8, 2020
## April 6, 2020
## April 1, 2020
## March 30, 2020
## March 25, 2020
- https://github.com/dotnet/csharplang/issues/3259 Open issues with native integers (Chuck)
- Records design (Mads, Andy)
## March 23, 2020
- Feedback from MVP Summit on records etc. (Mads, Andy)
## March 18, 2020
- https://github.com/jaredpar/csharplang/blob/record/proposals/recordsv3.md clone-style records (Jared)
## March 11, 2020
- Records design (Mads, Andy)
## March 9, 2020
- Digest feedback from design review
- Records design (Mads, Andy)
## Feb 26, 2020
- Design review
## Feb 24
## Feb 19
## Feb 12
## Feb 10
## Feb 5
## Feb 3
## Jan 29, 2020
- Records: drilling in to individual features (Mads)
## Jan 22, 2020
- https://github.com/dotnet/csharplang/issues/3117 Top-level statements and functions (Mads)
- https://github.com/dotnet/csharplang/issues/3086 Expressions Blocks (Chuck)
## Jan 15, 2020
- Records: Where it fits in our *programming with data* theme (Neal)
- Records: The individual subfeatures of records (Mads)
## Jan 13, 2020
- Records: Paging back in the previous proposal (Andy)
@ -52,6 +69,65 @@
Overview of meetings and agendas for 2020
## Feb 24
[C# Language Design Notes for Feb. 24, 2020](LDM-2020-02-24.md)
Taking another look at "nominal" records
## Feb 19
[C# Language Design Notes for Feb. 19, 2020](LDM-2020-02-19.md)
State-based value equality
## Feb 12
[C# Language Design Notes for Feb. 12, 2020](LDM-2020-02-12.md)
Records
## Feb 10
[C# Language Design Notes for Feb. 10, 2020](LDM-2020-02-10.md)
Records
## Feb 5
[C# Language Design Notes for Feb. 5, 2020](LDM-2020-02-05.md)
- Nullability of dependent calls (Chuck, Julien)
- https://github.com/dotnet/csharplang/issues/3137 Records as individual features (Mads)
## Feb 3
[C# Language Design Notes for Feb. 3, 2020](LDM-2020-02-03.md)
Value Equality
## Jan 29, 2020
[C# Language Design Notes for Jan. 29, 2020](LDM-2020-01-29.md)
Records: "With-ers"
## Jan 22, 2020
[C# Language Design Notes for Jan 22, 2020](LDM-2020-01-22.md)
1. Top-level statements and functions
2. Expression Blocks
## Jan 15, 2020
[C# Language Design Notes for Jan 15, 2020](LDM-2020-01-15.md)
Records
1. "programming with data"
1. Decomposing subfeatures of records
## Jan 8, 2020
[C# Language Design Notes for Jan 8, 2020](LDM-2020-01-08.md)

View file

@ -0,0 +1,171 @@
# Simple programs
* [x] Proposed
* [x] Prototype: Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
Allow a sequence of *statements* to occur right before the *namespace_member_declaration*s of a *compilation_unit* (i.e. source file).
The semantics are that if such a sequence of *statements* is present, the following type declaration, modulo the actual type name and the method name, would be emitted:
``` c#
static class Program
{
static async Task Main()
{
// statements
}
}
```
See also https://github.com/dotnet/csharplang/issues/3117.
## Motivation
[motivation]: #motivation
There's a certain amount of boilerplate surrounding even the simplest of programs,
because of the need for an explicit `Main` method. This seems to get in the way of
language learning and program clarity. The primary goal of the feature therefore is
to allow C# programs without unnecessary boilerplate around them, for the sake of
learners and the clarity of code.
## Detailed design
[design]: #detailed-design
### Syntax
The only additional syntax is allowing a sequence of *statement*s in a compilation unit,
just before the *namespace_member_declaration*s:
``` antlr
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
```
In all but one *compilation_unit* the *statement*s must all be local function declarations.
Example:
``` c#
// File 1 - any statements
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
// File 2 - only local functions
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
```
### Semantics
If any top-level statements are present in any compilation unit of the program, the meaning is as if
they were combined in the block body of a `Main` method of a `Program` class in the global namespace,
as follows:
``` c#
static class Program
{
static async Task Main()
{
// File 1 statements
// File 2 local functions
// ...
}
}
```
Note that the names "Program" and "Main" are used only for illustrations purposes, actual names used by
compiler are implementation dependent and neither the type, nor the method can be referenced by name from
source code.
The method is designated as the entry point of the program. Explicitly declared methods that by convention
could be considered as an entry point candidates are ignored. A warning is reported when that happens. It is
an error to specify `-main:<type>` compiler switch.
If any one compilation unit has statements other than local function declarations, statements from that
compilation unit occur first. This causes it to be legal for local functions in one file to reference
local variables in another. The order of statement contributions (which would all be local functions)
from other compilation units is undefined.
Async operations are allowed in top-level statements to the degree they are allowed in statements within
a regular async entry point method. However, they are not required, if `await` expressions and other async
operations are omitted, no warning is produced. Instead the signature of the generated entry point method
is equivalent to
``` c#
static void Main()
```
The example above would yield the following `$Main` method declaration:
``` c#
static class $Program
{
static void $Main()
{
// Statements from File 1
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
// Local functions from File 2
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
}
}
```
At the same time an example like this:
``` c#
// File 1
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
```
would yield:
``` c#
static class $Program
{
static async Task $Main()
{
// Statements from File 1
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
```
### Scope of top-level local variables and local functions
Even though top-level local variables and functions are "wrapped"
into the generated entry point method, they should still be in scope throughout the program.
For the purpose of simple-name evaluation, once the global namespace is reached:
- First, an attempt is made to evaluate the name within the generated entry point method and
only if this attempt fails
- The "regular" evaluation within the global namespace declaration is performed.
This could lead to name shadowing of namespaces and types declared within the global namespace
as well as to shadowing of imported names.
If the simple name evaluation occurs outside of the top-level statements and the evaluation
yields a top-level local variable or function, that should lead to an error.
In this way we protect our future ability to better address "Top-level functions" (scenario 2
in https://github.com/dotnet/csharplang/issues/3117), and are able to give useful diagnostics
to users who mistakenly believe them to be supported.

View file

@ -0,0 +1,5 @@
## Enum Base Type
In C# 6.0 we relaxed the syntax of an enum base type to be a type syntax, but require that it bind to one of a specified set of types. So `System.Int32` is permitted. But the spec has not been updated; it still requires one of a set of keywords for the base.
This is a placeholder for a specification of that change.

View file

@ -0,0 +1,3 @@
## Expression Bodied Everything
In C# 7.0, we added support for expression-bodied constructors, destructors, and accessors. This is a placeholder for the specification.

View file

@ -0,0 +1,3 @@
## Ref Locals and Returns
In C# 7.0 we added support for *ref locals and ref returns*. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Tuples
In C# 7.0 we added support for *tuples*. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Value Task
In C# 7.0 we added support for *value task* and custom task types and task builders. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Readonly structs
In C# 7.2, we added a feature permitting a struct declaration to have the `readonly` modifier. This is a placeholder for that feature's specification.

View file

@ -0,0 +1,3 @@
## Ref Extension Methods
In C# 7.2, we added support for *ref extension methods*. This is a placeholder for the feature's specification.

View file

@ -0,0 +1,3 @@
## Ref Structs and Span
In C# 7.2 we added support for *ref struct* types. This is a placeholder for the specification.

View file

@ -0,0 +1,3 @@
## Enum and Delegate type parameter constraint
In C# 7.3, we added support for type parameter constraint keywords `enum` and `delegate`. This is a placeholder for their specification.

View file

@ -0,0 +1,3 @@
## Ref loops
In C# 7.3, we added support for *ref for loops* and *ref foreach loops*. This is a placeholder for their specifications.

View file

@ -41,9 +41,9 @@ If an element-wise comparison returns some other non-bool type in a tuple equali
- if the non-bool type converts to `bool`, we apply that conversion,
- if there is no such conversion, but the type has an operator `false`, we'll use that and negate the result.
In a tuple inequality, the same rules apply except that we'll use the operator `true` (without negation) instead of the operator `true`.
In a tuple inequality, the same rules apply except that we'll use the operator `true` (without negation) instead of the operator `false`.
Those rules are similar the rules involved for using a non-bool type in an `if` statement and some other existing contexts.
Those rules are similar to the rules involved for using a non-bool type in an `if` statement and some other existing contexts.
## Evaluation order and special cases
The left-hand-side value is evaluated first, then the right-hand-side value, then the element-wise comparisons from left to right (including conversions, and with early exit based on existing rules for conditional AND/OR operators).

View file

@ -0,0 +1,3 @@
## Alternative interpolated verbatim strings
In C# 8.0 we added a feature that permits an interpolated verbatim string to be introduced with the characters `@$"` or the characters `$@"`. This is a placeholder for its specification.

View file

@ -156,7 +156,7 @@ An earlier version of this document recommended (1), but we since switched to (4
The two main problems with (1):
- producers of cancellable enumerables have to implement some boilerplate, and can only leverage the compiler's support for async-iterators to implement a `IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken)` method.
- it is likely that many producers would be tempted to just add a `CancellationToken` parameter to their async-enumerable signature instead, which will prevent consumers from passing the cancellation token they want when their are given an `IAsyncEnumerable` type.
- it is likely that many producers would be tempted to just add a `CancellationToken` parameter to their async-enumerable signature instead, which will prevent consumers from passing the cancellation token they want when they are given an `IAsyncEnumerable` type.
There are two main consumption scenarios:
1. `await foreach (var i in GetData(token)) ...` where the consumer calls the async-iterator method,

View file

@ -0,0 +1,3 @@
## Async using declaration
In C# 8.0 we added support for an *async using* statements. There are two forms. One has a block body that is the scope of the using declaratation. The other declares a local and is implicitly scoped to the end of the block. This is a placeholder for their specification.

View file

@ -0,0 +1,3 @@
## Override with constraints
In C# 8.0, we added a feature to permit the specification of certain type parameter constraints in an `override` method declaration. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Unmanaged constructed types
In C# 8.0, we extended the concept of an *unmanaged* type to include constructed (generic) types. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Notnull constraint
In C# 8.0, we added a language feature that permits the specification of a new type parameter constraint `notnull`. This is a placeholder for its specification.

View file

@ -0,0 +1,3 @@
## Obsolete on property accessor
In C# 8.0, we added support for declaring a property accessor `[Obsolete]`. This is a placeholder for the specification.

View file

@ -86,7 +86,7 @@ public static class MyClass
}
```
Readonly can be applied to property accessors to indicate that `this` will not be mutated in the accessor.
Readonly can be applied to property accessors to indicate that `this` will not be mutated in the accessor. The following examples have readonly setters because those accessors modify the state of member field, but do not modify the value of that member field.
```csharp
public int Prop1

View file

@ -0,0 +1,3 @@
## Name shadowing in nested functions
In C# 8.0, we added a feature that permits parameters and locals in lambdas and local functions to use names that hide/shadow the names of locals or parameters from the enclosing scope. This is a placholder for its specification.

View file

@ -0,0 +1,3 @@
## Unconstrained type parameter in null coalescing operator
In C# 8.0 we introduced a feature that permits a null coalescing operator to have a left operand that is not known to be either a reference or value type (i.e. an unconstrained type parameter). This is a placeholder for its specification.

View file

@ -13,8 +13,7 @@ potential implementation of the feature):
https://github.com/dotnet/csharplang/issues/191
This is an alternate design proposal to [compiler intrinsics]
(https://github.com/dotnet/csharplang/blob/master/proposals/intrinsics.md)
This is an alternate design proposal to [compiler intrinsics](https://github.com/dotnet/csharplang/blob/master/proposals/intrinsics.md)
## Detailed Design
@ -130,6 +129,18 @@ delegate* managed<string, int>;
delegate*<delegate* managed<string, int>, delegate*<string, int>>;
```
### Function pointer conversions
In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:
- [_Existing conversions_](https://github.com/dotnet/csharplang/blob/master/spec/unsafe-code.md#pointer-conversions)
- From _funcptr\_type_ `F0` to another _funcptr\_type_ `F1`, provided all of the following are true:
- `F0` and `F1` have the same number of parameters, and each parameter `D0n` in `F0` has the same `ref`, `out`, or `in` modifiers as the corresponding parameter `D1n` in `F1`.
- For each value parameter (a parameter with no `ref`, `out`, or `in` modifier), an identity conversion, implicit reference conversion, or implicit pointer conversion exists from the parameter type in `F0` to the corresponding parameter type in `F1`.
- For each `ref`, `out`, or `in` parameter, the parameter type in `F0` is the same as the corresponding parameter type in `F1`.
- If the return type is by value (no `ref` or `ref readonly`), an identity, implicit reference, or implicit pointer conversion exists from the return type of `F1` to the return type of `F0`.
- If the return type is by reference (`ref` or `ref readonly`), the return type and `ref` modifiers of `F1` are the same as the return type and `ref` modifiers of `F0`.
- The calling convention of `F0` is the same as the calling convention of `F1`.
### Allow address-of to target methods
Method groups will now be allowed as arguments to an address-of expression. The type of such an
@ -152,11 +163,27 @@ unsafe class Util {
}
```
The conversion of an address-of method group to `delegate*` has roughly the same process as method group to `delegate`
conversion. There are two additional restrictions to the existing process:
In an unsafe context, a method `M` is compatible with a function pointer type `F` if all of the following are true:
- `M` and `F` have the same number of parameters, and each parameter in `D` has the same `ref`, `out`, or `in` modifiers as the corresponding parameter in `F`.
- For each value parameter (a parameter with no `ref`, `out`, or `in` modifier), an identity conversion, implicit reference conversion, or implicit pointer conversion exists from the parameter type in `M` to the corresponding parameter type in `F`.
- For each `ref`, `out`, or `in` parameter, the parameter type in `M` is the same as the corresponding parameter type in `F`.
- If the return type is by value (no `ref` or `ref readonly`), an identity, implicit reference, or implicit pointer conversion exists from the return type of `F` to the return type of `M`.
- If the return type is by reference (`ref` or `ref readonly`), the return type and `ref` modifiers of `F` are the same as the return type and `ref` modifiers of `M`.
- The calling convention of `M` is the same as the calling convention of `F`.
- `M` is a static method.
- Only members of the method group that are marked as `static` will be considered.
- Only a `delegate*` with a managed calling convention can be the target of such a conversion.
In an unsafe context, an implicit conversion exists from an address-of expression whose target is a method group `E` to a compatible function pointer type `F` if `E` contains at least one method that is applicable in its normal form to an argument list constructed by use of the parameter types and modifiers of `F`, as described in the following.
- A single method `M` is selected corresponding to a method invocation of the form `E(A)` with the following modifications:
- The arguments list `A` is a list of expressions, each classified as a variable and with the type and modifier (`ref`, `out`, or `in`) of the corresponding _formal\_parameter\_list_ of `D`.
- The candidate methods are only those methods that are applicable in their normal form, not those applicable in their expanded form.
- The candidate methods are only those methods that are static.
- If the algorithm of Method invocations produces an error, then a compile-time error occurs. Otherwise, the algorithm produces a single best method `M` having the same number of parameters as `F` and the conversion is considered to exist.
- The selected method `M` must be compatible (as defined above) with the function pointer type `F`. Otherwise, a compile-time error occurs.
- The result of the conversion is a function pointer of type `F`.
An implicit conversion exists from an address-of expression whose target is a method group `E` to `void*` if there is only one static method `M` in `E`.
If there is one static method, then the single best method from `E` is `M`.
Otherwise, a compile-time error occurs.
This means developers can depend on overload resolution rules to work in conjunction with the
address-of operator:
@ -293,7 +320,7 @@ hide the fact that this is a pointer value and it kept peeking through even with
the conversion to `object` can't be allowed, it can't be a member of a `class`, etc ... The C# design is to require
`unsafe` for all pointer uses and hence this design follows that.
Developers will still be capable of preventing a _safe_ wrapper on top of `delegate*` values the same way that they do
Developers will still be capable of presenting a _safe_ wrapper on top of `delegate*` values the same way that they do
for normal pointer types today. Consider:
``` csharp

View file

@ -0,0 +1,37 @@
# Attributes on local functions
## Attributes
Local function declarations are now permitted to have [attributes](../spec/attributes.md). Parameters and type parameters on local functions are also allowed to have attributes.
Attributes with a specified meaning when applied to a method, its parameters, or its type parameters will have the same meaning when applied to a local function, its parameters, or its type parameters, respectively.
A local function can be made conditional in the same sense as a [conditional method](../spec/attributes.md#the-conditional-attribute) by decorating it with a `[ConditionalAttribute]`. A conditional local function must also be `static`. All restrictions on conditional methods also apply to conditional local functions, including that the return type must be `void`.
## Extern
The `extern` modifier is now permitted on local functions. This makes the local function external in the same sense as an [external method](../spec/classes.md#external-methods).
Similarly to an external method, the *local-function-body* of an external local function must be a semicolon. A semicolon *local-function-body* is only permitted on an external local function.
An external local function must also be `static`.
## Syntax
The [local functions grammar](csharp-7.0/local-functions.md#syntax-grammar) is modified as follows:
```
local-function-header
: attributes? local-function-modifiers? return-type identifier type-parameter-list?
( formal-parameter-list? ) type-parameter-constraints-clauses
;
local-function-modifiers
: (async | unsafe | static | extern)*
;
local-function-body
: block
| arrow-expression-body
| ';'
;
```

View file

@ -61,3 +61,18 @@ For a record struct or a record class:
* A public get-only auto-property is created. Its value is initialized during construction with the value of the corresponding primary constructor parameter. Each "matching" inherited abstract property's get accessor is overridden.
### Equality members
Record types produce synthesized implementations for the following methods:
* `object.GetHashCode()` override, unless it is sealed or user provided
* `object.Equals(object)` override, unless it is sealed or user provided
* `T Equals(T)` method, where `T` is the current type
`T Equals(T)` is specified to perform value equality, comparing the property with same name as
each primary constructor parameter to the corresponding property of the other type.
`object.Equals` performs the equivalent of
```C#
override Equals(object o) => Equals(o as T);
```

View file

@ -20,14 +20,17 @@ Dictionary<string, List<int>> field = new() {
{ "item1", new() { 1, 2, 3 } }
};
```
Allow omitting the type when it can be inferred from usage.
```cs
XmlReader.Create(reader, new() { IgnoreWhitespace = true });
```
Instantiate an object without spelling out the type.
```cs
private readonly static object s_syncObj = new();
```
## Detailed design
[design]: #detailed-design
@ -38,14 +41,15 @@ object_creation_expression
| 'new' type object_or_collection_initializer
;
```
A target-typed `new` is convertible to any type. As a result, it does not contribute to overload resolution. This is mainly to avoid unpredictable breaking changes.
The argument list and the initializer expressions will be bound after the type is determined.
The type of the expression would be inferred from the target-type which would be required to be one of the following:
- **Any struct type**
- **Any reference type**
- **Any struct type** (including tuple types)
- **Any reference type** (including delegate types)
- **Any type parameter** with a constructor or a `struct` constraint
with the following exceptions:
@ -53,10 +57,12 @@ with the following exceptions:
- **Enum types:** not all enum types contain the constant zero, so it should be desirable to use the explicit enum member.
- **Interface types:** this is a niche feature and it should be preferable to explicitly mention the type.
- **Array types:** arrays need a special syntax to provide the length.
- **Struct default constructor**: this rules out all primitive types and most value types. If you wanted to use the default value of such types you could write `default` instead.
- **dynamic:** we don't allow `new dynamic()`, so we don't allow `new()` with `dynamic` as a target type.
All the other types that are not permitted in the *object_creation_expression* are excluded as well, for instance, pointer types.
When the target type is a nullable value type, the target-typed `new` will be converted to the underlying type instead of the nullable type.
> **Open Issue:** should we allow delegates and tuples as the target-type?
The above rules include delegates (a reference type) and tuples (a struct type). Although both types are constructible, if the type is inferable, an anonymous function or a tuple literal can already be used.
@ -67,23 +73,20 @@ Action a = new(() => {}); // "new" is redundant
(int a, int b) t = new(); // ruled out by "use of struct default constructor"
Action a = new(); // no constructor found
var x = new() == (1, 2); // ruled out by "use of struct default constructor"
var x = new(1, 2) == (1, 2) // "new" is redundant
```
### Miscellaneous
`throw new()` is disallowed.
> **Open Issue:** should we allow `throw new()` with `Exception` as the target-type?
Target-typed `new` is not allowed with binary operators.
We have `throw null` today, but not `throw default` (though it would have the same effect). On the other hand, `throw new()` could be actually useful as a shorthand for `throw new Exception(...)`. Note that it is already allowed by the current specification. `Exception` is a reference type, and the specification for the throw statement says that the expression is converted to `Exception`.
It is disallowed when there is no type to target: unary operators, collection of a `foreach`, in a `using`, in a deconstruction, in an `await` expression, as an anonymous type property (`new { Prop = new() }`), in a `lock` statement, in a `sizeof`, in a `fixed` statement, in a member access (`new().field`), in a dynamically dispatched operation (`someDynamic.Method(new())`), in a LINQ query, as the operand of the `is` operator, as the left operand of the `??` operator, ...
> **Open Issue:** should we allow usages of a target-typed `new` with user-defined comparison and arithmetic operators?
For comparison, `default` only supports equality (user-defined and built-in) operators. Would it make sense to support other operators for `new()` as well?
It is also disallowed as a `ref`.
## Drawbacks
[drawbacks]: #drawbacks
None.
There were some concerns with target-typed `new` creating new categories of breaking changes, but we already have that with `null` and `default`, and that has not been a significant problem.
## Alternatives
[alternatives]: #alternatives
@ -96,6 +99,11 @@ Most of complaints about types being too long to duplicate in field initializati
- Should we forbid usages in expression trees? (no)
- How the feature interacts with `dynamic` arguments? (no special treatment)
- How IntelliSense should work with `new()`? (only when there is a single target-type)
## Design meetings
- [LDM-2017-10-18](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-10-18.md#100)
- [LDM-2018-05-21](https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-05-21.md)
- [LDM-2018-06-25](https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-06-25.md)
- [LDM-2018-08-22](https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-08-22.md#target-typed-new)
- [LDM-2018-10-17](https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-10-17.md)